2  * Leaflet 1.3.1, a JS library for interactive maps. http://leafletjs.com
 
   3  * (c) 2010-2017 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.3.1";
 
  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 Leafet'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.
 
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 DomEvent
 
2211  * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
 
2214 // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
 
2216 // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
 
2217 // Adds a listener function (`fn`) to a particular DOM event type of the
 
2218 // element `el`. You can optionally specify the context of the listener
 
2219 // (object the `this` keyword will point to). You can also pass several
 
2220 // space-separated types (e.g. `'click dblclick'`).
 
2223 // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
 
2224 // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
2225 function on(obj, types, fn, context) {
 
2227         if (typeof types === 'object') {
 
2228                 for (var type in types) {
 
2229                         addOne(obj, type, types[type], fn);
 
2232                 types = splitWords(types);
 
2234                 for (var i = 0, len = types.length; i < len; i++) {
 
2235                         addOne(obj, types[i], fn, context);
 
2242 var eventsKey = '_leaflet_events';
 
2244 // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
 
2245 // Removes a previously added listener function.
 
2246 // Note that if you passed a custom context to on, you must pass the same
 
2247 // context to `off` in order to remove the listener.
 
2250 // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
 
2251 // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
2252 function off(obj, types, fn, context) {
 
2254         if (typeof types === 'object') {
 
2255                 for (var type in types) {
 
2256                         removeOne(obj, type, types[type], fn);
 
2259                 types = splitWords(types);
 
2261                 for (var i = 0, len = types.length; i < len; i++) {
 
2262                         removeOne(obj, types[i], fn, context);
 
2265                 for (var j in obj[eventsKey]) {
 
2266                         removeOne(obj, j, obj[eventsKey][j]);
 
2268                 delete obj[eventsKey];
 
2274 function addOne(obj, type, fn, context) {
 
2275         var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
 
2277         if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
 
2279         var handler = function (e) {
 
2280                 return fn.call(context || obj, e || window.event);
 
2283         var originalHandler = handler;
 
2285         if (pointer && type.indexOf('touch') === 0) {
 
2286                 // Needs DomEvent.Pointer.js
 
2287                 addPointerListener(obj, type, handler, id);
 
2289         } else if (touch && (type === 'dblclick') && addDoubleTapListener &&
 
2290                    !(pointer && chrome)) {
 
2291                 // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
 
2293                 addDoubleTapListener(obj, handler, id);
 
2295         } else if ('addEventListener' in obj) {
 
2297                 if (type === 'mousewheel') {
 
2298                         obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
 
2300                 } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
 
2301                         handler = function (e) {
 
2302                                 e = e || window.event;
 
2303                                 if (isExternalTarget(obj, e)) {
 
2307                         obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
 
2310                         if (type === 'click' && android) {
 
2311                                 handler = function (e) {
 
2312                                         filterClick(e, originalHandler);
 
2315                         obj.addEventListener(type, handler, false);
 
2318         } else if ('attachEvent' in obj) {
 
2319                 obj.attachEvent('on' + type, handler);
 
2322         obj[eventsKey] = obj[eventsKey] || {};
 
2323         obj[eventsKey][id] = handler;
 
2326 function removeOne(obj, type, fn, context) {
 
2328         var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''),
 
2329             handler = obj[eventsKey] && obj[eventsKey][id];
 
2331         if (!handler) { return this; }
 
2333         if (pointer && type.indexOf('touch') === 0) {
 
2334                 removePointerListener(obj, type, id);
 
2336         } else if (touch && (type === 'dblclick') && removeDoubleTapListener &&
 
2337                    !(pointer && chrome)) {
 
2338                 removeDoubleTapListener(obj, id);
 
2340         } else if ('removeEventListener' in obj) {
 
2342                 if (type === 'mousewheel') {
 
2343                         obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
 
2346                         obj.removeEventListener(
 
2347                                 type === 'mouseenter' ? 'mouseover' :
 
2348                                 type === 'mouseleave' ? 'mouseout' : type, handler, false);
 
2351         } else if ('detachEvent' in obj) {
 
2352                 obj.detachEvent('on' + type, handler);
 
2355         obj[eventsKey][id] = null;
 
2358 // @function stopPropagation(ev: DOMEvent): this
 
2359 // Stop the given event from propagation to parent elements. Used inside the listener functions:
 
2361 // L.DomEvent.on(div, 'click', function (ev) {
 
2362 //      L.DomEvent.stopPropagation(ev);
 
2365 function stopPropagation(e) {
 
2367         if (e.stopPropagation) {
 
2368                 e.stopPropagation();
 
2369         } else if (e.originalEvent) {  // In case of Leaflet event.
 
2370                 e.originalEvent._stopped = true;
 
2372                 e.cancelBubble = true;
 
2379 // @function disableScrollPropagation(el: HTMLElement): this
 
2380 // Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
 
2381 function disableScrollPropagation(el) {
 
2382         addOne(el, 'mousewheel', stopPropagation);
 
2386 // @function disableClickPropagation(el: HTMLElement): this
 
2387 // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
 
2388 // `'mousedown'` and `'touchstart'` events (plus browser variants).
 
2389 function disableClickPropagation(el) {
 
2390         on(el, 'mousedown touchstart dblclick', stopPropagation);
 
2391         addOne(el, 'click', fakeStop);
 
2395 // @function preventDefault(ev: DOMEvent): this
 
2396 // Prevents the default action of the DOM Event `ev` from happening (such as
 
2397 // following a link in the href of the a element, or doing a POST request
 
2398 // with page reload when a `<form>` is submitted).
 
2399 // Use it inside listener functions.
 
2400 function preventDefault(e) {
 
2401         if (e.preventDefault) {
 
2404                 e.returnValue = false;
 
2409 // @function stop(ev: DOMEvent): this
 
2410 // Does `stopPropagation` and `preventDefault` at the same time.
 
2417 // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
 
2418 // Gets normalized mouse position from a DOM event relative to the
 
2419 // `container` or to the whole page if not specified.
 
2420 function getMousePosition(e, container) {
 
2422                 return new Point(e.clientX, e.clientY);
 
2425         var rect = container.getBoundingClientRect();
 
2427         var scaleX = rect.width / container.offsetWidth || 1;
 
2428         var scaleY = rect.height / container.offsetHeight || 1;
 
2430                 e.clientX / scaleX - rect.left - container.clientLeft,
 
2431                 e.clientY / scaleY - rect.top - container.clientTop);
 
2434 // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
 
2435 // and Firefox scrolls device pixels, not CSS pixels
 
2437         (win && chrome) ? 2 * window.devicePixelRatio :
 
2438         gecko ? window.devicePixelRatio : 1;
 
2440 // @function getWheelDelta(ev: DOMEvent): Number
 
2441 // Gets normalized wheel delta from a mousewheel DOM event, in vertical
 
2442 // pixels scrolled (negative if scrolling down).
 
2443 // Events from pointing devices without precise scrolling are mapped to
 
2444 // a best guess of 60 pixels.
 
2445 function getWheelDelta(e) {
 
2446         return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
 
2447                (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
 
2448                (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
 
2449                (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
 
2450                (e.deltaX || e.deltaZ) ? 0 :     // Skip horizontal/depth wheel events
 
2451                e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
 
2452                (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
 
2453                e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
 
2457 var skipEvents = {};
 
2459 function fakeStop(e) {
 
2460         // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e)
 
2461         skipEvents[e.type] = true;
 
2464 function skipped(e) {
 
2465         var events = skipEvents[e.type];
 
2466         // reset when checking, as it's only used in map container and propagates outside of the map
 
2467         skipEvents[e.type] = false;
 
2471 // check if element really left/entered the event target (for mouseenter/mouseleave)
 
2472 function isExternalTarget(el, e) {
 
2474         var related = e.relatedTarget;
 
2476         if (!related) { return true; }
 
2479                 while (related && (related !== el)) {
 
2480                         related = related.parentNode;
 
2485         return (related !== el);
 
2490 // this is a horrible workaround for a bug in Android where a single touch triggers two click events
 
2491 function filterClick(e, handler) {
 
2492         var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
 
2493             elapsed = lastClick && (timeStamp - lastClick);
 
2495         // are they closer together than 500ms yet more than 100ms?
 
2496         // Android typically triggers them ~300ms apart while multiple listeners
 
2497         // on the same event should be triggered far faster;
 
2498         // or check if click is simulated on the element, and if it is, reject any non-simulated events
 
2500         if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
 
2504         lastClick = timeStamp;
 
2512 var DomEvent = (Object.freeze || Object)({
 
2515         stopPropagation: stopPropagation,
 
2516         disableScrollPropagation: disableScrollPropagation,
 
2517         disableClickPropagation: disableClickPropagation,
 
2518         preventDefault: preventDefault,
 
2520         getMousePosition: getMousePosition,
 
2521         getWheelDelta: getWheelDelta,
 
2524         isExternalTarget: isExternalTarget,
 
2530  * @namespace DomUtil
 
2532  * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
 
2533  * tree, used by Leaflet internally.
 
2535  * Most functions expecting or returning a `HTMLElement` also work for
 
2536  * SVG elements. The only difference is that classes refer to CSS classes
 
2537  * in HTML and SVG classes in SVG.
 
2541 // @property TRANSFORM: String
 
2542 // Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
 
2543 var TRANSFORM = testProp(
 
2544         ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
 
2546 // webkitTransition comes first because some browser versions that drop vendor prefix don't do
 
2547 // the same for the transitionend event, in particular the Android 4.1 stock browser
 
2549 // @property TRANSITION: String
 
2550 // Vendor-prefixed transition style name.
 
2551 var TRANSITION = testProp(
 
2552         ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
 
2554 // @property TRANSITION_END: String
 
2555 // Vendor-prefixed transitionend event name.
 
2556 var TRANSITION_END =
 
2557         TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
 
2560 // @function get(id: String|HTMLElement): HTMLElement
 
2561 // Returns an element given its DOM id, or returns the element itself
 
2562 // if it was passed directly.
 
2564         return typeof id === 'string' ? document.getElementById(id) : id;
 
2567 // @function getStyle(el: HTMLElement, styleAttrib: String): String
 
2568 // Returns the value for a certain style attribute on an element,
 
2569 // including computed values or values set through CSS.
 
2570 function getStyle(el, style) {
 
2571         var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
 
2573         if ((!value || value === 'auto') && document.defaultView) {
 
2574                 var css = document.defaultView.getComputedStyle(el, null);
 
2575                 value = css ? css[style] : null;
 
2577         return value === 'auto' ? null : value;
 
2580 // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
 
2581 // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
 
2582 function create$1(tagName, className, container) {
 
2583         var el = document.createElement(tagName);
 
2584         el.className = className || '';
 
2587                 container.appendChild(el);
 
2592 // @function remove(el: HTMLElement)
 
2593 // Removes `el` from its parent element
 
2594 function remove(el) {
 
2595         var parent = el.parentNode;
 
2597                 parent.removeChild(el);
 
2601 // @function empty(el: HTMLElement)
 
2602 // Removes all of `el`'s children elements from `el`
 
2603 function empty(el) {
 
2604         while (el.firstChild) {
 
2605                 el.removeChild(el.firstChild);
 
2609 // @function toFront(el: HTMLElement)
 
2610 // Makes `el` the last child of its parent, so it renders in front of the other children.
 
2611 function toFront(el) {
 
2612         var parent = el.parentNode;
 
2613         if (parent.lastChild !== el) {
 
2614                 parent.appendChild(el);
 
2618 // @function toBack(el: HTMLElement)
 
2619 // Makes `el` the first child of its parent, so it renders behind the other children.
 
2620 function toBack(el) {
 
2621         var parent = el.parentNode;
 
2622         if (parent.firstChild !== el) {
 
2623                 parent.insertBefore(el, parent.firstChild);
 
2627 // @function hasClass(el: HTMLElement, name: String): Boolean
 
2628 // Returns `true` if the element's class attribute contains `name`.
 
2629 function hasClass(el, name) {
 
2630         if (el.classList !== undefined) {
 
2631                 return el.classList.contains(name);
 
2633         var className = getClass(el);
 
2634         return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
 
2637 // @function addClass(el: HTMLElement, name: String)
 
2638 // Adds `name` to the element's class attribute.
 
2639 function addClass(el, name) {
 
2640         if (el.classList !== undefined) {
 
2641                 var classes = splitWords(name);
 
2642                 for (var i = 0, len = classes.length; i < len; i++) {
 
2643                         el.classList.add(classes[i]);
 
2645         } else if (!hasClass(el, name)) {
 
2646                 var className = getClass(el);
 
2647                 setClass(el, (className ? className + ' ' : '') + name);
 
2651 // @function removeClass(el: HTMLElement, name: String)
 
2652 // Removes `name` from the element's class attribute.
 
2653 function removeClass(el, name) {
 
2654         if (el.classList !== undefined) {
 
2655                 el.classList.remove(name);
 
2657                 setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
 
2661 // @function setClass(el: HTMLElement, name: String)
 
2662 // Sets the element's class.
 
2663 function setClass(el, name) {
 
2664         if (el.className.baseVal === undefined) {
 
2665                 el.className = name;
 
2667                 // in case of SVG element
 
2668                 el.className.baseVal = name;
 
2672 // @function getClass(el: HTMLElement): String
 
2673 // Returns the element's class.
 
2674 function getClass(el) {
 
2675         return el.className.baseVal === undefined ? el.className : el.className.baseVal;
 
2678 // @function setOpacity(el: HTMLElement, opacity: Number)
 
2679 // Set the opacity of an element (including old IE support).
 
2680 // `opacity` must be a number from `0` to `1`.
 
2681 function setOpacity(el, value) {
 
2682         if ('opacity' in el.style) {
 
2683                 el.style.opacity = value;
 
2684         } else if ('filter' in el.style) {
 
2685                 _setOpacityIE(el, value);
 
2689 function _setOpacityIE(el, value) {
 
2691             filterName = 'DXImageTransform.Microsoft.Alpha';
 
2693         // filters collection throws an error if we try to retrieve a filter that doesn't exist
 
2695                 filter = el.filters.item(filterName);
 
2697                 // don't set opacity to 1 if we haven't already set an opacity,
 
2698                 // it isn't needed and breaks transparent pngs.
 
2699                 if (value === 1) { return; }
 
2702         value = Math.round(value * 100);
 
2705                 filter.Enabled = (value !== 100);
 
2706                 filter.Opacity = value;
 
2708                 el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
 
2712 // @function testProp(props: String[]): String|false
 
2713 // Goes through the array of style names and returns the first name
 
2714 // that is a valid style name for an element. If no such name is found,
 
2715 // it returns false. Useful for vendor-prefixed styles like `transform`.
 
2716 function testProp(props) {
 
2717         var style = document.documentElement.style;
 
2719         for (var i = 0; i < props.length; i++) {
 
2720                 if (props[i] in style) {
 
2727 // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
 
2728 // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
 
2729 // and optionally scaled by `scale`. Does not have an effect if the
 
2730 // browser doesn't support 3D CSS transforms.
 
2731 function setTransform(el, offset, scale) {
 
2732         var pos = offset || new Point(0, 0);
 
2734         el.style[TRANSFORM] =
 
2736                         'translate(' + pos.x + 'px,' + pos.y + 'px)' :
 
2737                         'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
 
2738                 (scale ? ' scale(' + scale + ')' : '');
 
2741 // @function setPosition(el: HTMLElement, position: Point)
 
2742 // Sets the position of `el` to coordinates specified by `position`,
 
2743 // using CSS translate or top/left positioning depending on the browser
 
2744 // (used by Leaflet internally to position its layers).
 
2745 function setPosition(el, point) {
 
2748         el._leaflet_pos = point;
 
2752                 setTransform(el, point);
 
2754                 el.style.left = point.x + 'px';
 
2755                 el.style.top = point.y + 'px';
 
2759 // @function getPosition(el: HTMLElement): Point
 
2760 // Returns the coordinates of an element previously positioned with setPosition.
 
2761 function getPosition(el) {
 
2762         // this method is only used for elements previously positioned using setPosition,
 
2763         // so it's safe to cache the position for performance
 
2765         return el._leaflet_pos || new Point(0, 0);
 
2768 // @function disableTextSelection()
 
2769 // Prevents the user from generating `selectstart` DOM events, usually generated
 
2770 // when the user drags the mouse through a page with text. Used internally
 
2771 // by Leaflet to override the behaviour of any click-and-drag interaction on
 
2772 // the map. Affects drag interactions on the whole document.
 
2774 // @function enableTextSelection()
 
2775 // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
 
2776 var disableTextSelection;
 
2777 var enableTextSelection;
 
2779 if ('onselectstart' in document) {
 
2780         disableTextSelection = function () {
 
2781                 on(window, 'selectstart', preventDefault);
 
2783         enableTextSelection = function () {
 
2784                 off(window, 'selectstart', preventDefault);
 
2787         var userSelectProperty = testProp(
 
2788                 ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
 
2790         disableTextSelection = function () {
 
2791                 if (userSelectProperty) {
 
2792                         var style = document.documentElement.style;
 
2793                         _userSelect = style[userSelectProperty];
 
2794                         style[userSelectProperty] = 'none';
 
2797         enableTextSelection = function () {
 
2798                 if (userSelectProperty) {
 
2799                         document.documentElement.style[userSelectProperty] = _userSelect;
 
2800                         _userSelect = undefined;
 
2805 // @function disableImageDrag()
 
2806 // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
 
2807 // for `dragstart` DOM events, usually generated when the user drags an image.
 
2808 function disableImageDrag() {
 
2809         on(window, 'dragstart', preventDefault);
 
2812 // @function enableImageDrag()
 
2813 // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
 
2814 function enableImageDrag() {
 
2815         off(window, 'dragstart', preventDefault);
 
2818 var _outlineElement;
 
2820 // @function preventOutline(el: HTMLElement)
 
2821 // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
 
2822 // of the element `el` invisible. Used internally by Leaflet to prevent
 
2823 // focusable elements from displaying an outline when the user performs a
 
2824 // drag interaction on them.
 
2825 function preventOutline(element) {
 
2826         while (element.tabIndex === -1) {
 
2827                 element = element.parentNode;
 
2829         if (!element.style) { return; }
 
2831         _outlineElement = element;
 
2832         _outlineStyle = element.style.outline;
 
2833         element.style.outline = 'none';
 
2834         on(window, 'keydown', restoreOutline);
 
2837 // @function restoreOutline()
 
2838 // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
 
2839 function restoreOutline() {
 
2840         if (!_outlineElement) { return; }
 
2841         _outlineElement.style.outline = _outlineStyle;
 
2842         _outlineElement = undefined;
 
2843         _outlineStyle = undefined;
 
2844         off(window, 'keydown', restoreOutline);
 
2848 var DomUtil = (Object.freeze || Object)({
 
2849         TRANSFORM: TRANSFORM,
 
2850         TRANSITION: TRANSITION,
 
2851         TRANSITION_END: TRANSITION_END,
 
2861         removeClass: removeClass,
 
2864         setOpacity: setOpacity,
 
2866         setTransform: setTransform,
 
2867         setPosition: setPosition,
 
2868         getPosition: getPosition,
 
2869         disableTextSelection: disableTextSelection,
 
2870         enableTextSelection: enableTextSelection,
 
2871         disableImageDrag: disableImageDrag,
 
2872         enableImageDrag: enableImageDrag,
 
2873         preventOutline: preventOutline,
 
2874         restoreOutline: restoreOutline
 
2878  * @class PosAnimation
 
2879  * @aka L.PosAnimation
 
2881  * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
 
2885  * var fx = new L.PosAnimation();
 
2886  * fx.run(el, [300, 500], 0.5);
 
2889  * @constructor L.PosAnimation()
 
2890  * Creates a `PosAnimation` object.
 
2894 var PosAnimation = Evented.extend({
 
2896         // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
 
2897         // Run an animation of a given element to a new position, optionally setting
 
2898         // duration in seconds (`0.25` by default) and easing linearity factor (3rd
 
2899         // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
 
2900         // `0.5` by default).
 
2901         run: function (el, newPos, duration, easeLinearity) {
 
2905                 this._inProgress = true;
 
2906                 this._duration = duration || 0.25;
 
2907                 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
 
2909                 this._startPos = getPosition(el);
 
2910                 this._offset = newPos.subtract(this._startPos);
 
2911                 this._startTime = +new Date();
 
2913                 // @event start: Event
 
2914                 // Fired when the animation starts
 
2921         // Stops the animation (if currently running).
 
2923                 if (!this._inProgress) { return; }
 
2929         _animate: function () {
 
2931                 this._animId = requestAnimFrame(this._animate, this);
 
2935         _step: function (round) {
 
2936                 var elapsed = (+new Date()) - this._startTime,
 
2937                     duration = this._duration * 1000;
 
2939                 if (elapsed < duration) {
 
2940                         this._runFrame(this._easeOut(elapsed / duration), round);
 
2947         _runFrame: function (progress, round) {
 
2948                 var pos = this._startPos.add(this._offset.multiplyBy(progress));
 
2952                 setPosition(this._el, pos);
 
2954                 // @event step: Event
 
2955                 // Fired continuously during the animation.
 
2959         _complete: function () {
 
2960                 cancelAnimFrame(this._animId);
 
2962                 this._inProgress = false;
 
2963                 // @event end: Event
 
2964                 // Fired when the animation ends.
 
2968         _easeOut: function (t) {
 
2969                 return 1 - Math.pow(1 - t, this._easeOutPower);
 
2978  * The central class of the API — it is used to create a map on a page and manipulate it.
 
2983  * // initialize the map on the "map" div with a given center and zoom
 
2984  * var map = L.map('map', {
 
2985  *      center: [51.505, -0.09],
 
2992 var Map = Evented.extend({
 
2995                 // @section Map State Options
 
2996                 // @option crs: CRS = L.CRS.EPSG3857
 
2997                 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
 
2998                 // sure what it means.
 
3001                 // @option center: LatLng = undefined
 
3002                 // Initial geographic center of the map
 
3005                 // @option zoom: Number = undefined
 
3006                 // Initial map zoom level
 
3009                 // @option minZoom: Number = *
 
3010                 // Minimum zoom level of the map.
 
3011                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
 
3012                 // the lowest of their `minZoom` options will be used instead.
 
3015                 // @option maxZoom: Number = *
 
3016                 // Maximum zoom level of the map.
 
3017                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
 
3018                 // the highest of their `maxZoom` options will be used instead.
 
3021                 // @option layers: Layer[] = []
 
3022                 // Array of layers that will be added to the map initially
 
3025                 // @option maxBounds: LatLngBounds = null
 
3026                 // When this option is set, the map restricts the view to the given
 
3027                 // geographical bounds, bouncing the user back if the user tries to pan
 
3028                 // outside the view. To set the restriction dynamically, use
 
3029                 // [`setMaxBounds`](#map-setmaxbounds) method.
 
3030                 maxBounds: undefined,
 
3032                 // @option renderer: Renderer = *
 
3033                 // The default method for drawing vector layers on the map. `L.SVG`
 
3034                 // or `L.Canvas` by default depending on browser support.
 
3035                 renderer: undefined,
 
3038                 // @section Animation Options
 
3039                 // @option zoomAnimation: Boolean = true
 
3040                 // Whether the map zoom animation is enabled. By default it's enabled
 
3041                 // in all browsers that support CSS3 Transitions except Android.
 
3042                 zoomAnimation: true,
 
3044                 // @option zoomAnimationThreshold: Number = 4
 
3045                 // Won't animate zoom if the zoom difference exceeds this value.
 
3046                 zoomAnimationThreshold: 4,
 
3048                 // @option fadeAnimation: Boolean = true
 
3049                 // Whether the tile fade animation is enabled. By default it's enabled
 
3050                 // in all browsers that support CSS3 Transitions except Android.
 
3051                 fadeAnimation: true,
 
3053                 // @option markerZoomAnimation: Boolean = true
 
3054                 // Whether markers animate their zoom with the zoom animation, if disabled
 
3055                 // they will disappear for the length of the animation. By default it's
 
3056                 // enabled in all browsers that support CSS3 Transitions except Android.
 
3057                 markerZoomAnimation: true,
 
3059                 // @option transform3DLimit: Number = 2^23
 
3060                 // Defines the maximum size of a CSS translation transform. The default
 
3061                 // value should not be changed unless a web browser positions layers in
 
3062                 // the wrong place after doing a large `panBy`.
 
3063                 transform3DLimit: 8388608, // Precision limit of a 32-bit float
 
3065                 // @section Interaction Options
 
3066                 // @option zoomSnap: Number = 1
 
3067                 // Forces the map's zoom level to always be a multiple of this, particularly
 
3068                 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
 
3069                 // By default, the zoom level snaps to the nearest integer; lower values
 
3070                 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
 
3071                 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
 
3074                 // @option zoomDelta: Number = 1
 
3075                 // Controls how much the map's zoom level will change after a
 
3076                 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
 
3077                 // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
 
3078                 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
 
3081                 // @option trackResize: Boolean = true
 
3082                 // Whether the map automatically handles browser window resize to update itself.
 
3086         initialize: function (id, options) { // (HTMLElement or String, Object)
 
3087                 options = setOptions(this, options);
 
3089                 this._initContainer(id);
 
3092                 // hack for https://github.com/Leaflet/Leaflet/issues/1980
 
3093                 this._onResize = bind(this._onResize, this);
 
3097                 if (options.maxBounds) {
 
3098                         this.setMaxBounds(options.maxBounds);
 
3101                 if (options.zoom !== undefined) {
 
3102                         this._zoom = this._limitZoom(options.zoom);
 
3105                 if (options.center && options.zoom !== undefined) {
 
3106                         this.setView(toLatLng(options.center), options.zoom, {reset: true});
 
3109                 this._handlers = [];
 
3111                 this._zoomBoundLayers = {};
 
3112                 this._sizeChanged = true;
 
3114                 this.callInitHooks();
 
3116                 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
 
3117                 this._zoomAnimated = TRANSITION && any3d && !mobileOpera &&
 
3118                                 this.options.zoomAnimation;
 
3120                 // zoom transitions run with the same duration for all layers, so if one of transitionend events
 
3121                 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
 
3122                 if (this._zoomAnimated) {
 
3123                         this._createAnimProxy();
 
3124                         on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
 
3127                 this._addLayers(this.options.layers);
 
3131         // @section Methods for modifying map state
 
3133         // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
 
3134         // Sets the view of the map (geographical center and zoom) with the given
 
3135         // animation options.
 
3136         setView: function (center, zoom, options) {
 
3138                 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
 
3139                 center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
 
3140                 options = options || {};
 
3144                 if (this._loaded && !options.reset && options !== true) {
 
3146                         if (options.animate !== undefined) {
 
3147                                 options.zoom = extend({animate: options.animate}, options.zoom);
 
3148                                 options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
 
3151                         // try animating pan or zoom
 
3152                         var moved = (this._zoom !== zoom) ?
 
3153                                 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
 
3154                                 this._tryAnimatedPan(center, options.pan);
 
3157                                 // prevent resize handler call, the view will refresh after animation anyway
 
3158                                 clearTimeout(this._sizeTimer);
 
3163                 // animation didn't start, just reset the map view
 
3164                 this._resetView(center, zoom);
 
3169         // @method setZoom(zoom: Number, options?: Zoom/pan options): this
 
3170         // Sets the zoom of the map.
 
3171         setZoom: function (zoom, options) {
 
3172                 if (!this._loaded) {
 
3176                 return this.setView(this.getCenter(), zoom, {zoom: options});
 
3179         // @method zoomIn(delta?: Number, options?: Zoom options): this
 
3180         // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
 
3181         zoomIn: function (delta, options) {
 
3182                 delta = delta || (any3d ? this.options.zoomDelta : 1);
 
3183                 return this.setZoom(this._zoom + delta, options);
 
3186         // @method zoomOut(delta?: Number, options?: Zoom options): this
 
3187         // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
 
3188         zoomOut: function (delta, options) {
 
3189                 delta = delta || (any3d ? this.options.zoomDelta : 1);
 
3190                 return this.setZoom(this._zoom - delta, options);
 
3193         // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
 
3194         // Zooms the map while keeping a specified geographical point on the map
 
3195         // stationary (e.g. used internally for scroll zoom and double-click zoom).
 
3197         // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
 
3198         // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
 
3199         setZoomAround: function (latlng, zoom, options) {
 
3200                 var scale = this.getZoomScale(zoom),
 
3201                     viewHalf = this.getSize().divideBy(2),
 
3202                     containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
 
3204                     centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
 
3205                     newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
 
3207                 return this.setView(newCenter, zoom, {zoom: options});
 
3210         _getBoundsCenterZoom: function (bounds, options) {
 
3212                 options = options || {};
 
3213                 bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
 
3215                 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
 
3216                     paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
 
3218                     zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
 
3220                 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
 
3222                 if (zoom === Infinity) {
 
3224                                 center: bounds.getCenter(),
 
3229                 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
 
3231                     swPoint = this.project(bounds.getSouthWest(), zoom),
 
3232                     nePoint = this.project(bounds.getNorthEast(), zoom),
 
3233                     center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
 
3241         // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
 
3242         // Sets a map view that contains the given geographical bounds with the
 
3243         // maximum zoom level possible.
 
3244         fitBounds: function (bounds, options) {
 
3246                 bounds = toLatLngBounds(bounds);
 
3248                 if (!bounds.isValid()) {
 
3249                         throw new Error('Bounds are not valid.');
 
3252                 var target = this._getBoundsCenterZoom(bounds, options);
 
3253                 return this.setView(target.center, target.zoom, options);
 
3256         // @method fitWorld(options?: fitBounds options): this
 
3257         // Sets a map view that mostly contains the whole world with the maximum
 
3258         // zoom level possible.
 
3259         fitWorld: function (options) {
 
3260                 return this.fitBounds([[-90, -180], [90, 180]], options);
 
3263         // @method panTo(latlng: LatLng, options?: Pan options): this
 
3264         // Pans the map to a given center.
 
3265         panTo: function (center, options) { // (LatLng)
 
3266                 return this.setView(center, this._zoom, {pan: options});
 
3269         // @method panBy(offset: Point, options?: Pan options): this
 
3270         // Pans the map by a given number of pixels (animated).
 
3271         panBy: function (offset, options) {
 
3272                 offset = toPoint(offset).round();
 
3273                 options = options || {};
 
3275                 if (!offset.x && !offset.y) {
 
3276                         return this.fire('moveend');
 
3278                 // If we pan too far, Chrome gets issues with tiles
 
3279                 // and makes them disappear or appear in the wrong place (slightly offset) #2602
 
3280                 if (options.animate !== true && !this.getSize().contains(offset)) {
 
3281                         this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
 
3285                 if (!this._panAnim) {
 
3286                         this._panAnim = new PosAnimation();
 
3289                                 'step': this._onPanTransitionStep,
 
3290                                 'end': this._onPanTransitionEnd
 
3294                 // don't fire movestart if animating inertia
 
3295                 if (!options.noMoveStart) {
 
3296                         this.fire('movestart');
 
3299                 // animate pan unless animate: false specified
 
3300                 if (options.animate !== false) {
 
3301                         addClass(this._mapPane, 'leaflet-pan-anim');
 
3303                         var newPos = this._getMapPanePos().subtract(offset).round();
 
3304                         this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
 
3306                         this._rawPanBy(offset);
 
3307                         this.fire('move').fire('moveend');
 
3313         // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
 
3314         // Sets the view of the map (geographical center and zoom) performing a smooth
 
3315         // pan-zoom animation.
 
3316         flyTo: function (targetCenter, targetZoom, options) {
 
3318                 options = options || {};
 
3319                 if (options.animate === false || !any3d) {
 
3320                         return this.setView(targetCenter, targetZoom, options);
 
3325                 var from = this.project(this.getCenter()),
 
3326                     to = this.project(targetCenter),
 
3327                     size = this.getSize(),
 
3328                     startZoom = this._zoom;
 
3330                 targetCenter = toLatLng(targetCenter);
 
3331                 targetZoom = targetZoom === undefined ? startZoom : targetZoom;
 
3333                 var w0 = Math.max(size.x, size.y),
 
3334                     w1 = w0 * this.getZoomScale(startZoom, targetZoom),
 
3335                     u1 = (to.distanceTo(from)) || 1,
 
3340                         var s1 = i ? -1 : 1,
 
3342                             t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
 
3343                             b1 = 2 * s2 * rho2 * u1,
 
3345                             sq = Math.sqrt(b * b + 1) - b;
 
3347                             // workaround for floating point precision bug when sq = 0, log = -Infinite,
 
3348                             // thus triggering an infinite loop in flyTo
 
3349                             var log = sq < 0.000000001 ? -18 : Math.log(sq);
 
3354                 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
 
3355                 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
 
3356                 function tanh(n) { return sinh(n) / cosh(n); }
 
3360                 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
 
3361                 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
 
3363                 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
 
3365                 var start = Date.now(),
 
3366                     S = (r(1) - r0) / rho,
 
3367                     duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
 
3370                         var t = (Date.now() - start) / duration,
 
3374                                 this._flyToFrame = requestAnimFrame(frame, this);
 
3377                                         this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
 
3378                                         this.getScaleZoom(w0 / w(s), startZoom),
 
3383                                         ._move(targetCenter, targetZoom)
 
3388                 this._moveStart(true, options.noMoveStart);
 
3394         // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
 
3395         // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
 
3396         // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
 
3397         flyToBounds: function (bounds, options) {
 
3398                 var target = this._getBoundsCenterZoom(bounds, options);
 
3399                 return this.flyTo(target.center, target.zoom, options);
 
3402         // @method setMaxBounds(bounds: Bounds): this
 
3403         // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
 
3404         setMaxBounds: function (bounds) {
 
3405                 bounds = toLatLngBounds(bounds);
 
3407                 if (!bounds.isValid()) {
 
3408                         this.options.maxBounds = null;
 
3409                         return this.off('moveend', this._panInsideMaxBounds);
 
3410                 } else if (this.options.maxBounds) {
 
3411                         this.off('moveend', this._panInsideMaxBounds);
 
3414                 this.options.maxBounds = bounds;
 
3417                         this._panInsideMaxBounds();
 
3420                 return this.on('moveend', this._panInsideMaxBounds);
 
3423         // @method setMinZoom(zoom: Number): this
 
3424         // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
 
3425         setMinZoom: function (zoom) {
 
3426                 var oldZoom = this.options.minZoom;
 
3427                 this.options.minZoom = zoom;
 
3429                 if (this._loaded && oldZoom !== zoom) {
 
3430                         this.fire('zoomlevelschange');
 
3432                         if (this.getZoom() < this.options.minZoom) {
 
3433                                 return this.setZoom(zoom);
 
3440         // @method setMaxZoom(zoom: Number): this
 
3441         // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
 
3442         setMaxZoom: function (zoom) {
 
3443                 var oldZoom = this.options.maxZoom;
 
3444                 this.options.maxZoom = zoom;
 
3446                 if (this._loaded && oldZoom !== zoom) {
 
3447                         this.fire('zoomlevelschange');
 
3449                         if (this.getZoom() > this.options.maxZoom) {
 
3450                                 return this.setZoom(zoom);
 
3457         // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
 
3458         // 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.
 
3459         panInsideBounds: function (bounds, options) {
 
3460                 this._enforcingBounds = true;
 
3461                 var center = this.getCenter(),
 
3462                     newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
 
3464                 if (!center.equals(newCenter)) {
 
3465                         this.panTo(newCenter, options);
 
3468                 this._enforcingBounds = false;
 
3472         // @method invalidateSize(options: Zoom/pan options): this
 
3473         // Checks if the map container size changed and updates the map if so —
 
3474         // call it after you've changed the map size dynamically, also animating
 
3475         // pan by default. If `options.pan` is `false`, panning will not occur.
 
3476         // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
 
3477         // that it doesn't happen often even if the method is called many
 
3481         // @method invalidateSize(animate: Boolean): this
 
3482         // Checks if the map container size changed and updates the map if so —
 
3483         // call it after you've changed the map size dynamically, also animating
 
3485         invalidateSize: function (options) {
 
3486                 if (!this._loaded) { return this; }
 
3491                 }, options === true ? {animate: true} : options);
 
3493                 var oldSize = this.getSize();
 
3494                 this._sizeChanged = true;
 
3495                 this._lastCenter = null;
 
3497                 var newSize = this.getSize(),
 
3498                     oldCenter = oldSize.divideBy(2).round(),
 
3499                     newCenter = newSize.divideBy(2).round(),
 
3500                     offset = oldCenter.subtract(newCenter);
 
3502                 if (!offset.x && !offset.y) { return this; }
 
3504                 if (options.animate && options.pan) {
 
3509                                 this._rawPanBy(offset);
 
3514                         if (options.debounceMoveend) {
 
3515                                 clearTimeout(this._sizeTimer);
 
3516                                 this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
 
3518                                 this.fire('moveend');
 
3522                 // @section Map state change events
 
3523                 // @event resize: ResizeEvent
 
3524                 // Fired when the map is resized.
 
3525                 return this.fire('resize', {
 
3531         // @section Methods for modifying map state
 
3532         // @method stop(): this
 
3533         // Stops the currently running `panTo` or `flyTo` animation, if any.
 
3535                 this.setZoom(this._limitZoom(this._zoom));
 
3536                 if (!this.options.zoomSnap) {
 
3537                         this.fire('viewreset');
 
3539                 return this._stop();
 
3542         // @section Geolocation methods
 
3543         // @method locate(options?: Locate options): this
 
3544         // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
 
3545         // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
 
3546         // and optionally sets the map view to the user's location with respect to
 
3547         // detection accuracy (or to the world view if geolocation failed).
 
3548         // Note that, if your page doesn't use HTTPS, this method will fail in
 
3549         // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
 
3550         // See `Locate options` for more details.
 
3551         locate: function (options) {
 
3553                 options = this._locateOptions = extend({
 
3557                         // maxZoom: <Number>
 
3559                         // enableHighAccuracy: false
 
3562                 if (!('geolocation' in navigator)) {
 
3563                         this._handleGeolocationError({
 
3565                                 message: 'Geolocation not supported.'
 
3570                 var onResponse = bind(this._handleGeolocationResponse, this),
 
3571                     onError = bind(this._handleGeolocationError, this);
 
3573                 if (options.watch) {
 
3574                         this._locationWatchId =
 
3575                                 navigator.geolocation.watchPosition(onResponse, onError, options);
 
3577                         navigator.geolocation.getCurrentPosition(onResponse, onError, options);
 
3582         // @method stopLocate(): this
 
3583         // Stops watching location previously initiated by `map.locate({watch: true})`
 
3584         // and aborts resetting the map view if map.locate was called with
 
3585         // `{setView: true}`.
 
3586         stopLocate: function () {
 
3587                 if (navigator.geolocation && navigator.geolocation.clearWatch) {
 
3588                         navigator.geolocation.clearWatch(this._locationWatchId);
 
3590                 if (this._locateOptions) {
 
3591                         this._locateOptions.setView = false;
 
3596         _handleGeolocationError: function (error) {
 
3598                     message = error.message ||
 
3599                             (c === 1 ? 'permission denied' :
 
3600                             (c === 2 ? 'position unavailable' : 'timeout'));
 
3602                 if (this._locateOptions.setView && !this._loaded) {
 
3606                 // @section Location events
 
3607                 // @event locationerror: ErrorEvent
 
3608                 // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
 
3609                 this.fire('locationerror', {
 
3611                         message: 'Geolocation error: ' + message + '.'
 
3615         _handleGeolocationResponse: function (pos) {
 
3616                 var lat = pos.coords.latitude,
 
3617                     lng = pos.coords.longitude,
 
3618                     latlng = new LatLng(lat, lng),
 
3619                     bounds = latlng.toBounds(pos.coords.accuracy),
 
3620                     options = this._locateOptions;
 
3622                 if (options.setView) {
 
3623                         var zoom = this.getBoundsZoom(bounds);
 
3624                         this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
 
3630                         timestamp: pos.timestamp
 
3633                 for (var i in pos.coords) {
 
3634                         if (typeof pos.coords[i] === 'number') {
 
3635                                 data[i] = pos.coords[i];
 
3639                 // @event locationfound: LocationEvent
 
3640                 // Fired when geolocation (using the [`locate`](#map-locate) method)
 
3641                 // went successfully.
 
3642                 this.fire('locationfound', data);
 
3645         // TODO Appropriate docs section?
 
3646         // @section Other Methods
 
3647         // @method addHandler(name: String, HandlerClass: Function): this
 
3648         // Adds a new `Handler` to the map, given its name and constructor function.
 
3649         addHandler: function (name, HandlerClass) {
 
3650                 if (!HandlerClass) { return this; }
 
3652                 var handler = this[name] = new HandlerClass(this);
 
3654                 this._handlers.push(handler);
 
3656                 if (this.options[name]) {
 
3663         // @method remove(): this
 
3664         // Destroys the map and clears all related event listeners.
 
3665         remove: function () {
 
3667                 this._initEvents(true);
 
3669                 if (this._containerId !== this._container._leaflet_id) {
 
3670                         throw new Error('Map container is being reused by another instance');
 
3674                         // throws error in IE6-8
 
3675                         delete this._container._leaflet_id;
 
3676                         delete this._containerId;
 
3679                         this._container._leaflet_id = undefined;
 
3681                         this._containerId = undefined;
 
3684                 if (this._locationWatchId !== undefined) {
 
3690                 remove(this._mapPane);
 
3692                 if (this._clearControlPos) {
 
3693                         this._clearControlPos();
 
3696                 this._clearHandlers();
 
3699                         // @section Map state change events
 
3700                         // @event unload: Event
 
3701                         // Fired when the map is destroyed with [remove](#map-remove) method.
 
3702                         this.fire('unload');
 
3706                 for (i in this._layers) {
 
3707                         this._layers[i].remove();
 
3709                 for (i in this._panes) {
 
3710                         remove(this._panes[i]);
 
3715                 delete this._mapPane;
 
3716                 delete this._renderer;
 
3721         // @section Other Methods
 
3722         // @method createPane(name: String, container?: HTMLElement): HTMLElement
 
3723         // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
 
3724         // then returns it. The pane is created as a child of `container`, or
 
3725         // as a child of the main map pane if not set.
 
3726         createPane: function (name, container) {
 
3727                 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
 
3728                     pane = create$1('div', className, container || this._mapPane);
 
3731                         this._panes[name] = pane;
 
3736         // @section Methods for Getting Map State
 
3738         // @method getCenter(): LatLng
 
3739         // Returns the geographical center of the map view
 
3740         getCenter: function () {
 
3741                 this._checkIfLoaded();
 
3743                 if (this._lastCenter && !this._moved()) {
 
3744                         return this._lastCenter;
 
3746                 return this.layerPointToLatLng(this._getCenterLayerPoint());
 
3749         // @method getZoom(): Number
 
3750         // Returns the current zoom level of the map view
 
3751         getZoom: function () {
 
3755         // @method getBounds(): LatLngBounds
 
3756         // Returns the geographical bounds visible in the current map view
 
3757         getBounds: function () {
 
3758                 var bounds = this.getPixelBounds(),
 
3759                     sw = this.unproject(bounds.getBottomLeft()),
 
3760                     ne = this.unproject(bounds.getTopRight());
 
3762                 return new LatLngBounds(sw, ne);
 
3765         // @method getMinZoom(): Number
 
3766         // 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.
 
3767         getMinZoom: function () {
 
3768                 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
 
3771         // @method getMaxZoom(): Number
 
3772         // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
 
3773         getMaxZoom: function () {
 
3774                 return this.options.maxZoom === undefined ?
 
3775                         (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
 
3776                         this.options.maxZoom;
 
3779         // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean): Number
 
3780         // Returns the maximum zoom level on which the given bounds fit to the map
 
3781         // view in its entirety. If `inside` (optional) is set to `true`, the method
 
3782         // instead returns the minimum zoom level on which the map view fits into
 
3783         // the given bounds in its entirety.
 
3784         getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
 
3785                 bounds = toLatLngBounds(bounds);
 
3786                 padding = toPoint(padding || [0, 0]);
 
3788                 var zoom = this.getZoom() || 0,
 
3789                     min = this.getMinZoom(),
 
3790                     max = this.getMaxZoom(),
 
3791                     nw = bounds.getNorthWest(),
 
3792                     se = bounds.getSouthEast(),
 
3793                     size = this.getSize().subtract(padding),
 
3794                     boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
 
3795                     snap = any3d ? this.options.zoomSnap : 1,
 
3796                     scalex = size.x / boundsSize.x,
 
3797                     scaley = size.y / boundsSize.y,
 
3798                     scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
 
3800                 zoom = this.getScaleZoom(scale, zoom);
 
3803                         zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
 
3804                         zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
 
3807                 return Math.max(min, Math.min(max, zoom));
 
3810         // @method getSize(): Point
 
3811         // Returns the current size of the map container (in pixels).
 
3812         getSize: function () {
 
3813                 if (!this._size || this._sizeChanged) {
 
3814                         this._size = new Point(
 
3815                                 this._container.clientWidth || 0,
 
3816                                 this._container.clientHeight || 0);
 
3818                         this._sizeChanged = false;
 
3820                 return this._size.clone();
 
3823         // @method getPixelBounds(): Bounds
 
3824         // Returns the bounds of the current map view in projected pixel
 
3825         // coordinates (sometimes useful in layer and overlay implementations).
 
3826         getPixelBounds: function (center, zoom) {
 
3827                 var topLeftPoint = this._getTopLeftPoint(center, zoom);
 
3828                 return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
 
3831         // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
 
3832         // the map pane? "left point of the map layer" can be confusing, specially
 
3833         // since there can be negative offsets.
 
3834         // @method getPixelOrigin(): Point
 
3835         // Returns the projected pixel coordinates of the top left point of
 
3836         // the map layer (useful in custom layer and overlay implementations).
 
3837         getPixelOrigin: function () {
 
3838                 this._checkIfLoaded();
 
3839                 return this._pixelOrigin;
 
3842         // @method getPixelWorldBounds(zoom?: Number): Bounds
 
3843         // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
 
3844         // If `zoom` is omitted, the map's current zoom level is used.
 
3845         getPixelWorldBounds: function (zoom) {
 
3846                 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
 
3849         // @section Other Methods
 
3851         // @method getPane(pane: String|HTMLElement): HTMLElement
 
3852         // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
 
3853         getPane: function (pane) {
 
3854                 return typeof pane === 'string' ? this._panes[pane] : pane;
 
3857         // @method getPanes(): Object
 
3858         // Returns a plain object containing the names of all [panes](#map-pane) as keys and
 
3859         // the panes as values.
 
3860         getPanes: function () {
 
3864         // @method getContainer: HTMLElement
 
3865         // Returns the HTML element that contains the map.
 
3866         getContainer: function () {
 
3867                 return this._container;
 
3871         // @section Conversion Methods
 
3873         // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
 
3874         // Returns the scale factor to be applied to a map transition from zoom level
 
3875         // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
 
3876         getZoomScale: function (toZoom, fromZoom) {
 
3877                 // TODO replace with universal implementation after refactoring projections
 
3878                 var crs = this.options.crs;
 
3879                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
 
3880                 return crs.scale(toZoom) / crs.scale(fromZoom);
 
3883         // @method getScaleZoom(scale: Number, fromZoom: Number): Number
 
3884         // Returns the zoom level that the map would end up at, if it is at `fromZoom`
 
3885         // level and everything is scaled by a factor of `scale`. Inverse of
 
3886         // [`getZoomScale`](#map-getZoomScale).
 
3887         getScaleZoom: function (scale, fromZoom) {
 
3888                 var crs = this.options.crs;
 
3889                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
 
3890                 var zoom = crs.zoom(scale * crs.scale(fromZoom));
 
3891                 return isNaN(zoom) ? Infinity : zoom;
 
3894         // @method project(latlng: LatLng, zoom: Number): Point
 
3895         // Projects a geographical coordinate `LatLng` according to the projection
 
3896         // of the map's CRS, then scales it according to `zoom` and the CRS's
 
3897         // `Transformation`. The result is pixel coordinate relative to
 
3899         project: function (latlng, zoom) {
 
3900                 zoom = zoom === undefined ? this._zoom : zoom;
 
3901                 return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
 
3904         // @method unproject(point: Point, zoom: Number): LatLng
 
3905         // Inverse of [`project`](#map-project).
 
3906         unproject: function (point, zoom) {
 
3907                 zoom = zoom === undefined ? this._zoom : zoom;
 
3908                 return this.options.crs.pointToLatLng(toPoint(point), zoom);
 
3911         // @method layerPointToLatLng(point: Point): LatLng
 
3912         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
 
3913         // returns the corresponding geographical coordinate (for the current zoom level).
 
3914         layerPointToLatLng: function (point) {
 
3915                 var projectedPoint = toPoint(point).add(this.getPixelOrigin());
 
3916                 return this.unproject(projectedPoint);
 
3919         // @method latLngToLayerPoint(latlng: LatLng): Point
 
3920         // Given a geographical coordinate, returns the corresponding pixel coordinate
 
3921         // relative to the [origin pixel](#map-getpixelorigin).
 
3922         latLngToLayerPoint: function (latlng) {
 
3923                 var projectedPoint = this.project(toLatLng(latlng))._round();
 
3924                 return projectedPoint._subtract(this.getPixelOrigin());
 
3927         // @method wrapLatLng(latlng: LatLng): LatLng
 
3928         // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
 
3929         // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
 
3931         // By default this means longitude is wrapped around the dateline so its
 
3932         // value is between -180 and +180 degrees.
 
3933         wrapLatLng: function (latlng) {
 
3934                 return this.options.crs.wrapLatLng(toLatLng(latlng));
 
3937         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
 
3938         // Returns a `LatLngBounds` with the same size as the given one, ensuring that
 
3939         // its center is within the CRS's bounds.
 
3940         // By default this means the center longitude is wrapped around the dateline so its
 
3941         // value is between -180 and +180 degrees, and the majority of the bounds
 
3942         // overlaps the CRS's bounds.
 
3943         wrapLatLngBounds: function (latlng) {
 
3944                 return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
 
3947         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
 
3948         // Returns the distance between two geographical coordinates according to
 
3949         // the map's CRS. By default this measures distance in meters.
 
3950         distance: function (latlng1, latlng2) {
 
3951                 return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
 
3954         // @method containerPointToLayerPoint(point: Point): Point
 
3955         // Given a pixel coordinate relative to the map container, returns the corresponding
 
3956         // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
 
3957         containerPointToLayerPoint: function (point) { // (Point)
 
3958                 return toPoint(point).subtract(this._getMapPanePos());
 
3961         // @method layerPointToContainerPoint(point: Point): Point
 
3962         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
 
3963         // returns the corresponding pixel coordinate relative to the map container.
 
3964         layerPointToContainerPoint: function (point) { // (Point)
 
3965                 return toPoint(point).add(this._getMapPanePos());
 
3968         // @method containerPointToLatLng(point: Point): LatLng
 
3969         // Given a pixel coordinate relative to the map container, returns
 
3970         // the corresponding geographical coordinate (for the current zoom level).
 
3971         containerPointToLatLng: function (point) {
 
3972                 var layerPoint = this.containerPointToLayerPoint(toPoint(point));
 
3973                 return this.layerPointToLatLng(layerPoint);
 
3976         // @method latLngToContainerPoint(latlng: LatLng): Point
 
3977         // Given a geographical coordinate, returns the corresponding pixel coordinate
 
3978         // relative to the map container.
 
3979         latLngToContainerPoint: function (latlng) {
 
3980                 return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
 
3983         // @method mouseEventToContainerPoint(ev: MouseEvent): Point
 
3984         // Given a MouseEvent object, returns the pixel coordinate relative to the
 
3985         // map container where the event took place.
 
3986         mouseEventToContainerPoint: function (e) {
 
3987                 return getMousePosition(e, this._container);
 
3990         // @method mouseEventToLayerPoint(ev: MouseEvent): Point
 
3991         // Given a MouseEvent object, returns the pixel coordinate relative to
 
3992         // the [origin pixel](#map-getpixelorigin) where the event took place.
 
3993         mouseEventToLayerPoint: function (e) {
 
3994                 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
 
3997         // @method mouseEventToLatLng(ev: MouseEvent): LatLng
 
3998         // Given a MouseEvent object, returns geographical coordinate where the
 
3999         // event took place.
 
4000         mouseEventToLatLng: function (e) { // (MouseEvent)
 
4001                 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
 
4005         // map initialization methods
 
4007         _initContainer: function (id) {
 
4008                 var container = this._container = get(id);
 
4011                         throw new Error('Map container not found.');
 
4012                 } else if (container._leaflet_id) {
 
4013                         throw new Error('Map container is already initialized.');
 
4016                 on(container, 'scroll', this._onScroll, this);
 
4017                 this._containerId = stamp(container);
 
4020         _initLayout: function () {
 
4021                 var container = this._container;
 
4023                 this._fadeAnimated = this.options.fadeAnimation && any3d;
 
4025                 addClass(container, 'leaflet-container' +
 
4026                         (touch ? ' leaflet-touch' : '') +
 
4027                         (retina ? ' leaflet-retina' : '') +
 
4028                         (ielt9 ? ' leaflet-oldie' : '') +
 
4029                         (safari ? ' leaflet-safari' : '') +
 
4030                         (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
 
4032                 var position = getStyle(container, 'position');
 
4034                 if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
 
4035                         container.style.position = 'relative';
 
4040                 if (this._initControlPos) {
 
4041                         this._initControlPos();
 
4045         _initPanes: function () {
 
4046                 var panes = this._panes = {};
 
4047                 this._paneRenderers = {};
 
4051                 // Panes are DOM elements used to control the ordering of layers on the map. You
 
4052                 // can access panes with [`map.getPane`](#map-getpane) or
 
4053                 // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
 
4054                 // [`map.createPane`](#map-createpane) method.
 
4056                 // Every map has the following default panes that differ only in zIndex.
 
4058                 // @pane mapPane: HTMLElement = 'auto'
 
4059                 // Pane that contains all other map panes
 
4061                 this._mapPane = this.createPane('mapPane', this._container);
 
4062                 setPosition(this._mapPane, new Point(0, 0));
 
4064                 // @pane tilePane: HTMLElement = 200
 
4065                 // Pane for `GridLayer`s and `TileLayer`s
 
4066                 this.createPane('tilePane');
 
4067                 // @pane overlayPane: HTMLElement = 400
 
4068                 // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s
 
4069                 this.createPane('shadowPane');
 
4070                 // @pane shadowPane: HTMLElement = 500
 
4071                 // Pane for overlay shadows (e.g. `Marker` shadows)
 
4072                 this.createPane('overlayPane');
 
4073                 // @pane markerPane: HTMLElement = 600
 
4074                 // Pane for `Icon`s of `Marker`s
 
4075                 this.createPane('markerPane');
 
4076                 // @pane tooltipPane: HTMLElement = 650
 
4077                 // Pane for `Tooltip`s.
 
4078                 this.createPane('tooltipPane');
 
4079                 // @pane popupPane: HTMLElement = 700
 
4080                 // Pane for `Popup`s.
 
4081                 this.createPane('popupPane');
 
4083                 if (!this.options.markerZoomAnimation) {
 
4084                         addClass(panes.markerPane, 'leaflet-zoom-hide');
 
4085                         addClass(panes.shadowPane, 'leaflet-zoom-hide');
 
4090         // private methods that modify map state
 
4092         // @section Map state change events
 
4093         _resetView: function (center, zoom) {
 
4094                 setPosition(this._mapPane, new Point(0, 0));
 
4096                 var loading = !this._loaded;
 
4097                 this._loaded = true;
 
4098                 zoom = this._limitZoom(zoom);
 
4100                 this.fire('viewprereset');
 
4102                 var zoomChanged = this._zoom !== zoom;
 
4104                         ._moveStart(zoomChanged, false)
 
4105                         ._move(center, zoom)
 
4106                         ._moveEnd(zoomChanged);
 
4108                 // @event viewreset: Event
 
4109                 // Fired when the map needs to redraw its content (this usually happens
 
4110                 // on map zoom or load). Very useful for creating custom overlays.
 
4111                 this.fire('viewreset');
 
4113                 // @event load: Event
 
4114                 // Fired when the map is initialized (when its center and zoom are set
 
4115                 // for the first time).
 
4121         _moveStart: function (zoomChanged, noMoveStart) {
 
4122                 // @event zoomstart: Event
 
4123                 // Fired when the map zoom is about to change (e.g. before zoom animation).
 
4124                 // @event movestart: Event
 
4125                 // Fired when the view of the map starts changing (e.g. user starts dragging the map).
 
4127                         this.fire('zoomstart');
 
4130                         this.fire('movestart');
 
4135         _move: function (center, zoom, data) {
 
4136                 if (zoom === undefined) {
 
4139                 var zoomChanged = this._zoom !== zoom;
 
4142                 this._lastCenter = center;
 
4143                 this._pixelOrigin = this._getNewPixelOrigin(center);
 
4145                 // @event zoom: Event
 
4146                 // Fired repeatedly during any change in zoom level, including zoom
 
4147                 // and fly animations.
 
4148                 if (zoomChanged || (data && data.pinch)) {      // Always fire 'zoom' if pinching because #3530
 
4149                         this.fire('zoom', data);
 
4152                 // @event move: Event
 
4153                 // Fired repeatedly during any movement of the map, including pan and
 
4155                 return this.fire('move', data);
 
4158         _moveEnd: function (zoomChanged) {
 
4159                 // @event zoomend: Event
 
4160                 // Fired when the map has changed, after any animations.
 
4162                         this.fire('zoomend');
 
4165                 // @event moveend: Event
 
4166                 // Fired when the center of the map stops changing (e.g. user stopped
 
4167                 // dragging the map).
 
4168                 return this.fire('moveend');
 
4171         _stop: function () {
 
4172                 cancelAnimFrame(this._flyToFrame);
 
4173                 if (this._panAnim) {
 
4174                         this._panAnim.stop();
 
4179         _rawPanBy: function (offset) {
 
4180                 setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
 
4183         _getZoomSpan: function () {
 
4184                 return this.getMaxZoom() - this.getMinZoom();
 
4187         _panInsideMaxBounds: function () {
 
4188                 if (!this._enforcingBounds) {
 
4189                         this.panInsideBounds(this.options.maxBounds);
 
4193         _checkIfLoaded: function () {
 
4194                 if (!this._loaded) {
 
4195                         throw new Error('Set map center and zoom first.');
 
4199         // DOM event handling
 
4201         // @section Interaction events
 
4202         _initEvents: function (remove$$1) {
 
4204                 this._targets[stamp(this._container)] = this;
 
4206                 var onOff = remove$$1 ? off : on;
 
4208                 // @event click: MouseEvent
 
4209                 // Fired when the user clicks (or taps) the map.
 
4210                 // @event dblclick: MouseEvent
 
4211                 // Fired when the user double-clicks (or double-taps) the map.
 
4212                 // @event mousedown: MouseEvent
 
4213                 // Fired when the user pushes the mouse button on the map.
 
4214                 // @event mouseup: MouseEvent
 
4215                 // Fired when the user releases the mouse button on the map.
 
4216                 // @event mouseover: MouseEvent
 
4217                 // Fired when the mouse enters the map.
 
4218                 // @event mouseout: MouseEvent
 
4219                 // Fired when the mouse leaves the map.
 
4220                 // @event mousemove: MouseEvent
 
4221                 // Fired while the mouse moves over the map.
 
4222                 // @event contextmenu: MouseEvent
 
4223                 // Fired when the user pushes the right mouse button on the map, prevents
 
4224                 // default browser context menu from showing if there are listeners on
 
4225                 // this event. Also fired on mobile when the user holds a single touch
 
4226                 // for a second (also called long press).
 
4227                 // @event keypress: KeyboardEvent
 
4228                 // Fired when the user presses a key from the keyboard while the map is focused.
 
4229                 onOff(this._container, 'click dblclick mousedown mouseup ' +
 
4230                         'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
 
4232                 if (this.options.trackResize) {
 
4233                         onOff(window, 'resize', this._onResize, this);
 
4236                 if (any3d && this.options.transform3DLimit) {
 
4237                         (remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
 
4241         _onResize: function () {
 
4242                 cancelAnimFrame(this._resizeRequest);
 
4243                 this._resizeRequest = requestAnimFrame(
 
4244                         function () { this.invalidateSize({debounceMoveend: true}); }, this);
 
4247         _onScroll: function () {
 
4248                 this._container.scrollTop  = 0;
 
4249                 this._container.scrollLeft = 0;
 
4252         _onMoveEnd: function () {
 
4253                 var pos = this._getMapPanePos();
 
4254                 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
 
4255                         // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
 
4256                         // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
 
4257                         this._resetView(this.getCenter(), this.getZoom());
 
4261         _findEventTargets: function (e, type) {
 
4264                     isHover = type === 'mouseout' || type === 'mouseover',
 
4265                     src = e.target || e.srcElement,
 
4269                         target = this._targets[stamp(src)];
 
4270                         if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
 
4271                                 // Prevent firing click after you just dragged an object.
 
4275                         if (target && target.listens(type, true)) {
 
4276                                 if (isHover && !isExternalTarget(src, e)) { break; }
 
4277                                 targets.push(target);
 
4278                                 if (isHover) { break; }
 
4280                         if (src === this._container) { break; }
 
4281                         src = src.parentNode;
 
4283                 if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) {
 
4289         _handleDOMEvent: function (e) {
 
4290                 if (!this._loaded || skipped(e)) { return; }
 
4294                 if (type === 'mousedown' || type === 'keypress') {
 
4295                         // prevents outline when clicking on keyboard-focusable element
 
4296                         preventOutline(e.target || e.srcElement);
 
4299                 this._fireDOMEvent(e, type);
 
4302         _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
 
4304         _fireDOMEvent: function (e, type, targets) {
 
4306                 if (e.type === 'click') {
 
4307                         // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
 
4308                         // @event preclick: MouseEvent
 
4309                         // Fired before mouse click on the map (sometimes useful when you
 
4310                         // want something to happen on click before any existing click
 
4311                         // handlers start running).
 
4312                         var synth = extend({}, e);
 
4313                         synth.type = 'preclick';
 
4314                         this._fireDOMEvent(synth, synth.type, targets);
 
4317                 if (e._stopped) { return; }
 
4319                 // Find the layer the event is propagating from and its parents.
 
4320                 targets = (targets || []).concat(this._findEventTargets(e, type));
 
4322                 if (!targets.length) { return; }
 
4324                 var target = targets[0];
 
4325                 if (type === 'contextmenu' && target.listens(type, true)) {
 
4333                 if (e.type !== 'keypress') {
 
4334                         var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
 
4335                         data.containerPoint = isMarker ?
 
4336                                 this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
 
4337                         data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
 
4338                         data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
 
4341                 for (var i = 0; i < targets.length; i++) {
 
4342                         targets[i].fire(type, data, true);
 
4343                         if (data.originalEvent._stopped ||
 
4344                                 (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
 
4348         _draggableMoved: function (obj) {
 
4349                 obj = obj.dragging && obj.dragging.enabled() ? obj : this;
 
4350                 return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
 
4353         _clearHandlers: function () {
 
4354                 for (var i = 0, len = this._handlers.length; i < len; i++) {
 
4355                         this._handlers[i].disable();
 
4359         // @section Other Methods
 
4361         // @method whenReady(fn: Function, context?: Object): this
 
4362         // Runs the given function `fn` when the map gets initialized with
 
4363         // a view (center and zoom) and at least one layer, or immediately
 
4364         // if it's already initialized, optionally passing a function context.
 
4365         whenReady: function (callback, context) {
 
4367                         callback.call(context || this, {target: this});
 
4369                         this.on('load', callback, context);
 
4375         // private methods for getting map state
 
4377         _getMapPanePos: function () {
 
4378                 return getPosition(this._mapPane) || new Point(0, 0);
 
4381         _moved: function () {
 
4382                 var pos = this._getMapPanePos();
 
4383                 return pos && !pos.equals([0, 0]);
 
4386         _getTopLeftPoint: function (center, zoom) {
 
4387                 var pixelOrigin = center && zoom !== undefined ?
 
4388                         this._getNewPixelOrigin(center, zoom) :
 
4389                         this.getPixelOrigin();
 
4390                 return pixelOrigin.subtract(this._getMapPanePos());
 
4393         _getNewPixelOrigin: function (center, zoom) {
 
4394                 var viewHalf = this.getSize()._divideBy(2);
 
4395                 return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
 
4398         _latLngToNewLayerPoint: function (latlng, zoom, center) {
 
4399                 var topLeft = this._getNewPixelOrigin(center, zoom);
 
4400                 return this.project(latlng, zoom)._subtract(topLeft);
 
4403         _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
 
4404                 var topLeft = this._getNewPixelOrigin(center, zoom);
 
4406                         this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
 
4407                         this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
 
4408                         this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
 
4409                         this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
 
4413         // layer point of the current center
 
4414         _getCenterLayerPoint: function () {
 
4415                 return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
 
4418         // offset of the specified place to the current center in pixels
 
4419         _getCenterOffset: function (latlng) {
 
4420                 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
 
4423         // adjust center for view to get inside bounds
 
4424         _limitCenter: function (center, zoom, bounds) {
 
4426                 if (!bounds) { return center; }
 
4428                 var centerPoint = this.project(center, zoom),
 
4429                     viewHalf = this.getSize().divideBy(2),
 
4430                     viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
 
4431                     offset = this._getBoundsOffset(viewBounds, bounds, zoom);
 
4433                 // If offset is less than a pixel, ignore.
 
4434                 // This prevents unstable projections from getting into
 
4435                 // an infinite loop of tiny offsets.
 
4436                 if (offset.round().equals([0, 0])) {
 
4440                 return this.unproject(centerPoint.add(offset), zoom);
 
4443         // adjust offset for view to get inside bounds
 
4444         _limitOffset: function (offset, bounds) {
 
4445                 if (!bounds) { return offset; }
 
4447                 var viewBounds = this.getPixelBounds(),
 
4448                     newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
 
4450                 return offset.add(this._getBoundsOffset(newBounds, bounds));
 
4453         // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
 
4454         _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
 
4455                 var projectedMaxBounds = toBounds(
 
4456                         this.project(maxBounds.getNorthEast(), zoom),
 
4457                         this.project(maxBounds.getSouthWest(), zoom)
 
4459                     minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
 
4460                     maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
 
4462                     dx = this._rebound(minOffset.x, -maxOffset.x),
 
4463                     dy = this._rebound(minOffset.y, -maxOffset.y);
 
4465                 return new Point(dx, dy);
 
4468         _rebound: function (left, right) {
 
4469                 return left + right > 0 ?
 
4470                         Math.round(left - right) / 2 :
 
4471                         Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
 
4474         _limitZoom: function (zoom) {
 
4475                 var min = this.getMinZoom(),
 
4476                     max = this.getMaxZoom(),
 
4477                     snap = any3d ? this.options.zoomSnap : 1;
 
4479                         zoom = Math.round(zoom / snap) * snap;
 
4481                 return Math.max(min, Math.min(max, zoom));
 
4484         _onPanTransitionStep: function () {
 
4488         _onPanTransitionEnd: function () {
 
4489                 removeClass(this._mapPane, 'leaflet-pan-anim');
 
4490                 this.fire('moveend');
 
4493         _tryAnimatedPan: function (center, options) {
 
4494                 // difference between the new and current centers in pixels
 
4495                 var offset = this._getCenterOffset(center)._trunc();
 
4497                 // don't animate too far unless animate: true specified in options
 
4498                 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
 
4500                 this.panBy(offset, options);
 
4505         _createAnimProxy: function () {
 
4507                 var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
 
4508                 this._panes.mapPane.appendChild(proxy);
 
4510                 this.on('zoomanim', function (e) {
 
4511                         var prop = TRANSFORM,
 
4512                             transform = this._proxy.style[prop];
 
4514                         setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
 
4516                         // workaround for case when transform is the same and so transitionend event is not fired
 
4517                         if (transform === this._proxy.style[prop] && this._animatingZoom) {
 
4518                                 this._onZoomTransitionEnd();
 
4522                 this.on('load moveend', function () {
 
4523                         var c = this.getCenter(),
 
4525                         setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
 
4528                 this._on('unload', this._destroyAnimProxy, this);
 
4531         _destroyAnimProxy: function () {
 
4532                 remove(this._proxy);
 
4536         _catchTransitionEnd: function (e) {
 
4537                 if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
 
4538                         this._onZoomTransitionEnd();
 
4542         _nothingToAnimate: function () {
 
4543                 return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
 
4546         _tryAnimatedZoom: function (center, zoom, options) {
 
4548                 if (this._animatingZoom) { return true; }
 
4550                 options = options || {};
 
4552                 // don't animate if disabled, not supported or zoom difference is too large
 
4553                 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
 
4554                         Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
 
4556                 // offset is the pixel coords of the zoom origin relative to the current center
 
4557                 var scale = this.getZoomScale(zoom),
 
4558                     offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
 
4560                 // don't animate if the zoom origin isn't within one screen from the current center, unless forced
 
4561                 if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
 
4563                 requestAnimFrame(function () {
 
4565                             ._moveStart(true, false)
 
4566                             ._animateZoom(center, zoom, true);
 
4572         _animateZoom: function (center, zoom, startAnim, noUpdate) {
 
4573                 if (!this._mapPane) { return; }
 
4576                         this._animatingZoom = true;
 
4578                         // remember what center/zoom to set after animation
 
4579                         this._animateToCenter = center;
 
4580                         this._animateToZoom = zoom;
 
4582                         addClass(this._mapPane, 'leaflet-zoom-anim');
 
4585                 // @event zoomanim: ZoomAnimEvent
 
4586                 // Fired on every frame of a zoom animation
 
4587                 this.fire('zoomanim', {
 
4593                 // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
 
4594                 setTimeout(bind(this._onZoomTransitionEnd, this), 250);
 
4597         _onZoomTransitionEnd: function () {
 
4598                 if (!this._animatingZoom) { return; }
 
4600                 if (this._mapPane) {
 
4601                         removeClass(this._mapPane, 'leaflet-zoom-anim');
 
4604                 this._animatingZoom = false;
 
4606                 this._move(this._animateToCenter, this._animateToZoom);
 
4608                 // This anim frame should prevent an obscure iOS webkit tile loading race condition.
 
4609                 requestAnimFrame(function () {
 
4610                         this._moveEnd(true);
 
4617 // @factory L.map(id: String, options?: Map options)
 
4618 // Instantiates a map object given the DOM ID of a `<div>` element
 
4619 // and optionally an object literal with `Map options`.
 
4622 // @factory L.map(el: HTMLElement, options?: Map options)
 
4623 // Instantiates a map object given an instance of a `<div>` HTML element
 
4624 // and optionally an object literal with `Map options`.
 
4625 function createMap(id, options) {
 
4626         return new Map(id, options);
 
4634  * L.Control is a base class for implementing map controls. Handles positioning.
 
4635  * All other controls extend from this class.
 
4638 var Control = Class.extend({
 
4640         // @aka Control options
 
4642                 // @option position: String = 'topright'
 
4643                 // The position of the control (one of the map corners). Possible values are `'topleft'`,
 
4644                 // `'topright'`, `'bottomleft'` or `'bottomright'`
 
4645                 position: 'topright'
 
4648         initialize: function (options) {
 
4649                 setOptions(this, options);
 
4653          * Classes extending L.Control will inherit the following methods:
 
4655          * @method getPosition: string
 
4656          * Returns the position of the control.
 
4658         getPosition: function () {
 
4659                 return this.options.position;
 
4662         // @method setPosition(position: string): this
 
4663         // Sets the position of the control.
 
4664         setPosition: function (position) {
 
4665                 var map = this._map;
 
4668                         map.removeControl(this);
 
4671                 this.options.position = position;
 
4674                         map.addControl(this);
 
4680         // @method getContainer: HTMLElement
 
4681         // Returns the HTMLElement that contains the control.
 
4682         getContainer: function () {
 
4683                 return this._container;
 
4686         // @method addTo(map: Map): this
 
4687         // Adds the control to the given map.
 
4688         addTo: function (map) {
 
4692                 var container = this._container = this.onAdd(map),
 
4693                     pos = this.getPosition(),
 
4694                     corner = map._controlCorners[pos];
 
4696                 addClass(container, 'leaflet-control');
 
4698                 if (pos.indexOf('bottom') !== -1) {
 
4699                         corner.insertBefore(container, corner.firstChild);
 
4701                         corner.appendChild(container);
 
4707         // @method remove: this
 
4708         // Removes the control from the map it is currently active on.
 
4709         remove: function () {
 
4714                 remove(this._container);
 
4716                 if (this.onRemove) {
 
4717                         this.onRemove(this._map);
 
4725         _refocusOnMap: function (e) {
 
4726                 // if map exists and event is not a keyboard event
 
4727                 if (this._map && e && e.screenX > 0 && e.screenY > 0) {
 
4728                         this._map.getContainer().focus();
 
4733 var control = function (options) {
 
4734         return new Control(options);
 
4737 /* @section Extension methods
 
4740  * Every control should extend from `L.Control` and (re-)implement the following methods.
 
4742  * @method onAdd(map: Map): HTMLElement
 
4743  * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
 
4745  * @method onRemove(map: Map)
 
4746  * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
 
4750  * @section Methods for Layers and Controls
 
4753         // @method addControl(control: Control): this
 
4754         // Adds the given control to the map
 
4755         addControl: function (control) {
 
4756                 control.addTo(this);
 
4760         // @method removeControl(control: Control): this
 
4761         // Removes the given control from the map
 
4762         removeControl: function (control) {
 
4767         _initControlPos: function () {
 
4768                 var corners = this._controlCorners = {},
 
4770                     container = this._controlContainer =
 
4771                             create$1('div', l + 'control-container', this._container);
 
4773                 function createCorner(vSide, hSide) {
 
4774                         var className = l + vSide + ' ' + l + hSide;
 
4776                         corners[vSide + hSide] = create$1('div', className, container);
 
4779                 createCorner('top', 'left');
 
4780                 createCorner('top', 'right');
 
4781                 createCorner('bottom', 'left');
 
4782                 createCorner('bottom', 'right');
 
4785         _clearControlPos: function () {
 
4786                 for (var i in this._controlCorners) {
 
4787                         remove(this._controlCorners[i]);
 
4789                 remove(this._controlContainer);
 
4790                 delete this._controlCorners;
 
4791                 delete this._controlContainer;
 
4796  * @class Control.Layers
 
4797  * @aka L.Control.Layers
 
4800  * 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`.
 
4805  * var baseLayers = {
 
4807  *      "OpenStreetMap": osm
 
4812  *      "Roads": roadsLayer
 
4815  * L.control.layers(baseLayers, overlays).addTo(map);
 
4818  * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
 
4822  *     "<someName1>": layer1,
 
4823  *     "<someName2>": layer2
 
4827  * The layer names can contain HTML, which allows you to add additional styling to the items:
 
4830  * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
 
4834 var Layers = Control.extend({
 
4836         // @aka Control.Layers options
 
4838                 // @option collapsed: Boolean = true
 
4839                 // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
 
4841                 position: 'topright',
 
4843                 // @option autoZIndex: Boolean = true
 
4844                 // 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.
 
4847                 // @option hideSingleBase: Boolean = false
 
4848                 // If `true`, the base layers in the control will be hidden when there is only one.
 
4849                 hideSingleBase: false,
 
4851                 // @option sortLayers: Boolean = false
 
4852                 // Whether to sort the layers. When `false`, layers will keep the order
 
4853                 // in which they were added to the control.
 
4856                 // @option sortFunction: Function = *
 
4857                 // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
 
4858                 // that will be used for sorting the layers, when `sortLayers` is `true`.
 
4859                 // The function receives both the `L.Layer` instances and their names, as in
 
4860                 // `sortFunction(layerA, layerB, nameA, nameB)`.
 
4861                 // By default, it sorts layers alphabetically by their name.
 
4862                 sortFunction: function (layerA, layerB, nameA, nameB) {
 
4863                         return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
 
4867         initialize: function (baseLayers, overlays, options) {
 
4868                 setOptions(this, options);
 
4870                 this._layerControlInputs = [];
 
4872                 this._lastZIndex = 0;
 
4873                 this._handlingClick = false;
 
4875                 for (var i in baseLayers) {
 
4876                         this._addLayer(baseLayers[i], i);
 
4879                 for (i in overlays) {
 
4880                         this._addLayer(overlays[i], i, true);
 
4884         onAdd: function (map) {
 
4889                 map.on('zoomend', this._checkDisabledLayers, this);
 
4891                 for (var i = 0; i < this._layers.length; i++) {
 
4892                         this._layers[i].layer.on('add remove', this._onLayerChange, this);
 
4895                 return this._container;
 
4898         addTo: function (map) {
 
4899                 Control.prototype.addTo.call(this, map);
 
4900                 // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
 
4901                 return this._expandIfNotCollapsed();
 
4904         onRemove: function () {
 
4905                 this._map.off('zoomend', this._checkDisabledLayers, this);
 
4907                 for (var i = 0; i < this._layers.length; i++) {
 
4908                         this._layers[i].layer.off('add remove', this._onLayerChange, this);
 
4912         // @method addBaseLayer(layer: Layer, name: String): this
 
4913         // Adds a base layer (radio button entry) with the given name to the control.
 
4914         addBaseLayer: function (layer, name) {
 
4915                 this._addLayer(layer, name);
 
4916                 return (this._map) ? this._update() : this;
 
4919         // @method addOverlay(layer: Layer, name: String): this
 
4920         // Adds an overlay (checkbox entry) with the given name to the control.
 
4921         addOverlay: function (layer, name) {
 
4922                 this._addLayer(layer, name, true);
 
4923                 return (this._map) ? this._update() : this;
 
4926         // @method removeLayer(layer: Layer): this
 
4927         // Remove the given layer from the control.
 
4928         removeLayer: function (layer) {
 
4929                 layer.off('add remove', this._onLayerChange, this);
 
4931                 var obj = this._getLayer(stamp(layer));
 
4933                         this._layers.splice(this._layers.indexOf(obj), 1);
 
4935                 return (this._map) ? this._update() : this;
 
4938         // @method expand(): this
 
4939         // Expand the control container if collapsed.
 
4940         expand: function () {
 
4941                 addClass(this._container, 'leaflet-control-layers-expanded');
 
4942                 this._form.style.height = null;
 
4943                 var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
 
4944                 if (acceptableHeight < this._form.clientHeight) {
 
4945                         addClass(this._form, 'leaflet-control-layers-scrollbar');
 
4946                         this._form.style.height = acceptableHeight + 'px';
 
4948                         removeClass(this._form, 'leaflet-control-layers-scrollbar');
 
4950                 this._checkDisabledLayers();
 
4954         // @method collapse(): this
 
4955         // Collapse the control container if expanded.
 
4956         collapse: function () {
 
4957                 removeClass(this._container, 'leaflet-control-layers-expanded');
 
4961         _initLayout: function () {
 
4962                 var className = 'leaflet-control-layers',
 
4963                     container = this._container = create$1('div', className),
 
4964                     collapsed = this.options.collapsed;
 
4966                 // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
 
4967                 container.setAttribute('aria-haspopup', true);
 
4969                 disableClickPropagation(container);
 
4970                 disableScrollPropagation(container);
 
4972                 var form = this._form = create$1('form', className + '-list');
 
4975                         this._map.on('click', this.collapse, this);
 
4979                                         mouseenter: this.expand,
 
4980                                         mouseleave: this.collapse
 
4985                 var link = this._layersLink = create$1('a', className + '-toggle', container);
 
4987                 link.title = 'Layers';
 
4990                         on(link, 'click', stop);
 
4991                         on(link, 'click', this.expand, this);
 
4993                         on(link, 'focus', this.expand, this);
 
5000                 this._baseLayersList = create$1('div', className + '-base', form);
 
5001                 this._separator = create$1('div', className + '-separator', form);
 
5002                 this._overlaysList = create$1('div', className + '-overlays', form);
 
5004                 container.appendChild(form);
 
5007         _getLayer: function (id) {
 
5008                 for (var i = 0; i < this._layers.length; i++) {
 
5010                         if (this._layers[i] && stamp(this._layers[i].layer) === id) {
 
5011                                 return this._layers[i];
 
5016         _addLayer: function (layer, name, overlay) {
 
5018                         layer.on('add remove', this._onLayerChange, this);
 
5027                 if (this.options.sortLayers) {
 
5028                         this._layers.sort(bind(function (a, b) {
 
5029                                 return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
 
5033                 if (this.options.autoZIndex && layer.setZIndex) {
 
5035                         layer.setZIndex(this._lastZIndex);
 
5038                 this._expandIfNotCollapsed();
 
5041         _update: function () {
 
5042                 if (!this._container) { return this; }
 
5044                 empty(this._baseLayersList);
 
5045                 empty(this._overlaysList);
 
5047                 this._layerControlInputs = [];
 
5048                 var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
 
5050                 for (i = 0; i < this._layers.length; i++) {
 
5051                         obj = this._layers[i];
 
5053                         overlaysPresent = overlaysPresent || obj.overlay;
 
5054                         baseLayersPresent = baseLayersPresent || !obj.overlay;
 
5055                         baseLayersCount += !obj.overlay ? 1 : 0;
 
5058                 // Hide base layers section if there's only one layer.
 
5059                 if (this.options.hideSingleBase) {
 
5060                         baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
 
5061                         this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
 
5064                 this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
 
5069         _onLayerChange: function (e) {
 
5070                 if (!this._handlingClick) {
 
5074                 var obj = this._getLayer(stamp(e.target));
 
5077                 // @section Layer events
 
5078                 // @event baselayerchange: LayersControlEvent
 
5079                 // Fired when the base layer is changed through the [layer control](#control-layers).
 
5080                 // @event overlayadd: LayersControlEvent
 
5081                 // Fired when an overlay is selected through the [layer control](#control-layers).
 
5082                 // @event overlayremove: LayersControlEvent
 
5083                 // Fired when an overlay is deselected through the [layer control](#control-layers).
 
5084                 // @namespace Control.Layers
 
5085                 var type = obj.overlay ?
 
5086                         (e.type === 'add' ? 'overlayadd' : 'overlayremove') :
 
5087                         (e.type === 'add' ? 'baselayerchange' : null);
 
5090                         this._map.fire(type, obj);
 
5094         // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
 
5095         _createRadioElement: function (name, checked) {
 
5097                 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
 
5098                                 name + '"' + (checked ? ' checked="checked"' : '') + '/>';
 
5100                 var radioFragment = document.createElement('div');
 
5101                 radioFragment.innerHTML = radioHtml;
 
5103                 return radioFragment.firstChild;
 
5106         _addItem: function (obj) {
 
5107                 var label = document.createElement('label'),
 
5108                     checked = this._map.hasLayer(obj.layer),
 
5112                         input = document.createElement('input');
 
5113                         input.type = 'checkbox';
 
5114                         input.className = 'leaflet-control-layers-selector';
 
5115                         input.defaultChecked = checked;
 
5117                         input = this._createRadioElement('leaflet-base-layers', checked);
 
5120                 this._layerControlInputs.push(input);
 
5121                 input.layerId = stamp(obj.layer);
 
5123                 on(input, 'click', this._onInputClick, this);
 
5125                 var name = document.createElement('span');
 
5126                 name.innerHTML = ' ' + obj.name;
 
5128                 // Helps from preventing layer control flicker when checkboxes are disabled
 
5129                 // https://github.com/Leaflet/Leaflet/issues/2771
 
5130                 var holder = document.createElement('div');
 
5132                 label.appendChild(holder);
 
5133                 holder.appendChild(input);
 
5134                 holder.appendChild(name);
 
5136                 var container = obj.overlay ? this._overlaysList : this._baseLayersList;
 
5137                 container.appendChild(label);
 
5139                 this._checkDisabledLayers();
 
5143         _onInputClick: function () {
 
5144                 var inputs = this._layerControlInputs,
 
5146                 var addedLayers = [],
 
5149                 this._handlingClick = true;
 
5151                 for (var i = inputs.length - 1; i >= 0; i--) {
 
5153                         layer = this._getLayer(input.layerId).layer;
 
5155                         if (input.checked) {
 
5156                                 addedLayers.push(layer);
 
5157                         } else if (!input.checked) {
 
5158                                 removedLayers.push(layer);
 
5162                 // Bugfix issue 2318: Should remove all old layers before readding new ones
 
5163                 for (i = 0; i < removedLayers.length; i++) {
 
5164                         if (this._map.hasLayer(removedLayers[i])) {
 
5165                                 this._map.removeLayer(removedLayers[i]);
 
5168                 for (i = 0; i < addedLayers.length; i++) {
 
5169                         if (!this._map.hasLayer(addedLayers[i])) {
 
5170                                 this._map.addLayer(addedLayers[i]);
 
5174                 this._handlingClick = false;
 
5176                 this._refocusOnMap();
 
5179         _checkDisabledLayers: function () {
 
5180                 var inputs = this._layerControlInputs,
 
5183                     zoom = this._map.getZoom();
 
5185                 for (var i = inputs.length - 1; i >= 0; i--) {
 
5187                         layer = this._getLayer(input.layerId).layer;
 
5188                         input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
 
5189                                          (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
 
5194         _expandIfNotCollapsed: function () {
 
5195                 if (this._map && !this.options.collapsed) {
 
5201         _expand: function () {
 
5202                 // Backward compatibility, remove me in 1.1.
 
5203                 return this.expand();
 
5206         _collapse: function () {
 
5207                 // Backward compatibility, remove me in 1.1.
 
5208                 return this.collapse();
 
5214 // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
 
5215 // 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.
 
5216 var layers = function (baseLayers, overlays, options) {
 
5217         return new Layers(baseLayers, overlays, options);
 
5221  * @class Control.Zoom
 
5222  * @aka L.Control.Zoom
 
5225  * 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`.
 
5228 var Zoom = Control.extend({
 
5230         // @aka Control.Zoom options
 
5232                 position: 'topleft',
 
5234                 // @option zoomInText: String = '+'
 
5235                 // The text set on the 'zoom in' button.
 
5238                 // @option zoomInTitle: String = 'Zoom in'
 
5239                 // The title set on the 'zoom in' button.
 
5240                 zoomInTitle: 'Zoom in',
 
5242                 // @option zoomOutText: String = '−'
 
5243                 // The text set on the 'zoom out' button.
 
5244                 zoomOutText: '−',
 
5246                 // @option zoomOutTitle: String = 'Zoom out'
 
5247                 // The title set on the 'zoom out' button.
 
5248                 zoomOutTitle: 'Zoom out'
 
5251         onAdd: function (map) {
 
5252                 var zoomName = 'leaflet-control-zoom',
 
5253                     container = create$1('div', zoomName + ' leaflet-bar'),
 
5254                     options = this.options;
 
5256                 this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
 
5257                         zoomName + '-in',  container, this._zoomIn);
 
5258                 this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
 
5259                         zoomName + '-out', container, this._zoomOut);
 
5261                 this._updateDisabled();
 
5262                 map.on('zoomend zoomlevelschange', this._updateDisabled, this);
 
5267         onRemove: function (map) {
 
5268                 map.off('zoomend zoomlevelschange', this._updateDisabled, this);
 
5271         disable: function () {
 
5272                 this._disabled = true;
 
5273                 this._updateDisabled();
 
5277         enable: function () {
 
5278                 this._disabled = false;
 
5279                 this._updateDisabled();
 
5283         _zoomIn: function (e) {
 
5284                 if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
 
5285                         this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
 
5289         _zoomOut: function (e) {
 
5290                 if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
 
5291                         this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
 
5295         _createButton: function (html, title, className, container, fn) {
 
5296                 var link = create$1('a', className, container);
 
5297                 link.innerHTML = html;
 
5302                  * Will force screen readers like VoiceOver to read this as "Zoom in - button"
 
5304                 link.setAttribute('role', 'button');
 
5305                 link.setAttribute('aria-label', title);
 
5307                 disableClickPropagation(link);
 
5308                 on(link, 'click', stop);
 
5309                 on(link, 'click', fn, this);
 
5310                 on(link, 'click', this._refocusOnMap, this);
 
5315         _updateDisabled: function () {
 
5316                 var map = this._map,
 
5317                     className = 'leaflet-disabled';
 
5319                 removeClass(this._zoomInButton, className);
 
5320                 removeClass(this._zoomOutButton, className);
 
5322                 if (this._disabled || map._zoom === map.getMinZoom()) {
 
5323                         addClass(this._zoomOutButton, className);
 
5325                 if (this._disabled || map._zoom === map.getMaxZoom()) {
 
5326                         addClass(this._zoomInButton, className);
 
5332 // @section Control options
 
5333 // @option zoomControl: Boolean = true
 
5334 // Whether a [zoom control](#control-zoom) is added to the map by default.
 
5339 Map.addInitHook(function () {
 
5340         if (this.options.zoomControl) {
 
5341                 this.zoomControl = new Zoom();
 
5342                 this.addControl(this.zoomControl);
 
5346 // @namespace Control.Zoom
 
5347 // @factory L.control.zoom(options: Control.Zoom options)
 
5348 // Creates a zoom control
 
5349 var zoom = function (options) {
 
5350         return new Zoom(options);
 
5354  * @class Control.Scale
 
5355  * @aka L.Control.Scale
 
5358  * 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`.
 
5363  * L.control.scale().addTo(map);
 
5367 var Scale = Control.extend({
 
5369         // @aka Control.Scale options
 
5371                 position: 'bottomleft',
 
5373                 // @option maxWidth: Number = 100
 
5374                 // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
 
5377                 // @option metric: Boolean = True
 
5378                 // Whether to show the metric scale line (m/km).
 
5381                 // @option imperial: Boolean = True
 
5382                 // Whether to show the imperial scale line (mi/ft).
 
5385                 // @option updateWhenIdle: Boolean = false
 
5386                 // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
 
5389         onAdd: function (map) {
 
5390                 var className = 'leaflet-control-scale',
 
5391                     container = create$1('div', className),
 
5392                     options = this.options;
 
5394                 this._addScales(options, className + '-line', container);
 
5396                 map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
5397                 map.whenReady(this._update, this);
 
5402         onRemove: function (map) {
 
5403                 map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
5406         _addScales: function (options, className, container) {
 
5407                 if (options.metric) {
 
5408                         this._mScale = create$1('div', className, container);
 
5410                 if (options.imperial) {
 
5411                         this._iScale = create$1('div', className, container);
 
5415         _update: function () {
 
5416                 var map = this._map,
 
5417                     y = map.getSize().y / 2;
 
5419                 var maxMeters = map.distance(
 
5420                         map.containerPointToLatLng([0, y]),
 
5421                         map.containerPointToLatLng([this.options.maxWidth, y]));
 
5423                 this._updateScales(maxMeters);
 
5426         _updateScales: function (maxMeters) {
 
5427                 if (this.options.metric && maxMeters) {
 
5428                         this._updateMetric(maxMeters);
 
5430                 if (this.options.imperial && maxMeters) {
 
5431                         this._updateImperial(maxMeters);
 
5435         _updateMetric: function (maxMeters) {
 
5436                 var meters = this._getRoundNum(maxMeters),
 
5437                     label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
 
5439                 this._updateScale(this._mScale, label, meters / maxMeters);
 
5442         _updateImperial: function (maxMeters) {
 
5443                 var maxFeet = maxMeters * 3.2808399,
 
5444                     maxMiles, miles, feet;
 
5446                 if (maxFeet > 5280) {
 
5447                         maxMiles = maxFeet / 5280;
 
5448                         miles = this._getRoundNum(maxMiles);
 
5449                         this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
 
5452                         feet = this._getRoundNum(maxFeet);
 
5453                         this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
 
5457         _updateScale: function (scale, text, ratio) {
 
5458                 scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
 
5459                 scale.innerHTML = text;
 
5462         _getRoundNum: function (num) {
 
5463                 var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
 
5476 // @factory L.control.scale(options?: Control.Scale options)
 
5477 // Creates an scale control with the given options.
 
5478 var scale = function (options) {
 
5479         return new Scale(options);
 
5483  * @class Control.Attribution
 
5484  * @aka L.Control.Attribution
 
5487  * 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.
 
5490 var Attribution = Control.extend({
 
5492         // @aka Control.Attribution options
 
5494                 position: 'bottomright',
 
5496                 // @option prefix: String = 'Leaflet'
 
5497                 // The HTML text shown before the attributions. Pass `false` to disable.
 
5498                 prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
 
5501         initialize: function (options) {
 
5502                 setOptions(this, options);
 
5504                 this._attributions = {};
 
5507         onAdd: function (map) {
 
5508                 map.attributionControl = this;
 
5509                 this._container = create$1('div', 'leaflet-control-attribution');
 
5510                 disableClickPropagation(this._container);
 
5512                 // TODO ugly, refactor
 
5513                 for (var i in map._layers) {
 
5514                         if (map._layers[i].getAttribution) {
 
5515                                 this.addAttribution(map._layers[i].getAttribution());
 
5521                 return this._container;
 
5524         // @method setPrefix(prefix: String): this
 
5525         // Sets the text before the attributions.
 
5526         setPrefix: function (prefix) {
 
5527                 this.options.prefix = prefix;
 
5532         // @method addAttribution(text: String): this
 
5533         // Adds an attribution text (e.g. `'Vector data © Mapbox'`).
 
5534         addAttribution: function (text) {
 
5535                 if (!text) { return this; }
 
5537                 if (!this._attributions[text]) {
 
5538                         this._attributions[text] = 0;
 
5540                 this._attributions[text]++;
 
5547         // @method removeAttribution(text: String): this
 
5548         // Removes an attribution text.
 
5549         removeAttribution: function (text) {
 
5550                 if (!text) { return this; }
 
5552                 if (this._attributions[text]) {
 
5553                         this._attributions[text]--;
 
5560         _update: function () {
 
5561                 if (!this._map) { return; }
 
5565                 for (var i in this._attributions) {
 
5566                         if (this._attributions[i]) {
 
5571                 var prefixAndAttribs = [];
 
5573                 if (this.options.prefix) {
 
5574                         prefixAndAttribs.push(this.options.prefix);
 
5576                 if (attribs.length) {
 
5577                         prefixAndAttribs.push(attribs.join(', '));
 
5580                 this._container.innerHTML = prefixAndAttribs.join(' | ');
 
5585 // @section Control options
 
5586 // @option attributionControl: Boolean = true
 
5587 // Whether a [attribution control](#control-attribution) is added to the map by default.
 
5589         attributionControl: true
 
5592 Map.addInitHook(function () {
 
5593         if (this.options.attributionControl) {
 
5594                 new Attribution().addTo(this);
 
5598 // @namespace Control.Attribution
 
5599 // @factory L.control.attribution(options: Control.Attribution options)
 
5600 // Creates an attribution control.
 
5601 var attribution = function (options) {
 
5602         return new Attribution(options);
 
5605 Control.Layers = Layers;
 
5606 Control.Zoom = Zoom;
 
5607 Control.Scale = Scale;
 
5608 Control.Attribution = Attribution;
 
5610 control.layers = layers;
 
5611 control.zoom = zoom;
 
5612 control.scale = scale;
 
5613 control.attribution = attribution;
 
5616         L.Handler is a base class for handler classes that are used internally to inject
 
5617         interaction features like dragging to classes like Map and Marker.
 
5622 // Abstract class for map interaction handlers
 
5624 var Handler = Class.extend({
 
5625         initialize: function (map) {
 
5629         // @method enable(): this
 
5630         // Enables the handler
 
5631         enable: function () {
 
5632                 if (this._enabled) { return this; }
 
5634                 this._enabled = true;
 
5639         // @method disable(): this
 
5640         // Disables the handler
 
5641         disable: function () {
 
5642                 if (!this._enabled) { return this; }
 
5644                 this._enabled = false;
 
5649         // @method enabled(): Boolean
 
5650         // Returns `true` if the handler is enabled
 
5651         enabled: function () {
 
5652                 return !!this._enabled;
 
5655         // @section Extension methods
 
5656         // Classes inheriting from `Handler` must implement the two following methods:
 
5657         // @method addHooks()
 
5658         // Called when the handler is enabled, should add event hooks.
 
5659         // @method removeHooks()
 
5660         // Called when the handler is disabled, should remove the event hooks added previously.
 
5663 // @section There is static function which can be called without instantiating L.Handler:
 
5664 // @function addTo(map: Map, name: String): this
 
5665 // Adds a new Handler to the given map with the given name.
 
5666 Handler.addTo = function (map, name) {
 
5667         map.addHandler(name, this);
 
5671 var Mixin = {Events: Events};
 
5678  * A class for making DOM elements draggable (including touch support).
 
5679  * Used internally for map and marker dragging. Only works for elements
 
5680  * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
 
5684  * var draggable = new L.Draggable(elementToDrag);
 
5685  * draggable.enable();
 
5689 var START = touch ? 'touchstart mousedown' : 'mousedown';
 
5691         mousedown: 'mouseup',
 
5692         touchstart: 'touchend',
 
5693         pointerdown: 'touchend',
 
5694         MSPointerDown: 'touchend'
 
5697         mousedown: 'mousemove',
 
5698         touchstart: 'touchmove',
 
5699         pointerdown: 'touchmove',
 
5700         MSPointerDown: 'touchmove'
 
5704 var Draggable = Evented.extend({
 
5708                 // @aka Draggable options
 
5709                 // @option clickTolerance: Number = 3
 
5710                 // The max number of pixels a user can shift the mouse pointer during a click
 
5711                 // for it to be considered a valid click (as opposed to a mouse drag).
 
5715         // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
 
5716         // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
 
5717         initialize: function (element, dragStartTarget, preventOutline$$1, options) {
 
5718                 setOptions(this, options);
 
5720                 this._element = element;
 
5721                 this._dragStartTarget = dragStartTarget || element;
 
5722                 this._preventOutline = preventOutline$$1;
 
5726         // Enables the dragging ability
 
5727         enable: function () {
 
5728                 if (this._enabled) { return; }
 
5730                 on(this._dragStartTarget, START, this._onDown, this);
 
5732                 this._enabled = true;
 
5735         // @method disable()
 
5736         // Disables the dragging ability
 
5737         disable: function () {
 
5738                 if (!this._enabled) { return; }
 
5740                 // If we're currently dragging this draggable,
 
5741                 // disabling it counts as first ending the drag.
 
5742                 if (Draggable._dragging === this) {
 
5746                 off(this._dragStartTarget, START, this._onDown, this);
 
5748                 this._enabled = false;
 
5749                 this._moved = false;
 
5752         _onDown: function (e) {
 
5753                 // Ignore simulated events, since we handle both touch and
 
5754                 // mouse explicitly; otherwise we risk getting duplicates of
 
5755                 // touch events, see #4315.
 
5756                 // Also ignore the event if disabled; this happens in IE11
 
5757                 // under some circumstances, see #3666.
 
5758                 if (e._simulated || !this._enabled) { return; }
 
5760                 this._moved = false;
 
5762                 if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
 
5764                 if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
 
5765                 Draggable._dragging = this;  // Prevent dragging multiple objects at once.
 
5767                 if (this._preventOutline) {
 
5768                         preventOutline(this._element);
 
5772                 disableTextSelection();
 
5774                 if (this._moving) { return; }
 
5776                 // @event down: Event
 
5777                 // Fired when a drag is about to start.
 
5780                 var first = e.touches ? e.touches[0] : e;
 
5782                 this._startPoint = new Point(first.clientX, first.clientY);
 
5784                 on(document, MOVE[e.type], this._onMove, this);
 
5785                 on(document, END[e.type], this._onUp, this);
 
5788         _onMove: function (e) {
 
5789                 // Ignore simulated events, since we handle both touch and
 
5790                 // mouse explicitly; otherwise we risk getting duplicates of
 
5791                 // touch events, see #4315.
 
5792                 // Also ignore the event if disabled; this happens in IE11
 
5793                 // under some circumstances, see #3666.
 
5794                 if (e._simulated || !this._enabled) { return; }
 
5796                 if (e.touches && e.touches.length > 1) {
 
5801                 var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
 
5802                     newPoint = new Point(first.clientX, first.clientY),
 
5803                     offset = newPoint.subtract(this._startPoint);
 
5805                 if (!offset.x && !offset.y) { return; }
 
5806                 if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
 
5811                         // @event dragstart: Event
 
5812                         // Fired when a drag starts
 
5813                         this.fire('dragstart');
 
5816                         this._startPos = getPosition(this._element).subtract(offset);
 
5818                         addClass(document.body, 'leaflet-dragging');
 
5820                         this._lastTarget = e.target || e.srcElement;
 
5821                         // IE and Edge do not give the <use> element, so fetch it
 
5823                         if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
 
5824                                 this._lastTarget = this._lastTarget.correspondingUseElement;
 
5826                         addClass(this._lastTarget, 'leaflet-drag-target');
 
5829                 this._newPos = this._startPos.add(offset);
 
5830                 this._moving = true;
 
5832                 cancelAnimFrame(this._animRequest);
 
5833                 this._lastEvent = e;
 
5834                 this._animRequest = requestAnimFrame(this._updatePosition, this, true);
 
5837         _updatePosition: function () {
 
5838                 var e = {originalEvent: this._lastEvent};
 
5840                 // @event predrag: Event
 
5841                 // Fired continuously during dragging *before* each corresponding
 
5842                 // update of the element's position.
 
5843                 this.fire('predrag', e);
 
5844                 setPosition(this._element, this._newPos);
 
5846                 // @event drag: Event
 
5847                 // Fired continuously during dragging.
 
5848                 this.fire('drag', e);
 
5851         _onUp: function (e) {
 
5852                 // Ignore simulated events, since we handle both touch and
 
5853                 // mouse explicitly; otherwise we risk getting duplicates of
 
5854                 // touch events, see #4315.
 
5855                 // Also ignore the event if disabled; this happens in IE11
 
5856                 // under some circumstances, see #3666.
 
5857                 if (e._simulated || !this._enabled) { return; }
 
5861         finishDrag: function () {
 
5862                 removeClass(document.body, 'leaflet-dragging');
 
5864                 if (this._lastTarget) {
 
5865                         removeClass(this._lastTarget, 'leaflet-drag-target');
 
5866                         this._lastTarget = null;
 
5869                 for (var i in MOVE) {
 
5870                         off(document, MOVE[i], this._onMove, this);
 
5871                         off(document, END[i], this._onUp, this);
 
5875                 enableTextSelection();
 
5877                 if (this._moved && this._moving) {
 
5878                         // ensure drag is not fired after dragend
 
5879                         cancelAnimFrame(this._animRequest);
 
5881                         // @event dragend: DragEndEvent
 
5882                         // Fired when the drag ends.
 
5883                         this.fire('dragend', {
 
5884                                 distance: this._newPos.distanceTo(this._startPos)
 
5888                 this._moving = false;
 
5889                 Draggable._dragging = false;
 
5895  * @namespace LineUtil
 
5897  * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast.
 
5900 // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
 
5901 // Improves rendering performance dramatically by lessening the number of points to draw.
 
5903 // @function simplify(points: Point[], tolerance: Number): Point[]
 
5904 // Dramatically reduces the number of points in a polyline while retaining
 
5905 // its shape and returns a new array of simplified points, using the
 
5906 // [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
 
5907 // Used for a huge performance boost when processing/displaying Leaflet polylines for
 
5908 // each zoom level and also reducing visual noise. tolerance affects the amount of
 
5909 // simplification (lesser value means higher quality but slower and with more points).
 
5910 // Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
 
5911 function simplify(points, tolerance) {
 
5912         if (!tolerance || !points.length) {
 
5913                 return points.slice();
 
5916         var sqTolerance = tolerance * tolerance;
 
5918             // stage 1: vertex reduction
 
5919             points = _reducePoints(points, sqTolerance);
 
5921             // stage 2: Douglas-Peucker simplification
 
5922             points = _simplifyDP(points, sqTolerance);
 
5927 // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
 
5928 // Returns the distance between point `p` and segment `p1` to `p2`.
 
5929 function pointToSegmentDistance(p, p1, p2) {
 
5930         return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
 
5933 // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
 
5934 // Returns the closest point from a point `p` on a segment `p1` to `p2`.
 
5935 function closestPointOnSegment(p, p1, p2) {
 
5936         return _sqClosestPointOnSegment(p, p1, p2);
 
5939 // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
 
5940 function _simplifyDP(points, sqTolerance) {
 
5942         var len = points.length,
 
5943             ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
 
5944             markers = new ArrayConstructor(len);
 
5946             markers[0] = markers[len - 1] = 1;
 
5948         _simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
 
5953         for (i = 0; i < len; i++) {
 
5955                         newPoints.push(points[i]);
 
5962 function _simplifyDPStep(points, markers, sqTolerance, first, last) {
 
5967         for (i = first + 1; i <= last - 1; i++) {
 
5968                 sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
 
5970                 if (sqDist > maxSqDist) {
 
5976         if (maxSqDist > sqTolerance) {
 
5979                 _simplifyDPStep(points, markers, sqTolerance, first, index);
 
5980                 _simplifyDPStep(points, markers, sqTolerance, index, last);
 
5984 // reduce points that are too close to each other to a single point
 
5985 function _reducePoints(points, sqTolerance) {
 
5986         var reducedPoints = [points[0]];
 
5988         for (var i = 1, prev = 0, len = points.length; i < len; i++) {
 
5989                 if (_sqDist(points[i], points[prev]) > sqTolerance) {
 
5990                         reducedPoints.push(points[i]);
 
5994         if (prev < len - 1) {
 
5995                 reducedPoints.push(points[len - 1]);
 
5997         return reducedPoints;
 
6002 // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
 
6003 // Clips the segment a to b by rectangular bounds with the
 
6004 // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
 
6005 // (modifying the segment points directly!). Used by Leaflet to only show polyline
 
6006 // points that are on the screen or near, increasing performance.
 
6007 function clipSegment(a, b, bounds, useLastCode, round) {
 
6008         var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
 
6009             codeB = _getBitCode(b, bounds),
 
6011             codeOut, p, newCode;
 
6013             // save 2nd code to avoid calculating it on the next segment
 
6017                 // if a,b is inside the clip window (trivial accept)
 
6018                 if (!(codeA | codeB)) {
 
6022                 // if a,b is outside the clip window (trivial reject)
 
6023                 if (codeA & codeB) {
 
6028                 codeOut = codeA || codeB;
 
6029                 p = _getEdgeIntersection(a, b, codeOut, bounds, round);
 
6030                 newCode = _getBitCode(p, bounds);
 
6032                 if (codeOut === codeA) {
 
6042 function _getEdgeIntersection(a, b, code, bounds, round) {
 
6049         if (code & 8) { // top
 
6050                 x = a.x + dx * (max.y - a.y) / dy;
 
6053         } else if (code & 4) { // bottom
 
6054                 x = a.x + dx * (min.y - a.y) / dy;
 
6057         } else if (code & 2) { // right
 
6059                 y = a.y + dy * (max.x - a.x) / dx;
 
6061         } else if (code & 1) { // left
 
6063                 y = a.y + dy * (min.x - a.x) / dx;
 
6066         return new Point(x, y, round);
 
6069 function _getBitCode(p, bounds) {
 
6072         if (p.x < bounds.min.x) { // left
 
6074         } else if (p.x > bounds.max.x) { // right
 
6078         if (p.y < bounds.min.y) { // bottom
 
6080         } else if (p.y > bounds.max.y) { // top
 
6087 // square distance (to avoid unnecessary Math.sqrt calls)
 
6088 function _sqDist(p1, p2) {
 
6089         var dx = p2.x - p1.x,
 
6091         return dx * dx + dy * dy;
 
6094 // return closest point on segment or distance to that point
 
6095 function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
 
6100             dot = dx * dx + dy * dy,
 
6104                 t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
 
6118         return sqDist ? dx * dx + dy * dy : new Point(x, y);
 
6122 // @function isFlat(latlngs: LatLng[]): Boolean
 
6123 // Returns true if `latlngs` is a flat array, false is nested.
 
6124 function isFlat(latlngs) {
 
6125         return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
 
6128 function _flat(latlngs) {
 
6129         console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
 
6130         return isFlat(latlngs);
 
6134 var LineUtil = (Object.freeze || Object)({
 
6136         pointToSegmentDistance: pointToSegmentDistance,
 
6137         closestPointOnSegment: closestPointOnSegment,
 
6138         clipSegment: clipSegment,
 
6139         _getEdgeIntersection: _getEdgeIntersection,
 
6140         _getBitCode: _getBitCode,
 
6141         _sqClosestPointOnSegment: _sqClosestPointOnSegment,
 
6147  * @namespace PolyUtil
 
6148  * Various utility functions for polygon geometries.
 
6151 /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
 
6152  * 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)).
 
6153  * Used by Leaflet to only show polygon points that are on the screen or near, increasing
 
6154  * performance. Note that polygon points needs different algorithm for clipping
 
6155  * than polyline, so there's a separate method for it.
 
6157 function clipPolygon(points, bounds, round) {
 
6159             edges = [1, 4, 2, 8],
 
6164         for (i = 0, len = points.length; i < len; i++) {
 
6165                 points[i]._code = _getBitCode(points[i], bounds);
 
6168         // for each edge (left, bottom, right, top)
 
6169         for (k = 0; k < 4; k++) {
 
6173                 for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
 
6177                         // if a is inside the clip window
 
6178                         if (!(a._code & edge)) {
 
6179                                 // if b is outside the clip window (a->b goes out of screen)
 
6180                                 if (b._code & edge) {
 
6181                                         p = _getEdgeIntersection(b, a, edge, bounds, round);
 
6182                                         p._code = _getBitCode(p, bounds);
 
6183                                         clippedPoints.push(p);
 
6185                                 clippedPoints.push(a);
 
6187                         // else if b is inside the clip window (a->b enters the screen)
 
6188                         } else if (!(b._code & edge)) {
 
6189                                 p = _getEdgeIntersection(b, a, edge, bounds, round);
 
6190                                 p._code = _getBitCode(p, bounds);
 
6191                                 clippedPoints.push(p);
 
6194                 points = clippedPoints;
 
6201 var PolyUtil = (Object.freeze || Object)({
 
6202         clipPolygon: clipPolygon
 
6206  * @namespace Projection
 
6208  * Leaflet comes with a set of already defined Projections out of the box:
 
6210  * @projection L.Projection.LonLat
 
6212  * Equirectangular, or Plate Carree projection — the most simple projection,
 
6213  * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
 
6214  * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
 
6215  * `EPSG:4326` and `Simple` CRS.
 
6219         project: function (latlng) {
 
6220                 return new Point(latlng.lng, latlng.lat);
 
6223         unproject: function (point) {
 
6224                 return new LatLng(point.y, point.x);
 
6227         bounds: new Bounds([-180, -90], [180, 90])
 
6231  * @namespace Projection
 
6232  * @projection L.Projection.Mercator
 
6234  * 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.
 
6239         R_MINOR: 6356752.314245179,
 
6241         bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
 
6243         project: function (latlng) {
 
6244                 var d = Math.PI / 180,
 
6247                     tmp = this.R_MINOR / r,
 
6248                     e = Math.sqrt(1 - tmp * tmp),
 
6249                     con = e * Math.sin(y);
 
6251                 var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
 
6252                 y = -r * Math.log(Math.max(ts, 1E-10));
 
6254                 return new Point(latlng.lng * d * r, y);
 
6257         unproject: function (point) {
 
6258                 var d = 180 / Math.PI,
 
6260                     tmp = this.R_MINOR / r,
 
6261                     e = Math.sqrt(1 - tmp * tmp),
 
6262                     ts = Math.exp(-point.y / r),
 
6263                     phi = Math.PI / 2 - 2 * Math.atan(ts);
 
6265                 for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
 
6266                         con = e * Math.sin(phi);
 
6267                         con = Math.pow((1 - con) / (1 + con), e / 2);
 
6268                         dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
 
6272                 return new LatLng(phi * d, point.x * d / r);
 
6279  * An object with methods for projecting geographical coordinates of the world onto
 
6280  * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection).
 
6282  * @property bounds: Bounds
 
6283  * The bounds (specified in CRS units) where the projection is valid
 
6285  * @method project(latlng: LatLng): Point
 
6286  * Projects geographical coordinates into a 2D point.
 
6287  * Only accepts actual `L.LatLng` instances, not arrays.
 
6289  * @method unproject(point: Point): LatLng
 
6290  * The inverse of `project`. Projects a 2D point into a geographical location.
 
6291  * Only accepts actual `L.Point` instances, not arrays.
 
6293  * Note that the projection instances do not inherit from Leafet's `Class` object,
 
6294  * and can't be instantiated. Also, new classes can't inherit from them,
 
6295  * and methods can't be added to them with the `include` function.
 
6302 var index = (Object.freeze || Object)({
 
6305         SphericalMercator: SphericalMercator
 
6310  * @crs L.CRS.EPSG3395
 
6312  * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
 
6314 var EPSG3395 = extend({}, Earth, {
 
6316         projection: Mercator,
 
6318         transformation: (function () {
 
6319                 var scale = 0.5 / (Math.PI * Mercator.R);
 
6320                 return toTransformation(scale, 0.5, -scale, 0.5);
 
6326  * @crs L.CRS.EPSG4326
 
6328  * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
 
6330  * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
 
6331  * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
 
6332  * with this CRS, ensure that there are two 256x256 pixel tiles covering the
 
6333  * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
 
6334  * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
 
6337 var EPSG4326 = extend({}, Earth, {
 
6340         transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
 
6347  * A simple CRS that maps longitude and latitude into `x` and `y` directly.
 
6348  * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
 
6349  * axis should still be inverted (going from bottom to top). `distance()` returns
 
6350  * simple euclidean distance.
 
6353 var Simple = extend({}, CRS, {
 
6355         transformation: toTransformation(1, 0, -1, 0),
 
6357         scale: function (zoom) {
 
6358                 return Math.pow(2, zoom);
 
6361         zoom: function (scale) {
 
6362                 return Math.log(scale) / Math.LN2;
 
6365         distance: function (latlng1, latlng2) {
 
6366                 var dx = latlng2.lng - latlng1.lng,
 
6367                     dy = latlng2.lat - latlng1.lat;
 
6369                 return Math.sqrt(dx * dx + dy * dy);
 
6376 CRS.EPSG3395 = EPSG3395;
 
6377 CRS.EPSG3857 = EPSG3857;
 
6378 CRS.EPSG900913 = EPSG900913;
 
6379 CRS.EPSG4326 = EPSG4326;
 
6380 CRS.Simple = Simple;
 
6388  * A set of methods from the Layer base class that all Leaflet layers use.
 
6389  * Inherits all methods, options and events from `L.Evented`.
 
6394  * var layer = L.Marker(latlng).addTo(map);
 
6400  * Fired after the layer is added to a map
 
6402  * @event remove: Event
 
6403  * Fired after the layer is removed from a map
 
6407 var Layer = Evented.extend({
 
6409         // Classes extending `L.Layer` will inherit the following options:
 
6411                 // @option pane: String = 'overlayPane'
 
6412                 // 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.
 
6413                 pane: 'overlayPane',
 
6415                 // @option attribution: String = null
 
6416                 // String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox".
 
6419                 bubblingMouseEvents: true
 
6423          * Classes extending `L.Layer` will inherit the following methods:
 
6425          * @method addTo(map: Map|LayerGroup): this
 
6426          * Adds the layer to the given map or layer group.
 
6428         addTo: function (map) {
 
6433         // @method remove: this
 
6434         // Removes the layer from the map it is currently active on.
 
6435         remove: function () {
 
6436                 return this.removeFrom(this._map || this._mapToAdd);
 
6439         // @method removeFrom(map: Map): this
 
6440         // Removes the layer from the given map
 
6441         removeFrom: function (obj) {
 
6443                         obj.removeLayer(this);
 
6448         // @method getPane(name? : String): HTMLElement
 
6449         // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
 
6450         getPane: function (name) {
 
6451                 return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
 
6454         addInteractiveTarget: function (targetEl) {
 
6455                 this._map._targets[stamp(targetEl)] = this;
 
6459         removeInteractiveTarget: function (targetEl) {
 
6460                 delete this._map._targets[stamp(targetEl)];
 
6464         // @method getAttribution: String
 
6465         // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
 
6466         getAttribution: function () {
 
6467                 return this.options.attribution;
 
6470         _layerAdd: function (e) {
 
6473                 // check in case layer gets added and then removed before the map is ready
 
6474                 if (!map.hasLayer(this)) { return; }
 
6477                 this._zoomAnimated = map._zoomAnimated;
 
6479                 if (this.getEvents) {
 
6480                         var events = this.getEvents();
 
6481                         map.on(events, this);
 
6482                         this.once('remove', function () {
 
6483                                 map.off(events, this);
 
6489                 if (this.getAttribution && map.attributionControl) {
 
6490                         map.attributionControl.addAttribution(this.getAttribution());
 
6494                 map.fire('layeradd', {layer: this});
 
6498 /* @section Extension methods
 
6501  * Every layer should extend from `L.Layer` and (re-)implement the following methods.
 
6503  * @method onAdd(map: Map): this
 
6504  * 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).
 
6506  * @method onRemove(map: Map): this
 
6507  * 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).
 
6509  * @method getEvents(): Object
 
6510  * 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.
 
6512  * @method getAttribution(): String
 
6513  * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
 
6515  * @method beforeAdd(map: Map): this
 
6516  * 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.
 
6521  * @section Layer events
 
6523  * @event layeradd: LayerEvent
 
6524  * Fired when a new layer is added to the map.
 
6526  * @event layerremove: LayerEvent
 
6527  * Fired when some layer is removed from the map
 
6529  * @section Methods for Layers and Controls
 
6532         // @method addLayer(layer: Layer): this
 
6533         // Adds the given layer to the map
 
6534         addLayer: function (layer) {
 
6535                 if (!layer._layerAdd) {
 
6536                         throw new Error('The provided object is not a Layer.');
 
6539                 var id = stamp(layer);
 
6540                 if (this._layers[id]) { return this; }
 
6541                 this._layers[id] = layer;
 
6543                 layer._mapToAdd = this;
 
6545                 if (layer.beforeAdd) {
 
6546                         layer.beforeAdd(this);
 
6549                 this.whenReady(layer._layerAdd, layer);
 
6554         // @method removeLayer(layer: Layer): this
 
6555         // Removes the given layer from the map.
 
6556         removeLayer: function (layer) {
 
6557                 var id = stamp(layer);
 
6559                 if (!this._layers[id]) { return this; }
 
6562                         layer.onRemove(this);
 
6565                 if (layer.getAttribution && this.attributionControl) {
 
6566                         this.attributionControl.removeAttribution(layer.getAttribution());
 
6569                 delete this._layers[id];
 
6572                         this.fire('layerremove', {layer: layer});
 
6573                         layer.fire('remove');
 
6576                 layer._map = layer._mapToAdd = null;
 
6581         // @method hasLayer(layer: Layer): Boolean
 
6582         // Returns `true` if the given layer is currently added to the map
 
6583         hasLayer: function (layer) {
 
6584                 return !!layer && (stamp(layer) in this._layers);
 
6587         /* @method eachLayer(fn: Function, context?: Object): this
 
6588          * Iterates over the layers of the map, optionally specifying context of the iterator function.
 
6590          * map.eachLayer(function(layer){
 
6591          *     layer.bindPopup('Hello');
 
6595         eachLayer: function (method, context) {
 
6596                 for (var i in this._layers) {
 
6597                         method.call(context, this._layers[i]);
 
6602         _addLayers: function (layers) {
 
6603                 layers = layers ? (isArray(layers) ? layers : [layers]) : [];
 
6605                 for (var i = 0, len = layers.length; i < len; i++) {
 
6606                         this.addLayer(layers[i]);
 
6610         _addZoomLimit: function (layer) {
 
6611                 if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
 
6612                         this._zoomBoundLayers[stamp(layer)] = layer;
 
6613                         this._updateZoomLevels();
 
6617         _removeZoomLimit: function (layer) {
 
6618                 var id = stamp(layer);
 
6620                 if (this._zoomBoundLayers[id]) {
 
6621                         delete this._zoomBoundLayers[id];
 
6622                         this._updateZoomLevels();
 
6626         _updateZoomLevels: function () {
 
6627                 var minZoom = Infinity,
 
6628                     maxZoom = -Infinity,
 
6629                     oldZoomSpan = this._getZoomSpan();
 
6631                 for (var i in this._zoomBoundLayers) {
 
6632                         var options = this._zoomBoundLayers[i].options;
 
6634                         minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
 
6635                         maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
 
6638                 this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
 
6639                 this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
 
6641                 // @section Map state change events
 
6642                 // @event zoomlevelschange: Event
 
6643                 // Fired when the number of zoomlevels on the map is changed due
 
6644                 // to adding or removing a layer.
 
6645                 if (oldZoomSpan !== this._getZoomSpan()) {
 
6646                         this.fire('zoomlevelschange');
 
6649                 if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
 
6650                         this.setZoom(this._layersMaxZoom);
 
6652                 if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
 
6653                         this.setZoom(this._layersMinZoom);
 
6663  * Used to group several layers and handle them as one. If you add it to the map,
 
6664  * any layers added or removed from the group will be added/removed on the map as
 
6665  * well. Extends `Layer`.
 
6670  * L.layerGroup([marker1, marker2])
 
6671  *      .addLayer(polyline)
 
6676 var LayerGroup = Layer.extend({
 
6678         initialize: function (layers, options) {
 
6679                 setOptions(this, options);
 
6686                         for (i = 0, len = layers.length; i < len; i++) {
 
6687                                 this.addLayer(layers[i]);
 
6692         // @method addLayer(layer: Layer): this
 
6693         // Adds the given layer to the group.
 
6694         addLayer: function (layer) {
 
6695                 var id = this.getLayerId(layer);
 
6697                 this._layers[id] = layer;
 
6700                         this._map.addLayer(layer);
 
6706         // @method removeLayer(layer: Layer): this
 
6707         // Removes the given layer from the group.
 
6709         // @method removeLayer(id: Number): this
 
6710         // Removes the layer with the given internal ID from the group.
 
6711         removeLayer: function (layer) {
 
6712                 var id = layer in this._layers ? layer : this.getLayerId(layer);
 
6714                 if (this._map && this._layers[id]) {
 
6715                         this._map.removeLayer(this._layers[id]);
 
6718                 delete this._layers[id];
 
6723         // @method hasLayer(layer: Layer): Boolean
 
6724         // Returns `true` if the given layer is currently added to the group.
 
6726         // @method hasLayer(id: Number): Boolean
 
6727         // Returns `true` if the given internal ID is currently added to the group.
 
6728         hasLayer: function (layer) {
 
6729                 return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
 
6732         // @method clearLayers(): this
 
6733         // Removes all the layers from the group.
 
6734         clearLayers: function () {
 
6735                 return this.eachLayer(this.removeLayer, this);
 
6738         // @method invoke(methodName: String, …): this
 
6739         // Calls `methodName` on every layer contained in this group, passing any
 
6740         // additional parameters. Has no effect if the layers contained do not
 
6741         // implement `methodName`.
 
6742         invoke: function (methodName) {
 
6743                 var args = Array.prototype.slice.call(arguments, 1),
 
6746                 for (i in this._layers) {
 
6747                         layer = this._layers[i];
 
6749                         if (layer[methodName]) {
 
6750                                 layer[methodName].apply(layer, args);
 
6757         onAdd: function (map) {
 
6758                 this.eachLayer(map.addLayer, map);
 
6761         onRemove: function (map) {
 
6762                 this.eachLayer(map.removeLayer, map);
 
6765         // @method eachLayer(fn: Function, context?: Object): this
 
6766         // Iterates over the layers of the group, optionally specifying context of the iterator function.
 
6768         // group.eachLayer(function (layer) {
 
6769         //      layer.bindPopup('Hello');
 
6772         eachLayer: function (method, context) {
 
6773                 for (var i in this._layers) {
 
6774                         method.call(context, this._layers[i]);
 
6779         // @method getLayer(id: Number): Layer
 
6780         // Returns the layer with the given internal ID.
 
6781         getLayer: function (id) {
 
6782                 return this._layers[id];
 
6785         // @method getLayers(): Layer[]
 
6786         // Returns an array of all the layers added to the group.
 
6787         getLayers: function () {
 
6789                 this.eachLayer(layers.push, layers);
 
6793         // @method setZIndex(zIndex: Number): this
 
6794         // Calls `setZIndex` on every layer contained in this group, passing the z-index.
 
6795         setZIndex: function (zIndex) {
 
6796                 return this.invoke('setZIndex', zIndex);
 
6799         // @method getLayerId(layer: Layer): Number
 
6800         // Returns the internal ID for a layer
 
6801         getLayerId: function (layer) {
 
6802                 return stamp(layer);
 
6807 // @factory L.layerGroup(layers?: Layer[], options?: Object)
 
6808 // Create a layer group, optionally given an initial set of layers and an `options` object.
 
6809 var layerGroup = function (layers, options) {
 
6810         return new LayerGroup(layers, options);
 
6814  * @class FeatureGroup
 
6815  * @aka L.FeatureGroup
 
6816  * @inherits LayerGroup
 
6818  * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
 
6819  *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
 
6820  *  * Events are propagated to the `FeatureGroup`, so if the group has an event
 
6821  * handler, it will handle events from any of the layers. This includes mouse events
 
6822  * and custom events.
 
6823  *  * Has `layeradd` and `layerremove` events
 
6828  * L.featureGroup([marker1, marker2, polyline])
 
6829  *      .bindPopup('Hello world!')
 
6830  *      .on('click', function() { alert('Clicked on a member of the group!'); })
 
6835 var FeatureGroup = LayerGroup.extend({
 
6837         addLayer: function (layer) {
 
6838                 if (this.hasLayer(layer)) {
 
6842                 layer.addEventParent(this);
 
6844                 LayerGroup.prototype.addLayer.call(this, layer);
 
6846                 // @event layeradd: LayerEvent
 
6847                 // Fired when a layer is added to this `FeatureGroup`
 
6848                 return this.fire('layeradd', {layer: layer});
 
6851         removeLayer: function (layer) {
 
6852                 if (!this.hasLayer(layer)) {
 
6855                 if (layer in this._layers) {
 
6856                         layer = this._layers[layer];
 
6859                 layer.removeEventParent(this);
 
6861                 LayerGroup.prototype.removeLayer.call(this, layer);
 
6863                 // @event layerremove: LayerEvent
 
6864                 // Fired when a layer is removed from this `FeatureGroup`
 
6865                 return this.fire('layerremove', {layer: layer});
 
6868         // @method setStyle(style: Path options): this
 
6869         // Sets the given path options to each layer of the group that has a `setStyle` method.
 
6870         setStyle: function (style) {
 
6871                 return this.invoke('setStyle', style);
 
6874         // @method bringToFront(): this
 
6875         // Brings the layer group to the top of all other layers
 
6876         bringToFront: function () {
 
6877                 return this.invoke('bringToFront');
 
6880         // @method bringToBack(): this
 
6881         // Brings the layer group to the back of all other layers
 
6882         bringToBack: function () {
 
6883                 return this.invoke('bringToBack');
 
6886         // @method getBounds(): LatLngBounds
 
6887         // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
 
6888         getBounds: function () {
 
6889                 var bounds = new LatLngBounds();
 
6891                 for (var id in this._layers) {
 
6892                         var layer = this._layers[id];
 
6893                         bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
 
6899 // @factory L.featureGroup(layers: Layer[])
 
6900 // Create a feature group, optionally given an initial set of layers.
 
6901 var featureGroup = function (layers) {
 
6902         return new FeatureGroup(layers);
 
6909  * Represents an icon to provide when creating a marker.
 
6914  * var myIcon = L.icon({
 
6915  *     iconUrl: 'my-icon.png',
 
6916  *     iconRetinaUrl: 'my-icon@2x.png',
 
6917  *     iconSize: [38, 95],
 
6918  *     iconAnchor: [22, 94],
 
6919  *     popupAnchor: [-3, -76],
 
6920  *     shadowUrl: 'my-icon-shadow.png',
 
6921  *     shadowRetinaUrl: 'my-icon-shadow@2x.png',
 
6922  *     shadowSize: [68, 95],
 
6923  *     shadowAnchor: [22, 94]
 
6926  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
 
6929  * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
 
6933 var Icon = Class.extend({
 
6938          * @option iconUrl: String = null
 
6939          * **(required)** The URL to the icon image (absolute or relative to your script path).
 
6941          * @option iconRetinaUrl: String = null
 
6942          * The URL to a retina sized version of the icon image (absolute or relative to your
 
6943          * script path). Used for Retina screen devices.
 
6945          * @option iconSize: Point = null
 
6946          * Size of the icon image in pixels.
 
6948          * @option iconAnchor: Point = null
 
6949          * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
 
6950          * will be aligned so that this point is at the marker's geographical location. Centered
 
6951          * by default if size is specified, also can be set in CSS with negative margins.
 
6953          * @option popupAnchor: Point = [0, 0]
 
6954          * The coordinates of the point from which popups will "open", relative to the icon anchor.
 
6956          * @option tooltipAnchor: Point = [0, 0]
 
6957          * The coordinates of the point from which tooltips will "open", relative to the icon anchor.
 
6959          * @option shadowUrl: String = null
 
6960          * The URL to the icon shadow image. If not specified, no shadow image will be created.
 
6962          * @option shadowRetinaUrl: String = null
 
6964          * @option shadowSize: Point = null
 
6965          * Size of the shadow image in pixels.
 
6967          * @option shadowAnchor: Point = null
 
6968          * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
 
6969          * as iconAnchor if not specified).
 
6971          * @option className: String = ''
 
6972          * A custom class name to assign to both icon and shadow images. Empty by default.
 
6976                 popupAnchor: [0, 0],
 
6977                 tooltipAnchor: [0, 0],
 
6980         initialize: function (options) {
 
6981                 setOptions(this, options);
 
6984         // @method createIcon(oldIcon?: HTMLElement): HTMLElement
 
6985         // Called internally when the icon has to be shown, returns a `<img>` HTML element
 
6986         // styled according to the options.
 
6987         createIcon: function (oldIcon) {
 
6988                 return this._createIcon('icon', oldIcon);
 
6991         // @method createShadow(oldIcon?: HTMLElement): HTMLElement
 
6992         // As `createIcon`, but for the shadow beneath it.
 
6993         createShadow: function (oldIcon) {
 
6994                 return this._createIcon('shadow', oldIcon);
 
6997         _createIcon: function (name, oldIcon) {
 
6998                 var src = this._getIconUrl(name);
 
7001                         if (name === 'icon') {
 
7002                                 throw new Error('iconUrl not set in Icon options (see the docs).');
 
7007                 var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
 
7008                 this._setIconStyles(img, name);
 
7013         _setIconStyles: function (img, name) {
 
7014                 var options = this.options;
 
7015                 var sizeOption = options[name + 'Size'];
 
7017                 if (typeof sizeOption === 'number') {
 
7018                         sizeOption = [sizeOption, sizeOption];
 
7021                 var size = toPoint(sizeOption),
 
7022                     anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
 
7023                             size && size.divideBy(2, true));
 
7025                 img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
 
7028                         img.style.marginLeft = (-anchor.x) + 'px';
 
7029                         img.style.marginTop  = (-anchor.y) + 'px';
 
7033                         img.style.width  = size.x + 'px';
 
7034                         img.style.height = size.y + 'px';
 
7038         _createImg: function (src, el) {
 
7039                 el = el || document.createElement('img');
 
7044         _getIconUrl: function (name) {
 
7045                 return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
 
7050 // @factory L.icon(options: Icon options)
 
7051 // Creates an icon instance with the given options.
 
7052 function icon(options) {
 
7053         return new Icon(options);
 
7057  * @miniclass Icon.Default (Icon)
 
7058  * @aka L.Icon.Default
 
7061  * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
 
7062  * no icon is specified. Points to the blue marker image distributed with Leaflet
 
7065  * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
 
7066  * (which is a set of `Icon options`).
 
7068  * If you want to _completely_ replace the default icon, override the
 
7069  * `L.Marker.prototype.options.icon` with your own icon instead.
 
7072 var IconDefault = Icon.extend({
 
7075                 iconUrl:       'marker-icon.png',
 
7076                 iconRetinaUrl: 'marker-icon-2x.png',
 
7077                 shadowUrl:     'marker-shadow.png',
 
7079                 iconAnchor:  [12, 41],
 
7080                 popupAnchor: [1, -34],
 
7081                 tooltipAnchor: [16, -28],
 
7082                 shadowSize:  [41, 41]
 
7085         _getIconUrl: function (name) {
 
7086                 if (!IconDefault.imagePath) {   // Deprecated, backwards-compatibility only
 
7087                         IconDefault.imagePath = this._detectIconPath();
 
7090                 // @option imagePath: String
 
7091                 // `Icon.Default` will try to auto-detect the location of the
 
7092                 // blue icon images. If you are placing these images in a non-standard
 
7093                 // way, set this option to point to the right path.
 
7094                 return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
 
7097         _detectIconPath: function () {
 
7098                 var el = create$1('div',  'leaflet-default-icon-path', document.body);
 
7099                 var path = getStyle(el, 'background-image') ||
 
7100                            getStyle(el, 'backgroundImage');     // IE8
 
7102                 document.body.removeChild(el);
 
7104                 if (path === null || path.indexOf('url') !== 0) {
 
7107                         path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, '');
 
7115  * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
 
7119 /* @namespace Marker
 
7120  * @section Interaction handlers
 
7122  * 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:
 
7125  * marker.dragging.disable();
 
7128  * @property dragging: Handler
 
7129  * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
 
7132 var MarkerDrag = Handler.extend({
 
7133         initialize: function (marker) {
 
7134                 this._marker = marker;
 
7137         addHooks: function () {
 
7138                 var icon = this._marker._icon;
 
7140                 if (!this._draggable) {
 
7141                         this._draggable = new Draggable(icon, icon, true);
 
7144                 this._draggable.on({
 
7145                         dragstart: this._onDragStart,
 
7146                         predrag: this._onPreDrag,
 
7148                         dragend: this._onDragEnd
 
7151                 addClass(icon, 'leaflet-marker-draggable');
 
7154         removeHooks: function () {
 
7155                 this._draggable.off({
 
7156                         dragstart: this._onDragStart,
 
7157                         predrag: this._onPreDrag,
 
7159                         dragend: this._onDragEnd
 
7162                 if (this._marker._icon) {
 
7163                         removeClass(this._marker._icon, 'leaflet-marker-draggable');
 
7167         moved: function () {
 
7168                 return this._draggable && this._draggable._moved;
 
7171         _adjustPan: function (e) {
 
7172                 var marker = this._marker,
 
7174                     speed = this._marker.options.autoPanSpeed,
 
7175                     padding = this._marker.options.autoPanPadding,
 
7176                     iconPos = L.DomUtil.getPosition(marker._icon),
 
7177                     bounds = map.getPixelBounds(),
 
7178                     origin = map.getPixelOrigin();
 
7180                 var panBounds = toBounds(
 
7181                         bounds.min._subtract(origin).add(padding),
 
7182                         bounds.max._subtract(origin).subtract(padding)
 
7185                 if (!panBounds.contains(iconPos)) {
 
7186                         // Compute incremental movement
 
7187                         var movement = toPoint(
 
7188                                 (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) -
 
7189                                 (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x),
 
7191                                 (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) -
 
7192                                 (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y)
 
7193                         ).multiplyBy(speed);
 
7195                         map.panBy(movement, {animate: false});
 
7197                         this._draggable._newPos._add(movement);
 
7198                         this._draggable._startPos._add(movement);
 
7200                         L.DomUtil.setPosition(marker._icon, this._draggable._newPos);
 
7203                         this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
 
7207         _onDragStart: function () {
 
7208                 // @section Dragging events
 
7209                 // @event dragstart: Event
 
7210                 // Fired when the user starts dragging the marker.
 
7212                 // @event movestart: Event
 
7213                 // Fired when the marker starts moving (because of dragging).
 
7215                 this._oldLatLng = this._marker.getLatLng();
 
7222         _onPreDrag: function (e) {
 
7223                 if (this._marker.options.autoPan) {
 
7224                         cancelAnimFrame(this._panRequest);
 
7225                         this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
 
7229         _onDrag: function (e) {
 
7230                 var marker = this._marker,
 
7231                     shadow = marker._shadow,
 
7232                 iconPos = getPosition(marker._icon),
 
7233                     latlng = marker._map.layerPointToLatLng(iconPos);
 
7235                 // update shadow position
 
7237                         setPosition(shadow, iconPos);
 
7240                 marker._latlng = latlng;
 
7242                 e.oldLatLng = this._oldLatLng;
 
7244                 // @event drag: Event
 
7245                 // Fired repeatedly while the user drags the marker.
 
7251         _onDragEnd: function (e) {
 
7252                 // @event dragend: DragEndEvent
 
7253                 // Fired when the user stops dragging the marker.
 
7255                  cancelAnimFrame(this._panRequest);
 
7257                 // @event moveend: Event
 
7258                 // Fired when the marker stops moving (because of dragging).
 
7259                 delete this._oldLatLng;
 
7262                     .fire('dragend', e);
 
7268  * @inherits Interactive layer
 
7270  * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
 
7275  * L.marker([50.5, 30.5]).addTo(map);
 
7279 var Marker = Layer.extend({
 
7282         // @aka Marker options
 
7284                 // @option icon: Icon = *
 
7285                 // Icon instance to use for rendering the marker.
 
7286                 // See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
 
7287                 // If not specified, a common instance of `L.Icon.Default` is used.
 
7288                 icon: new IconDefault(),
 
7290                 // Option inherited from "Interactive layer" abstract class
 
7293                 // @option draggable: Boolean = false
 
7294                 // Whether the marker is draggable with mouse/touch or not.
 
7297                 // @option autoPan: Boolean = false
 
7298                 // Set it to `true` if you want the map to do panning animation when marker hits the edges.
 
7301                 // @option autoPanPadding: Point = Point(50, 50)
 
7302                 // Equivalent of setting both top left and bottom right autopan padding to the same value.
 
7303                 autoPanPadding: [50, 50],
 
7305                 // @option autoPanSpeed: Number = 10
 
7306                 // Number of pixels the map should move by.
 
7309                 // @option keyboard: Boolean = true
 
7310                 // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
 
7313                 // @option title: String = ''
 
7314                 // Text for the browser tooltip that appear on marker hover (no tooltip by default).
 
7317                 // @option alt: String = ''
 
7318                 // Text for the `alt` attribute of the icon image (useful for accessibility).
 
7321                 // @option zIndexOffset: Number = 0
 
7322                 // 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).
 
7325                 // @option opacity: Number = 1.0
 
7326                 // The opacity of the marker.
 
7329                 // @option riseOnHover: Boolean = false
 
7330                 // If `true`, the marker will get on top of others when you hover the mouse over it.
 
7333                 // @option riseOffset: Number = 250
 
7334                 // The z-index offset used for the `riseOnHover` feature.
 
7337                 // @option pane: String = 'markerPane'
 
7338                 // `Map pane` where the markers icon will be added.
 
7341                 // @option bubblingMouseEvents: Boolean = false
 
7342                 // When `true`, a mouse event on this marker will trigger the same event on the map
 
7343                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
 
7344                 bubblingMouseEvents: false
 
7349          * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
 
7352         initialize: function (latlng, options) {
 
7353                 setOptions(this, options);
 
7354                 this._latlng = toLatLng(latlng);
 
7357         onAdd: function (map) {
 
7358                 this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
 
7360                 if (this._zoomAnimated) {
 
7361                         map.on('zoomanim', this._animateZoom, this);
 
7368         onRemove: function (map) {
 
7369                 if (this.dragging && this.dragging.enabled()) {
 
7370                         this.options.draggable = true;
 
7371                         this.dragging.removeHooks();
 
7373                 delete this.dragging;
 
7375                 if (this._zoomAnimated) {
 
7376                         map.off('zoomanim', this._animateZoom, this);
 
7380                 this._removeShadow();
 
7383         getEvents: function () {
 
7386                         viewreset: this.update
 
7390         // @method getLatLng: LatLng
 
7391         // Returns the current geographical position of the marker.
 
7392         getLatLng: function () {
 
7393                 return this._latlng;
 
7396         // @method setLatLng(latlng: LatLng): this
 
7397         // Changes the marker position to the given point.
 
7398         setLatLng: function (latlng) {
 
7399                 var oldLatLng = this._latlng;
 
7400                 this._latlng = toLatLng(latlng);
 
7403                 // @event move: Event
 
7404                 // 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`.
 
7405                 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
 
7408         // @method setZIndexOffset(offset: Number): this
 
7409         // Changes the [zIndex offset](#marker-zindexoffset) of the marker.
 
7410         setZIndexOffset: function (offset) {
 
7411                 this.options.zIndexOffset = offset;
 
7412                 return this.update();
 
7415         // @method setIcon(icon: Icon): this
 
7416         // Changes the marker icon.
 
7417         setIcon: function (icon) {
 
7419                 this.options.icon = icon;
 
7427                         this.bindPopup(this._popup, this._popup.options);
 
7433         getElement: function () {
 
7437         update: function () {
 
7439                 if (this._icon && this._map) {
 
7440                         var pos = this._map.latLngToLayerPoint(this._latlng).round();
 
7447         _initIcon: function () {
 
7448                 var options = this.options,
 
7449                     classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
 
7451                 var icon = options.icon.createIcon(this._icon),
 
7454                 // if we're not reusing the icon, remove the old one and init new one
 
7455                 if (icon !== this._icon) {
 
7461                         if (options.title) {
 
7462                                 icon.title = options.title;
 
7465                         if (icon.tagName === 'IMG') {
 
7466                                 icon.alt = options.alt || '';
 
7470                 addClass(icon, classToAdd);
 
7472                 if (options.keyboard) {
 
7473                         icon.tabIndex = '0';
 
7478                 if (options.riseOnHover) {
 
7480                                 mouseover: this._bringToFront,
 
7481                                 mouseout: this._resetZIndex
 
7485                 var newShadow = options.icon.createShadow(this._shadow),
 
7488                 if (newShadow !== this._shadow) {
 
7489                         this._removeShadow();
 
7494                         addClass(newShadow, classToAdd);
 
7497                 this._shadow = newShadow;
 
7500                 if (options.opacity < 1) {
 
7501                         this._updateOpacity();
 
7506                         this.getPane().appendChild(this._icon);
 
7508                 this._initInteraction();
 
7509                 if (newShadow && addShadow) {
 
7510                         this.getPane('shadowPane').appendChild(this._shadow);
 
7514         _removeIcon: function () {
 
7515                 if (this.options.riseOnHover) {
 
7517                                 mouseover: this._bringToFront,
 
7518                                 mouseout: this._resetZIndex
 
7523                 this.removeInteractiveTarget(this._icon);
 
7528         _removeShadow: function () {
 
7530                         remove(this._shadow);
 
7532                 this._shadow = null;
 
7535         _setPos: function (pos) {
 
7536                 setPosition(this._icon, pos);
 
7539                         setPosition(this._shadow, pos);
 
7542                 this._zIndex = pos.y + this.options.zIndexOffset;
 
7544                 this._resetZIndex();
 
7547         _updateZIndex: function (offset) {
 
7548                 this._icon.style.zIndex = this._zIndex + offset;
 
7551         _animateZoom: function (opt) {
 
7552                 var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
 
7557         _initInteraction: function () {
 
7559                 if (!this.options.interactive) { return; }
 
7561                 addClass(this._icon, 'leaflet-interactive');
 
7563                 this.addInteractiveTarget(this._icon);
 
7566                         var draggable = this.options.draggable;
 
7567                         if (this.dragging) {
 
7568                                 draggable = this.dragging.enabled();
 
7569                                 this.dragging.disable();
 
7572                         this.dragging = new MarkerDrag(this);
 
7575                                 this.dragging.enable();
 
7580         // @method setOpacity(opacity: Number): this
 
7581         // Changes the opacity of the marker.
 
7582         setOpacity: function (opacity) {
 
7583                 this.options.opacity = opacity;
 
7585                         this._updateOpacity();
 
7591         _updateOpacity: function () {
 
7592                 var opacity = this.options.opacity;
 
7594                 setOpacity(this._icon, opacity);
 
7597                         setOpacity(this._shadow, opacity);
 
7601         _bringToFront: function () {
 
7602                 this._updateZIndex(this.options.riseOffset);
 
7605         _resetZIndex: function () {
 
7606                 this._updateZIndex(0);
 
7609         _getPopupAnchor: function () {
 
7610                 return this.options.icon.options.popupAnchor;
 
7613         _getTooltipAnchor: function () {
 
7614                 return this.options.icon.options.tooltipAnchor;
 
7619 // factory L.marker(latlng: LatLng, options? : Marker options)
 
7621 // @factory L.marker(latlng: LatLng, options? : Marker options)
 
7622 // Instantiates a Marker object given a geographical point and optionally an options object.
 
7623 function marker(latlng, options) {
 
7624         return new Marker(latlng, options);
 
7630  * @inherits Interactive layer
 
7632  * An abstract class that contains options and constants shared between vector
 
7633  * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
 
7636 var Path = Layer.extend({
 
7639         // @aka Path options
 
7641                 // @option stroke: Boolean = true
 
7642                 // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
 
7645                 // @option color: String = '#3388ff'
 
7649                 // @option weight: Number = 3
 
7650                 // Stroke width in pixels
 
7653                 // @option opacity: Number = 1.0
 
7657                 // @option lineCap: String= 'round'
 
7658                 // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
 
7661                 // @option lineJoin: String = 'round'
 
7662                 // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
 
7665                 // @option dashArray: String = null
 
7666                 // 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).
 
7669                 // @option dashOffset: String = null
 
7670                 // 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).
 
7673                 // @option fill: Boolean = depends
 
7674                 // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
 
7677                 // @option fillColor: String = *
 
7678                 // Fill color. Defaults to the value of the [`color`](#path-color) option
 
7681                 // @option fillOpacity: Number = 0.2
 
7685                 // @option fillRule: String = 'evenodd'
 
7686                 // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
 
7687                 fillRule: 'evenodd',
 
7691                 // Option inherited from "Interactive layer" abstract class
 
7694                 // @option bubblingMouseEvents: Boolean = true
 
7695                 // When `true`, a mouse event on this path will trigger the same event on the map
 
7696                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
 
7697                 bubblingMouseEvents: true
 
7700         beforeAdd: function (map) {
 
7701                 // Renderer is set here because we need to call renderer.getEvents
 
7702                 // before this.getEvents.
 
7703                 this._renderer = map.getRenderer(this);
 
7706         onAdd: function () {
 
7707                 this._renderer._initPath(this);
 
7709                 this._renderer._addPath(this);
 
7712         onRemove: function () {
 
7713                 this._renderer._removePath(this);
 
7716         // @method redraw(): this
 
7717         // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
 
7718         redraw: function () {
 
7720                         this._renderer._updatePath(this);
 
7725         // @method setStyle(style: Path options): this
 
7726         // Changes the appearance of a Path based on the options in the `Path options` object.
 
7727         setStyle: function (style) {
 
7728                 setOptions(this, style);
 
7729                 if (this._renderer) {
 
7730                         this._renderer._updateStyle(this);
 
7735         // @method bringToFront(): this
 
7736         // Brings the layer to the top of all path layers.
 
7737         bringToFront: function () {
 
7738                 if (this._renderer) {
 
7739                         this._renderer._bringToFront(this);
 
7744         // @method bringToBack(): this
 
7745         // Brings the layer to the bottom of all path layers.
 
7746         bringToBack: function () {
 
7747                 if (this._renderer) {
 
7748                         this._renderer._bringToBack(this);
 
7753         getElement: function () {
 
7757         _reset: function () {
 
7758                 // defined in child classes
 
7763         _clickTolerance: function () {
 
7764                 // used when doing hit detection for Canvas layers
 
7765                 return (this.options.stroke ? this.options.weight / 2 : 0) + this._renderer.options.tolerance;
 
7770  * @class CircleMarker
 
7771  * @aka L.CircleMarker
 
7774  * A circle of a fixed size with radius specified in pixels. Extends `Path`.
 
7777 var CircleMarker = Path.extend({
 
7780         // @aka CircleMarker options
 
7784                 // @option radius: Number = 10
 
7785                 // Radius of the circle marker, in pixels
 
7789         initialize: function (latlng, options) {
 
7790                 setOptions(this, options);
 
7791                 this._latlng = toLatLng(latlng);
 
7792                 this._radius = this.options.radius;
 
7795         // @method setLatLng(latLng: LatLng): this
 
7796         // Sets the position of a circle marker to a new location.
 
7797         setLatLng: function (latlng) {
 
7798                 this._latlng = toLatLng(latlng);
 
7800                 return this.fire('move', {latlng: this._latlng});
 
7803         // @method getLatLng(): LatLng
 
7804         // Returns the current geographical position of the circle marker
 
7805         getLatLng: function () {
 
7806                 return this._latlng;
 
7809         // @method setRadius(radius: Number): this
 
7810         // Sets the radius of a circle marker. Units are in pixels.
 
7811         setRadius: function (radius) {
 
7812                 this.options.radius = this._radius = radius;
 
7813                 return this.redraw();
 
7816         // @method getRadius(): Number
 
7817         // Returns the current radius of the circle
 
7818         getRadius: function () {
 
7819                 return this._radius;
 
7822         setStyle : function (options) {
 
7823                 var radius = options && options.radius || this._radius;
 
7824                 Path.prototype.setStyle.call(this, options);
 
7825                 this.setRadius(radius);
 
7829         _project: function () {
 
7830                 this._point = this._map.latLngToLayerPoint(this._latlng);
 
7831                 this._updateBounds();
 
7834         _updateBounds: function () {
 
7835                 var r = this._radius,
 
7836                     r2 = this._radiusY || r,
 
7837                     w = this._clickTolerance(),
 
7838                     p = [r + w, r2 + w];
 
7839                 this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
 
7842         _update: function () {
 
7848         _updatePath: function () {
 
7849                 this._renderer._updateCircle(this);
 
7852         _empty: function () {
 
7853                 return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
 
7856         // Needed by the `Canvas` renderer for interactivity
 
7857         _containsPoint: function (p) {
 
7858                 return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
 
7863 // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
 
7864 // Instantiates a circle marker object given a geographical point, and an optional options object.
 
7865 function circleMarker(latlng, options) {
 
7866         return new CircleMarker(latlng, options);
 
7872  * @inherits CircleMarker
 
7874  * A class for drawing circle overlays on a map. Extends `CircleMarker`.
 
7876  * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
 
7881  * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
 
7885 var Circle = CircleMarker.extend({
 
7887         initialize: function (latlng, options, legacyOptions) {
 
7888                 if (typeof options === 'number') {
 
7889                         // Backwards compatibility with 0.7.x factory (latlng, radius, options?)
 
7890                         options = extend({}, legacyOptions, {radius: options});
 
7892                 setOptions(this, options);
 
7893                 this._latlng = toLatLng(latlng);
 
7895                 if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
 
7898                 // @aka Circle options
 
7899                 // @option radius: Number; Radius of the circle, in meters.
 
7900                 this._mRadius = this.options.radius;
 
7903         // @method setRadius(radius: Number): this
 
7904         // Sets the radius of a circle. Units are in meters.
 
7905         setRadius: function (radius) {
 
7906                 this._mRadius = radius;
 
7907                 return this.redraw();
 
7910         // @method getRadius(): Number
 
7911         // Returns the current radius of a circle. Units are in meters.
 
7912         getRadius: function () {
 
7913                 return this._mRadius;
 
7916         // @method getBounds(): LatLngBounds
 
7917         // Returns the `LatLngBounds` of the path.
 
7918         getBounds: function () {
 
7919                 var half = [this._radius, this._radiusY || this._radius];
 
7921                 return new LatLngBounds(
 
7922                         this._map.layerPointToLatLng(this._point.subtract(half)),
 
7923                         this._map.layerPointToLatLng(this._point.add(half)));
 
7926         setStyle: Path.prototype.setStyle,
 
7928         _project: function () {
 
7930                 var lng = this._latlng.lng,
 
7931                     lat = this._latlng.lat,
 
7933                     crs = map.options.crs;
 
7935                 if (crs.distance === Earth.distance) {
 
7936                         var d = Math.PI / 180,
 
7937                             latR = (this._mRadius / Earth.R) / d,
 
7938                             top = map.project([lat + latR, lng]),
 
7939                             bottom = map.project([lat - latR, lng]),
 
7940                             p = top.add(bottom).divideBy(2),
 
7941                             lat2 = map.unproject(p).lat,
 
7942                             lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
 
7943                                     (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
 
7945                         if (isNaN(lngR) || lngR === 0) {
 
7946                                 lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
 
7949                         this._point = p.subtract(map.getPixelOrigin());
 
7950                         this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x;
 
7951                         this._radiusY = p.y - top.y;
 
7954                         var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
 
7956                         this._point = map.latLngToLayerPoint(this._latlng);
 
7957                         this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
 
7960                 this._updateBounds();
 
7964 // @factory L.circle(latlng: LatLng, options?: Circle options)
 
7965 // Instantiates a circle object given a geographical point, and an options object
 
7966 // which contains the circle radius.
 
7968 // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
 
7969 // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
 
7970 // Do not use in new applications or plugins.
 
7971 function circle(latlng, options, legacyOptions) {
 
7972         return new Circle(latlng, options, legacyOptions);
 
7980  * A class for drawing polyline overlays on a map. Extends `Path`.
 
7985  * // create a red polyline from an array of LatLng points
 
7992  * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
 
7994  * // zoom the map to the polyline
 
7995  * map.fitBounds(polyline.getBounds());
 
7998  * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
 
8001  * // create a red polyline from an array of arrays of LatLng points
 
8003  *      [[45.51, -122.68],
 
8014 var Polyline = Path.extend({
 
8017         // @aka Polyline options
 
8019                 // @option smoothFactor: Number = 1.0
 
8020                 // How much to simplify the polyline on each zoom level. More means
 
8021                 // better performance and smoother look, and less means more accurate representation.
 
8024                 // @option noClip: Boolean = false
 
8025                 // Disable polyline clipping.
 
8029         initialize: function (latlngs, options) {
 
8030                 setOptions(this, options);
 
8031                 this._setLatLngs(latlngs);
 
8034         // @method getLatLngs(): LatLng[]
 
8035         // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
 
8036         getLatLngs: function () {
 
8037                 return this._latlngs;
 
8040         // @method setLatLngs(latlngs: LatLng[]): this
 
8041         // Replaces all the points in the polyline with the given array of geographical points.
 
8042         setLatLngs: function (latlngs) {
 
8043                 this._setLatLngs(latlngs);
 
8044                 return this.redraw();
 
8047         // @method isEmpty(): Boolean
 
8048         // Returns `true` if the Polyline has no LatLngs.
 
8049         isEmpty: function () {
 
8050                 return !this._latlngs.length;
 
8053         // @method closestLayerPoint: Point
 
8054         // Returns the point closest to `p` on the Polyline.
 
8055         closestLayerPoint: function (p) {
 
8056                 var minDistance = Infinity,
 
8058                     closest = _sqClosestPointOnSegment,
 
8061                 for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
 
8062                         var points = this._parts[j];
 
8064                         for (var i = 1, len = points.length; i < len; i++) {
 
8068                                 var sqDist = closest(p, p1, p2, true);
 
8070                                 if (sqDist < minDistance) {
 
8071                                         minDistance = sqDist;
 
8072                                         minPoint = closest(p, p1, p2);
 
8077                         minPoint.distance = Math.sqrt(minDistance);
 
8082         // @method getCenter(): LatLng
 
8083         // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
 
8084         getCenter: function () {
 
8085                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
8087                         throw new Error('Must add layer to map before using getCenter()');
 
8090                 var i, halfDist, segDist, dist, p1, p2, ratio,
 
8091                     points = this._rings[0],
 
8092                     len = points.length;
 
8094                 if (!len) { return null; }
 
8096                 // polyline centroid algorithm; only uses the first ring if there are multiple
 
8098                 for (i = 0, halfDist = 0; i < len - 1; i++) {
 
8099                         halfDist += points[i].distanceTo(points[i + 1]) / 2;
 
8102                 // The line is so small in the current view that all points are on the same pixel.
 
8103                 if (halfDist === 0) {
 
8104                         return this._map.layerPointToLatLng(points[0]);
 
8107                 for (i = 0, dist = 0; i < len - 1; i++) {
 
8110                         segDist = p1.distanceTo(p2);
 
8113                         if (dist > halfDist) {
 
8114                                 ratio = (dist - halfDist) / segDist;
 
8115                                 return this._map.layerPointToLatLng([
 
8116                                         p2.x - ratio * (p2.x - p1.x),
 
8117                                         p2.y - ratio * (p2.y - p1.y)
 
8123         // @method getBounds(): LatLngBounds
 
8124         // Returns the `LatLngBounds` of the path.
 
8125         getBounds: function () {
 
8126                 return this._bounds;
 
8129         // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
 
8130         // Adds a given point to the polyline. By default, adds to the first ring of
 
8131         // the polyline in case of a multi-polyline, but can be overridden by passing
 
8132         // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
 
8133         addLatLng: function (latlng, latlngs) {
 
8134                 latlngs = latlngs || this._defaultShape();
 
8135                 latlng = toLatLng(latlng);
 
8136                 latlngs.push(latlng);
 
8137                 this._bounds.extend(latlng);
 
8138                 return this.redraw();
 
8141         _setLatLngs: function (latlngs) {
 
8142                 this._bounds = new LatLngBounds();
 
8143                 this._latlngs = this._convertLatLngs(latlngs);
 
8146         _defaultShape: function () {
 
8147                 return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
 
8150         // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
 
8151         _convertLatLngs: function (latlngs) {
 
8153                     flat = isFlat(latlngs);
 
8155                 for (var i = 0, len = latlngs.length; i < len; i++) {
 
8157                                 result[i] = toLatLng(latlngs[i]);
 
8158                                 this._bounds.extend(result[i]);
 
8160                                 result[i] = this._convertLatLngs(latlngs[i]);
 
8167         _project: function () {
 
8168                 var pxBounds = new Bounds();
 
8170                 this._projectLatlngs(this._latlngs, this._rings, pxBounds);
 
8172                 var w = this._clickTolerance(),
 
8173                     p = new Point(w, w);
 
8175                 if (this._bounds.isValid() && pxBounds.isValid()) {
 
8176                         pxBounds.min._subtract(p);
 
8177                         pxBounds.max._add(p);
 
8178                         this._pxBounds = pxBounds;
 
8182         // recursively turns latlngs into a set of rings with projected coordinates
 
8183         _projectLatlngs: function (latlngs, result, projectedBounds) {
 
8184                 var flat = latlngs[0] instanceof LatLng,
 
8185                     len = latlngs.length,
 
8190                         for (i = 0; i < len; i++) {
 
8191                                 ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
 
8192                                 projectedBounds.extend(ring[i]);
 
8196                         for (i = 0; i < len; i++) {
 
8197                                 this._projectLatlngs(latlngs[i], result, projectedBounds);
 
8202         // clip polyline by renderer bounds so that we have less to render for performance
 
8203         _clipPoints: function () {
 
8204                 var bounds = this._renderer._bounds;
 
8207                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
8211                 if (this.options.noClip) {
 
8212                         this._parts = this._rings;
 
8216                 var parts = this._parts,
 
8217                     i, j, k, len, len2, segment, points;
 
8219                 for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
 
8220                         points = this._rings[i];
 
8222                         for (j = 0, len2 = points.length; j < len2 - 1; j++) {
 
8223                                 segment = clipSegment(points[j], points[j + 1], bounds, j, true);
 
8225                                 if (!segment) { continue; }
 
8227                                 parts[k] = parts[k] || [];
 
8228                                 parts[k].push(segment[0]);
 
8230                                 // if segment goes out of screen, or it's the last one, it's the end of the line part
 
8231                                 if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
 
8232                                         parts[k].push(segment[1]);
 
8239         // simplify each clipped part of the polyline for performance
 
8240         _simplifyPoints: function () {
 
8241                 var parts = this._parts,
 
8242                     tolerance = this.options.smoothFactor;
 
8244                 for (var i = 0, len = parts.length; i < len; i++) {
 
8245                         parts[i] = simplify(parts[i], tolerance);
 
8249         _update: function () {
 
8250                 if (!this._map) { return; }
 
8253                 this._simplifyPoints();
 
8257         _updatePath: function () {
 
8258                 this._renderer._updatePoly(this);
 
8261         // Needed by the `Canvas` renderer for interactivity
 
8262         _containsPoint: function (p, closed) {
 
8263                 var i, j, k, len, len2, part,
 
8264                     w = this._clickTolerance();
 
8266                 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
 
8268                 // hit detection for polylines
 
8269                 for (i = 0, len = this._parts.length; i < len; i++) {
 
8270                         part = this._parts[i];
 
8272                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
8273                                 if (!closed && (j === 0)) { continue; }
 
8275                                 if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
 
8284 // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
 
8285 // Instantiates a polyline object given an array of geographical points and
 
8286 // optionally an options object. You can create a `Polyline` object with
 
8287 // multiple separate lines (`MultiPolyline`) by passing an array of arrays
 
8288 // of geographic points.
 
8289 function polyline(latlngs, options) {
 
8290         return new Polyline(latlngs, options);
 
8293 // Retrocompat. Allow plugins to support Leaflet versions before and after 1.1.
 
8294 Polyline._flat = _flat;
 
8299  * @inherits Polyline
 
8301  * A class for drawing polygon overlays on a map. Extends `Polyline`.
 
8303  * 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.
 
8309  * // create a red polygon from an array of LatLng points
 
8310  * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
 
8312  * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
 
8314  * // zoom the map to the polygon
 
8315  * map.fitBounds(polygon.getBounds());
 
8318  * 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:
 
8322  *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
 
8323  *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
 
8327  * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
 
8331  *   [ // first polygon
 
8332  *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
 
8333  *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
 
8335  *   [ // second polygon
 
8336  *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
 
8342 var Polygon = Polyline.extend({
 
8348         isEmpty: function () {
 
8349                 return !this._latlngs.length || !this._latlngs[0].length;
 
8352         getCenter: function () {
 
8353                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
8355                         throw new Error('Must add layer to map before using getCenter()');
 
8358                 var i, j, p1, p2, f, area, x, y, center,
 
8359                     points = this._rings[0],
 
8360                     len = points.length;
 
8362                 if (!len) { return null; }
 
8364                 // polygon centroid algorithm; only uses the first ring if there are multiple
 
8368                 for (i = 0, j = len - 1; i < len; j = i++) {
 
8372                         f = p1.y * p2.x - p2.y * p1.x;
 
8373                         x += (p1.x + p2.x) * f;
 
8374                         y += (p1.y + p2.y) * f;
 
8379                         // Polygon is so small that all points are on same pixel.
 
8382                         center = [x / area, y / area];
 
8384                 return this._map.layerPointToLatLng(center);
 
8387         _convertLatLngs: function (latlngs) {
 
8388                 var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
 
8389                     len = result.length;
 
8391                 // remove last point if it equals first one
 
8392                 if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
 
8398         _setLatLngs: function (latlngs) {
 
8399                 Polyline.prototype._setLatLngs.call(this, latlngs);
 
8400                 if (isFlat(this._latlngs)) {
 
8401                         this._latlngs = [this._latlngs];
 
8405         _defaultShape: function () {
 
8406                 return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
 
8409         _clipPoints: function () {
 
8410                 // polygons need a different clipping algorithm so we redefine that
 
8412                 var bounds = this._renderer._bounds,
 
8413                     w = this.options.weight,
 
8414                     p = new Point(w, w);
 
8416                 // increase clip padding by stroke width to avoid stroke on clip edges
 
8417                 bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
 
8420                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
8424                 if (this.options.noClip) {
 
8425                         this._parts = this._rings;
 
8429                 for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
 
8430                         clipped = clipPolygon(this._rings[i], bounds, true);
 
8431                         if (clipped.length) {
 
8432                                 this._parts.push(clipped);
 
8437         _updatePath: function () {
 
8438                 this._renderer._updatePoly(this, true);
 
8441         // Needed by the `Canvas` renderer for interactivity
 
8442         _containsPoint: function (p) {
 
8444                     part, p1, p2, i, j, k, len, len2;
 
8446                 if (!this._pxBounds.contains(p)) { return false; }
 
8448                 // ray casting algorithm for detecting if point is in polygon
 
8449                 for (i = 0, len = this._parts.length; i < len; i++) {
 
8450                         part = this._parts[i];
 
8452                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
8456                                 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)) {
 
8462                 // also check if it's on polygon stroke
 
8463                 return inside || Polyline.prototype._containsPoint.call(this, p, true);
 
8469 // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
 
8470 function polygon(latlngs, options) {
 
8471         return new Polygon(latlngs, options);
 
8477  * @inherits FeatureGroup
 
8479  * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
 
8480  * GeoJSON data and display it on the map. Extends `FeatureGroup`.
 
8486  *      style: function (feature) {
 
8487  *              return {color: feature.properties.color};
 
8489  * }).bindPopup(function (layer) {
 
8490  *      return layer.feature.properties.description;
 
8495 var GeoJSON = FeatureGroup.extend({
 
8498          * @aka GeoJSON options
 
8500          * @option pointToLayer: Function = *
 
8501          * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
 
8502          * called when data is added, passing the GeoJSON point feature and its `LatLng`.
 
8503          * The default is to spawn a default `Marker`:
 
8505          * function(geoJsonPoint, latlng) {
 
8506          *      return L.marker(latlng);
 
8510          * @option style: Function = *
 
8511          * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
 
8512          * called internally when data is added.
 
8513          * The default value is to not override any defaults:
 
8515          * function (geoJsonFeature) {
 
8520          * @option onEachFeature: Function = *
 
8521          * A `Function` that will be called once for each created `Feature`, after it has
 
8522          * been created and styled. Useful for attaching events and popups to features.
 
8523          * The default is to do nothing with the newly created layers:
 
8525          * function (feature, layer) {}
 
8528          * @option filter: Function = *
 
8529          * A `Function` that will be used to decide whether to include a feature or not.
 
8530          * The default is to include all features:
 
8532          * function (geoJsonFeature) {
 
8536          * Note: dynamically changing the `filter` option will have effect only on newly
 
8537          * added data. It will _not_ re-evaluate already included features.
 
8539          * @option coordsToLatLng: Function = *
 
8540          * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
 
8541          * The default is the `coordsToLatLng` static method.
 
8544         initialize: function (geojson, options) {
 
8545                 setOptions(this, options);
 
8550                         this.addData(geojson);
 
8554         // @method addData( <GeoJSON> data ): this
 
8555         // Adds a GeoJSON object to the layer.
 
8556         addData: function (geojson) {
 
8557                 var features = isArray(geojson) ? geojson : geojson.features,
 
8561                         for (i = 0, len = features.length; i < len; i++) {
 
8562                                 // only add this if geometry or geometries are set and not null
 
8563                                 feature = features[i];
 
8564                                 if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
 
8565                                         this.addData(feature);
 
8571                 var options = this.options;
 
8573                 if (options.filter && !options.filter(geojson)) { return this; }
 
8575                 var layer = geometryToLayer(geojson, options);
 
8579                 layer.feature = asFeature(geojson);
 
8581                 layer.defaultOptions = layer.options;
 
8582                 this.resetStyle(layer);
 
8584                 if (options.onEachFeature) {
 
8585                         options.onEachFeature(geojson, layer);
 
8588                 return this.addLayer(layer);
 
8591         // @method resetStyle( <Path> layer ): this
 
8592         // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
 
8593         resetStyle: function (layer) {
 
8594                 // reset any custom styles
 
8595                 layer.options = extend({}, layer.defaultOptions);
 
8596                 this._setLayerStyle(layer, this.options.style);
 
8600         // @method setStyle( <Function> style ): this
 
8601         // Changes styles of GeoJSON vector layers with the given style function.
 
8602         setStyle: function (style) {
 
8603                 return this.eachLayer(function (layer) {
 
8604                         this._setLayerStyle(layer, style);
 
8608         _setLayerStyle: function (layer, style) {
 
8609                 if (typeof style === 'function') {
 
8610                         style = style(layer.feature);
 
8612                 if (layer.setStyle) {
 
8613                         layer.setStyle(style);
 
8619 // There are several static functions which can be called without instantiating L.GeoJSON:
 
8621 // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
 
8622 // Creates a `Layer` from a given GeoJSON feature. Can use a custom
 
8623 // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
 
8624 // functions if provided as options.
 
8625 function geometryToLayer(geojson, options) {
 
8627         var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
 
8628             coords = geometry ? geometry.coordinates : null,
 
8630             pointToLayer = options && options.pointToLayer,
 
8631             _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
 
8632             latlng, latlngs, i, len;
 
8634         if (!coords && !geometry) {
 
8638         switch (geometry.type) {
 
8640                 latlng = _coordsToLatLng(coords);
 
8641                 return pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng);
 
8644                 for (i = 0, len = coords.length; i < len; i++) {
 
8645                         latlng = _coordsToLatLng(coords[i]);
 
8646                         layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng));
 
8648                 return new FeatureGroup(layers);
 
8651         case 'MultiLineString':
 
8652                 latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
 
8653                 return new Polyline(latlngs, options);
 
8656         case 'MultiPolygon':
 
8657                 latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
 
8658                 return new Polygon(latlngs, options);
 
8660         case 'GeometryCollection':
 
8661                 for (i = 0, len = geometry.geometries.length; i < len; i++) {
 
8662                         var layer = geometryToLayer({
 
8663                                 geometry: geometry.geometries[i],
 
8665                                 properties: geojson.properties
 
8672                 return new FeatureGroup(layers);
 
8675                 throw new Error('Invalid GeoJSON object.');
 
8679 // @function coordsToLatLng(coords: Array): LatLng
 
8680 // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
 
8681 // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
 
8682 function coordsToLatLng(coords) {
 
8683         return new LatLng(coords[1], coords[0], coords[2]);
 
8686 // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
 
8687 // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
 
8688 // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
 
8689 // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
 
8690 function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
 
8693         for (var i = 0, len = coords.length, latlng; i < len; i++) {
 
8694                 latlng = levelsDeep ?
 
8695                         coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
 
8696                         (_coordsToLatLng || coordsToLatLng)(coords[i]);
 
8698                 latlngs.push(latlng);
 
8704 // @function latLngToCoords(latlng: LatLng, precision?: Number): Array
 
8705 // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
 
8706 function latLngToCoords(latlng, precision) {
 
8707         precision = typeof precision === 'number' ? precision : 6;
 
8708         return latlng.alt !== undefined ?
 
8709                 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
 
8710                 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
 
8713 // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
 
8714 // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
 
8715 // `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.
 
8716 function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
 
8719         for (var i = 0, len = latlngs.length; i < len; i++) {
 
8720                 coords.push(levelsDeep ?
 
8721                         latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) :
 
8722                         latLngToCoords(latlngs[i], precision));
 
8725         if (!levelsDeep && closed) {
 
8726                 coords.push(coords[0]);
 
8732 function getFeature(layer, newGeometry) {
 
8733         return layer.feature ?
 
8734                 extend({}, layer.feature, {geometry: newGeometry}) :
 
8735                 asFeature(newGeometry);
 
8738 // @function asFeature(geojson: Object): Object
 
8739 // Normalize GeoJSON geometries/features into GeoJSON features.
 
8740 function asFeature(geojson) {
 
8741         if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
 
8752 var PointToGeoJSON = {
 
8753         toGeoJSON: function (precision) {
 
8754                 return getFeature(this, {
 
8756                         coordinates: latLngToCoords(this.getLatLng(), precision)
 
8761 // @namespace Marker
 
8762 // @method toGeoJSON(): Object
 
8763 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
 
8764 Marker.include(PointToGeoJSON);
 
8766 // @namespace CircleMarker
 
8767 // @method toGeoJSON(): Object
 
8768 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
 
8769 Circle.include(PointToGeoJSON);
 
8770 CircleMarker.include(PointToGeoJSON);
 
8773 // @namespace Polyline
 
8774 // @method toGeoJSON(): Object
 
8775 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
 
8777         toGeoJSON: function (precision) {
 
8778                 var multi = !isFlat(this._latlngs);
 
8780                 var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
 
8782                 return getFeature(this, {
 
8783                         type: (multi ? 'Multi' : '') + 'LineString',
 
8789 // @namespace Polygon
 
8790 // @method toGeoJSON(): Object
 
8791 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
 
8793         toGeoJSON: function (precision) {
 
8794                 var holes = !isFlat(this._latlngs),
 
8795                     multi = holes && !isFlat(this._latlngs[0]);
 
8797                 var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
 
8803                 return getFeature(this, {
 
8804                         type: (multi ? 'Multi' : '') + 'Polygon',
 
8811 // @namespace LayerGroup
 
8812 LayerGroup.include({
 
8813         toMultiPoint: function (precision) {
 
8816                 this.eachLayer(function (layer) {
 
8817                         coords.push(layer.toGeoJSON(precision).geometry.coordinates);
 
8820                 return getFeature(this, {
 
8826         // @method toGeoJSON(): Object
 
8827         // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
 
8828         toGeoJSON: function (precision) {
 
8830                 var type = this.feature && this.feature.geometry && this.feature.geometry.type;
 
8832                 if (type === 'MultiPoint') {
 
8833                         return this.toMultiPoint(precision);
 
8836                 var isGeometryCollection = type === 'GeometryCollection',
 
8839                 this.eachLayer(function (layer) {
 
8840                         if (layer.toGeoJSON) {
 
8841                                 var json = layer.toGeoJSON(precision);
 
8842                                 if (isGeometryCollection) {
 
8843                                         jsons.push(json.geometry);
 
8845                                         var feature = asFeature(json);
 
8846                                         // Squash nested feature collections
 
8847                                         if (feature.type === 'FeatureCollection') {
 
8848                                                 jsons.push.apply(jsons, feature.features);
 
8850                                                 jsons.push(feature);
 
8856                 if (isGeometryCollection) {
 
8857                         return getFeature(this, {
 
8859                                 type: 'GeometryCollection'
 
8864                         type: 'FeatureCollection',
 
8870 // @namespace GeoJSON
 
8871 // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
 
8872 // Creates a GeoJSON layer. Optionally accepts an object in
 
8873 // [GeoJSON format](http://geojson.org/geojson-spec.html) to display on the map
 
8874 // (you can alternatively add it later with `addData` method) and an `options` object.
 
8875 function geoJSON(geojson, options) {
 
8876         return new GeoJSON(geojson, options);
 
8879 // Backward compatibility.
 
8880 var geoJson = geoJSON;
 
8883  * @class ImageOverlay
 
8884  * @aka L.ImageOverlay
 
8885  * @inherits Interactive layer
 
8887  * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
 
8892  * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
 
8893  *      imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
 
8894  * L.imageOverlay(imageUrl, imageBounds).addTo(map);
 
8898 var ImageOverlay = Layer.extend({
 
8901         // @aka ImageOverlay options
 
8903                 // @option opacity: Number = 1.0
 
8904                 // The opacity of the image overlay.
 
8907                 // @option alt: String = ''
 
8908                 // Text for the `alt` attribute of the image (useful for accessibility).
 
8911                 // @option interactive: Boolean = false
 
8912                 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
 
8915                 // @option crossOrigin: Boolean = false
 
8916                 // If true, the image will have its crossOrigin attribute set to ''. This is needed if you want to access image pixel data.
 
8919                 // @option errorOverlayUrl: String = ''
 
8920                 // URL to the overlay image to show in place of the overlay that failed to load.
 
8921                 errorOverlayUrl: '',
 
8923                 // @option zIndex: Number = 1
 
8924                 // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the tile layer.
 
8927                 // @option className: String = ''
 
8928                 // A custom class name to assign to the image. Empty by default.
 
8932         initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
 
8934                 this._bounds = toLatLngBounds(bounds);
 
8936                 setOptions(this, options);
 
8939         onAdd: function () {
 
8943                         if (this.options.opacity < 1) {
 
8944                                 this._updateOpacity();
 
8948                 if (this.options.interactive) {
 
8949                         addClass(this._image, 'leaflet-interactive');
 
8950                         this.addInteractiveTarget(this._image);
 
8953                 this.getPane().appendChild(this._image);
 
8957         onRemove: function () {
 
8958                 remove(this._image);
 
8959                 if (this.options.interactive) {
 
8960                         this.removeInteractiveTarget(this._image);
 
8964         // @method setOpacity(opacity: Number): this
 
8965         // Sets the opacity of the overlay.
 
8966         setOpacity: function (opacity) {
 
8967                 this.options.opacity = opacity;
 
8970                         this._updateOpacity();
 
8975         setStyle: function (styleOpts) {
 
8976                 if (styleOpts.opacity) {
 
8977                         this.setOpacity(styleOpts.opacity);
 
8982         // @method bringToFront(): this
 
8983         // Brings the layer to the top of all overlays.
 
8984         bringToFront: function () {
 
8986                         toFront(this._image);
 
8991         // @method bringToBack(): this
 
8992         // Brings the layer to the bottom of all overlays.
 
8993         bringToBack: function () {
 
8995                         toBack(this._image);
 
9000         // @method setUrl(url: String): this
 
9001         // Changes the URL of the image.
 
9002         setUrl: function (url) {
 
9006                         this._image.src = url;
 
9011         // @method setBounds(bounds: LatLngBounds): this
 
9012         // Update the bounds that this ImageOverlay covers
 
9013         setBounds: function (bounds) {
 
9014                 this._bounds = toLatLngBounds(bounds);
 
9022         getEvents: function () {
 
9025                         viewreset: this._reset
 
9028                 if (this._zoomAnimated) {
 
9029                         events.zoomanim = this._animateZoom;
 
9035         // @method: setZIndex(value: Number) : this
 
9036         // Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
 
9037         setZIndex: function (value) {
 
9038                 this.options.zIndex = value;
 
9039                 this._updateZIndex();
 
9043         // @method getBounds(): LatLngBounds
 
9044         // Get the bounds that this ImageOverlay covers
 
9045         getBounds: function () {
 
9046                 return this._bounds;
 
9049         // @method getElement(): HTMLElement
 
9050         // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
 
9051         // used by this overlay.
 
9052         getElement: function () {
 
9056         _initImage: function () {
 
9057                 var wasElementSupplied = this._url.tagName === 'IMG';
 
9058                 var img = this._image = wasElementSupplied ? this._url : create$1('img');
 
9060                 addClass(img, 'leaflet-image-layer');
 
9061                 if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); }
 
9062                 if (this.options.className) { addClass(img, this.options.className); }
 
9064                 img.onselectstart = falseFn;
 
9065                 img.onmousemove = falseFn;
 
9067                 // @event load: Event
 
9068                 // Fired when the ImageOverlay layer has loaded its image
 
9069                 img.onload = bind(this.fire, this, 'load');
 
9070                 img.onerror = bind(this._overlayOnError, this, 'error');
 
9072                 if (this.options.crossOrigin) {
 
9073                         img.crossOrigin = '';
 
9076                 if (this.options.zIndex) {
 
9077                         this._updateZIndex();
 
9080                 if (wasElementSupplied) {
 
9081                         this._url = img.src;
 
9085                 img.src = this._url;
 
9086                 img.alt = this.options.alt;
 
9089         _animateZoom: function (e) {
 
9090                 var scale = this._map.getZoomScale(e.zoom),
 
9091                     offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
 
9093                 setTransform(this._image, offset, scale);
 
9096         _reset: function () {
 
9097                 var image = this._image,
 
9098                     bounds = new Bounds(
 
9099                         this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
 
9100                         this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
 
9101                     size = bounds.getSize();
 
9103                 setPosition(image, bounds.min);
 
9105                 image.style.width  = size.x + 'px';
 
9106                 image.style.height = size.y + 'px';
 
9109         _updateOpacity: function () {
 
9110                 setOpacity(this._image, this.options.opacity);
 
9113         _updateZIndex: function () {
 
9114                 if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
 
9115                         this._image.style.zIndex = this.options.zIndex;
 
9119         _overlayOnError: function () {
 
9120                 // @event error: Event
 
9121                 // Fired when the ImageOverlay layer has loaded its image
 
9124                 var errorUrl = this.options.errorOverlayUrl;
 
9125                 if (errorUrl && this._url !== errorUrl) {
 
9126                         this._url = errorUrl;
 
9127                         this._image.src = errorUrl;
 
9132 // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
 
9133 // Instantiates an image overlay object given the URL of the image and the
 
9134 // geographical bounds it is tied to.
 
9135 var imageOverlay = function (url, bounds, options) {
 
9136         return new ImageOverlay(url, bounds, options);
 
9140  * @class VideoOverlay
 
9141  * @aka L.VideoOverlay
 
9142  * @inherits ImageOverlay
 
9144  * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
 
9146  * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
 
9152  * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
 
9153  *      videoBounds = [[ 32, -130], [ 13, -100]];
 
9154  * L.VideoOverlay(videoUrl, videoBounds ).addTo(map);
 
9158 var VideoOverlay = ImageOverlay.extend({
 
9161         // @aka VideoOverlay options
 
9163                 // @option autoplay: Boolean = true
 
9164                 // Whether the video starts playing automatically when loaded.
 
9167                 // @option loop: Boolean = true
 
9168                 // Whether the video will loop back to the beginning when played.
 
9172         _initImage: function () {
 
9173                 var wasElementSupplied = this._url.tagName === 'VIDEO';
 
9174                 var vid = this._image = wasElementSupplied ? this._url : create$1('video');
 
9176                 addClass(vid, 'leaflet-image-layer');
 
9177                 if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); }
 
9179                 vid.onselectstart = falseFn;
 
9180                 vid.onmousemove = falseFn;
 
9182                 // @event load: Event
 
9183                 // Fired when the video has finished loading the first frame
 
9184                 vid.onloadeddata = bind(this.fire, this, 'load');
 
9186                 if (wasElementSupplied) {
 
9187                         var sourceElements = vid.getElementsByTagName('source');
 
9189                         for (var j = 0; j < sourceElements.length; j++) {
 
9190                                 sources.push(sourceElements[j].src);
 
9193                         this._url = (sourceElements.length > 0) ? sources : [vid.src];
 
9197                 if (!isArray(this._url)) { this._url = [this._url]; }
 
9199                 vid.autoplay = !!this.options.autoplay;
 
9200                 vid.loop = !!this.options.loop;
 
9201                 for (var i = 0; i < this._url.length; i++) {
 
9202                         var source = create$1('source');
 
9203                         source.src = this._url[i];
 
9204                         vid.appendChild(source);
 
9208         // @method getElement(): HTMLVideoElement
 
9209         // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
 
9210         // used by this overlay.
 
9214 // @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options)
 
9215 // Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the
 
9216 // geographical bounds it is tied to.
 
9218 function videoOverlay(video, bounds, options) {
 
9219         return new VideoOverlay(video, bounds, options);
 
9226  * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
 
9229 // @namespace DivOverlay
 
9230 var DivOverlay = Layer.extend({
 
9233         // @aka DivOverlay options
 
9235                 // @option offset: Point = Point(0, 7)
 
9236                 // The offset of the popup position. Useful to control the anchor
 
9237                 // of the popup when opening it on some overlays.
 
9240                 // @option className: String = ''
 
9241                 // A custom CSS class name to assign to the popup.
 
9244                 // @option pane: String = 'popupPane'
 
9245                 // `Map pane` where the popup will be added.
 
9249         initialize: function (options, source) {
 
9250                 setOptions(this, options);
 
9252                 this._source = source;
 
9255         onAdd: function (map) {
 
9256                 this._zoomAnimated = map._zoomAnimated;
 
9258                 if (!this._container) {
 
9262                 if (map._fadeAnimated) {
 
9263                         setOpacity(this._container, 0);
 
9266                 clearTimeout(this._removeTimeout);
 
9267                 this.getPane().appendChild(this._container);
 
9270                 if (map._fadeAnimated) {
 
9271                         setOpacity(this._container, 1);
 
9274                 this.bringToFront();
 
9277         onRemove: function (map) {
 
9278                 if (map._fadeAnimated) {
 
9279                         setOpacity(this._container, 0);
 
9280                         this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200);
 
9282                         remove(this._container);
 
9287         // @method getLatLng: LatLng
 
9288         // Returns the geographical point of popup.
 
9289         getLatLng: function () {
 
9290                 return this._latlng;
 
9293         // @method setLatLng(latlng: LatLng): this
 
9294         // Sets the geographical point where the popup will open.
 
9295         setLatLng: function (latlng) {
 
9296                 this._latlng = toLatLng(latlng);
 
9298                         this._updatePosition();
 
9304         // @method getContent: String|HTMLElement
 
9305         // Returns the content of the popup.
 
9306         getContent: function () {
 
9307                 return this._content;
 
9310         // @method setContent(htmlContent: String|HTMLElement|Function): this
 
9311         // 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.
 
9312         setContent: function (content) {
 
9313                 this._content = content;
 
9318         // @method getElement: String|HTMLElement
 
9319         // Alias for [getContent()](#popup-getcontent)
 
9320         getElement: function () {
 
9321                 return this._container;
 
9324         // @method update: null
 
9325         // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
 
9326         update: function () {
 
9327                 if (!this._map) { return; }
 
9329                 this._container.style.visibility = 'hidden';
 
9331                 this._updateContent();
 
9332                 this._updateLayout();
 
9333                 this._updatePosition();
 
9335                 this._container.style.visibility = '';
 
9340         getEvents: function () {
 
9342                         zoom: this._updatePosition,
 
9343                         viewreset: this._updatePosition
 
9346                 if (this._zoomAnimated) {
 
9347                         events.zoomanim = this._animateZoom;
 
9352         // @method isOpen: Boolean
 
9353         // Returns `true` when the popup is visible on the map.
 
9354         isOpen: function () {
 
9355                 return !!this._map && this._map.hasLayer(this);
 
9358         // @method bringToFront: this
 
9359         // Brings this popup in front of other popups (in the same map pane).
 
9360         bringToFront: function () {
 
9362                         toFront(this._container);
 
9367         // @method bringToBack: this
 
9368         // Brings this popup to the back of other popups (in the same map pane).
 
9369         bringToBack: function () {
 
9371                         toBack(this._container);
 
9376         _updateContent: function () {
 
9377                 if (!this._content) { return; }
 
9379                 var node = this._contentNode;
 
9380                 var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
 
9382                 if (typeof content === 'string') {
 
9383                         node.innerHTML = content;
 
9385                         while (node.hasChildNodes()) {
 
9386                                 node.removeChild(node.firstChild);
 
9388                         node.appendChild(content);
 
9390                 this.fire('contentupdate');
 
9393         _updatePosition: function () {
 
9394                 if (!this._map) { return; }
 
9396                 var pos = this._map.latLngToLayerPoint(this._latlng),
 
9397                     offset = toPoint(this.options.offset),
 
9398                     anchor = this._getAnchor();
 
9400                 if (this._zoomAnimated) {
 
9401                         setPosition(this._container, pos.add(anchor));
 
9403                         offset = offset.add(pos).add(anchor);
 
9406                 var bottom = this._containerBottom = -offset.y,
 
9407                     left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
 
9409                 // bottom position the popup in case the height of the popup changes (images loading etc)
 
9410                 this._container.style.bottom = bottom + 'px';
 
9411                 this._container.style.left = left + 'px';
 
9414         _getAnchor: function () {
 
9422  * @inherits DivOverlay
 
9424  * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
 
9425  * open popups while making sure that only one popup is open at one time
 
9426  * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
 
9430  * If you want to just bind a popup to marker click and then open it, it's really easy:
 
9433  * marker.bindPopup(popupContent).openPopup();
 
9435  * Path overlays like polylines also have a `bindPopup` method.
 
9436  * Here's a more complicated way to open a popup on a map:
 
9439  * var popup = L.popup()
 
9440  *      .setLatLng(latlng)
 
9441  *      .setContent('<p>Hello world!<br />This is a nice popup.</p>')
 
9448 var Popup = DivOverlay.extend({
 
9451         // @aka Popup options
 
9453                 // @option maxWidth: Number = 300
 
9454                 // Max width of the popup, in pixels.
 
9457                 // @option minWidth: Number = 50
 
9458                 // Min width of the popup, in pixels.
 
9461                 // @option maxHeight: Number = null
 
9462                 // If set, creates a scrollable container of the given height
 
9463                 // inside a popup if its content exceeds it.
 
9466                 // @option autoPan: Boolean = true
 
9467                 // Set it to `false` if you don't want the map to do panning animation
 
9468                 // to fit the opened popup.
 
9471                 // @option autoPanPaddingTopLeft: Point = null
 
9472                 // The margin between the popup and the top left corner of the map
 
9473                 // view after autopanning was performed.
 
9474                 autoPanPaddingTopLeft: null,
 
9476                 // @option autoPanPaddingBottomRight: Point = null
 
9477                 // The margin between the popup and the bottom right corner of the map
 
9478                 // view after autopanning was performed.
 
9479                 autoPanPaddingBottomRight: null,
 
9481                 // @option autoPanPadding: Point = Point(5, 5)
 
9482                 // Equivalent of setting both top left and bottom right autopan padding to the same value.
 
9483                 autoPanPadding: [5, 5],
 
9485                 // @option keepInView: Boolean = false
 
9486                 // Set it to `true` if you want to prevent users from panning the popup
 
9487                 // off of the screen while it is open.
 
9490                 // @option closeButton: Boolean = true
 
9491                 // Controls the presence of a close button in the popup.
 
9494                 // @option autoClose: Boolean = true
 
9495                 // Set it to `false` if you want to override the default behavior of
 
9496                 // the popup closing when another popup is opened.
 
9499                 // @option closeOnEscapeKey: Boolean = true
 
9500                 // Set it to `false` if you want to override the default behavior of
 
9501                 // the ESC key for closing of the popup.
 
9502                 closeOnEscapeKey: true,
 
9504                 // @option closeOnClick: Boolean = *
 
9505                 // Set it if you want to override the default behavior of the popup closing when user clicks
 
9506                 // on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
 
9508                 // @option className: String = ''
 
9509                 // A custom CSS class name to assign to the popup.
 
9514         // @method openOn(map: Map): this
 
9515         // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
 
9516         openOn: function (map) {
 
9517                 map.openPopup(this);
 
9521         onAdd: function (map) {
 
9522                 DivOverlay.prototype.onAdd.call(this, map);
 
9525                 // @section Popup events
 
9526                 // @event popupopen: PopupEvent
 
9527                 // Fired when a popup is opened in the map
 
9528                 map.fire('popupopen', {popup: this});
 
9532                         // @section Popup events
 
9533                         // @event popupopen: PopupEvent
 
9534                         // Fired when a popup bound to this layer is opened
 
9535                         this._source.fire('popupopen', {popup: this}, true);
 
9536                         // For non-path layers, we toggle the popup when clicking
 
9537                         // again the layer, so prevent the map to reopen it.
 
9538                         if (!(this._source instanceof Path)) {
 
9539                                 this._source.on('preclick', stopPropagation);
 
9544         onRemove: function (map) {
 
9545                 DivOverlay.prototype.onRemove.call(this, map);
 
9548                 // @section Popup events
 
9549                 // @event popupclose: PopupEvent
 
9550                 // Fired when a popup in the map is closed
 
9551                 map.fire('popupclose', {popup: this});
 
9555                         // @section Popup events
 
9556                         // @event popupclose: PopupEvent
 
9557                         // Fired when a popup bound to this layer is closed
 
9558                         this._source.fire('popupclose', {popup: this}, true);
 
9559                         if (!(this._source instanceof Path)) {
 
9560                                 this._source.off('preclick', stopPropagation);
 
9565         getEvents: function () {
 
9566                 var events = DivOverlay.prototype.getEvents.call(this);
 
9568                 if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
 
9569                         events.preclick = this._close;
 
9572                 if (this.options.keepInView) {
 
9573                         events.moveend = this._adjustPan;
 
9579         _close: function () {
 
9581                         this._map.closePopup(this);
 
9585         _initLayout: function () {
 
9586                 var prefix = 'leaflet-popup',
 
9587                     container = this._container = create$1('div',
 
9588                         prefix + ' ' + (this.options.className || '') +
 
9589                         ' leaflet-zoom-animated');
 
9591                 var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
 
9592                 this._contentNode = create$1('div', prefix + '-content', wrapper);
 
9594                 disableClickPropagation(wrapper);
 
9595                 disableScrollPropagation(this._contentNode);
 
9596                 on(wrapper, 'contextmenu', stopPropagation);
 
9598                 this._tipContainer = create$1('div', prefix + '-tip-container', container);
 
9599                 this._tip = create$1('div', prefix + '-tip', this._tipContainer);
 
9601                 if (this.options.closeButton) {
 
9602                         var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
 
9603                         closeButton.href = '#close';
 
9604                         closeButton.innerHTML = '×';
 
9606                         on(closeButton, 'click', this._onCloseButtonClick, this);
 
9610         _updateLayout: function () {
 
9611                 var container = this._contentNode,
 
9612                     style = container.style;
 
9615                 style.whiteSpace = 'nowrap';
 
9617                 var width = container.offsetWidth;
 
9618                 width = Math.min(width, this.options.maxWidth);
 
9619                 width = Math.max(width, this.options.minWidth);
 
9621                 style.width = (width + 1) + 'px';
 
9622                 style.whiteSpace = '';
 
9626                 var height = container.offsetHeight,
 
9627                     maxHeight = this.options.maxHeight,
 
9628                     scrolledClass = 'leaflet-popup-scrolled';
 
9630                 if (maxHeight && height > maxHeight) {
 
9631                         style.height = maxHeight + 'px';
 
9632                         addClass(container, scrolledClass);
 
9634                         removeClass(container, scrolledClass);
 
9637                 this._containerWidth = this._container.offsetWidth;
 
9640         _animateZoom: function (e) {
 
9641                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
 
9642                     anchor = this._getAnchor();
 
9643                 setPosition(this._container, pos.add(anchor));
 
9646         _adjustPan: function () {
 
9647                 if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; }
 
9649                 var map = this._map,
 
9650                     marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
 
9651                     containerHeight = this._container.offsetHeight + marginBottom,
 
9652                     containerWidth = this._containerWidth,
 
9653                     layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
 
9655                 layerPos._add(getPosition(this._container));
 
9657                 var containerPos = map.layerPointToContainerPoint(layerPos),
 
9658                     padding = toPoint(this.options.autoPanPadding),
 
9659                     paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
 
9660                     paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
 
9661                     size = map.getSize(),
 
9665                 if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
 
9666                         dx = containerPos.x + containerWidth - size.x + paddingBR.x;
 
9668                 if (containerPos.x - dx - paddingTL.x < 0) { // left
 
9669                         dx = containerPos.x - paddingTL.x;
 
9671                 if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
 
9672                         dy = containerPos.y + containerHeight - size.y + paddingBR.y;
 
9674                 if (containerPos.y - dy - paddingTL.y < 0) { // top
 
9675                         dy = containerPos.y - paddingTL.y;
 
9679                 // @section Popup events
 
9680                 // @event autopanstart: Event
 
9681                 // Fired when the map starts autopanning when opening a popup.
 
9684                             .fire('autopanstart')
 
9689         _onCloseButtonClick: function (e) {
 
9694         _getAnchor: function () {
 
9695                 // Where should we anchor the popup on the source layer?
 
9696                 return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
 
9702 // @factory L.popup(options?: Popup options, source?: Layer)
 
9703 // 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.
 
9704 var popup = function (options, source) {
 
9705         return new Popup(options, source);
 
9710  * @section Interaction Options
 
9711  * @option closePopupOnClick: Boolean = true
 
9712  * Set it to `false` if you don't want popups to close when user clicks the map.
 
9715         closePopupOnClick: true
 
9720 // @section Methods for Layers and Controls
 
9722         // @method openPopup(popup: Popup): this
 
9723         // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
 
9725         // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
 
9726         // Creates a popup with the specified content and options and opens it in the given point on a map.
 
9727         openPopup: function (popup, latlng, options) {
 
9728                 if (!(popup instanceof Popup)) {
 
9729                         popup = new Popup(options).setContent(popup);
 
9733                         popup.setLatLng(latlng);
 
9736                 if (this.hasLayer(popup)) {
 
9740                 if (this._popup && this._popup.options.autoClose) {
 
9744                 this._popup = popup;
 
9745                 return this.addLayer(popup);
 
9748         // @method closePopup(popup?: Popup): this
 
9749         // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
 
9750         closePopup: function (popup) {
 
9751                 if (!popup || popup === this._popup) {
 
9752                         popup = this._popup;
 
9756                         this.removeLayer(popup);
 
9764  * @section Popup methods example
 
9766  * All layers share a set of methods convenient for binding popups to it.
 
9769  * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
 
9770  * layer.openPopup();
 
9771  * layer.closePopup();
 
9774  * 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.
 
9777 // @section Popup methods
 
9780         // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
 
9781         // Binds a popup to the layer with the passed `content` and sets up the
 
9782         // necessary event listeners. If a `Function` is passed it will receive
 
9783         // the layer as the first argument and should return a `String` or `HTMLElement`.
 
9784         bindPopup: function (content, options) {
 
9786                 if (content instanceof Popup) {
 
9787                         setOptions(content, options);
 
9788                         this._popup = content;
 
9789                         content._source = this;
 
9791                         if (!this._popup || options) {
 
9792                                 this._popup = new Popup(options, this);
 
9794                         this._popup.setContent(content);
 
9797                 if (!this._popupHandlersAdded) {
 
9799                                 click: this._openPopup,
 
9800                                 keypress: this._onKeyPress,
 
9801                                 remove: this.closePopup,
 
9802                                 move: this._movePopup
 
9804                         this._popupHandlersAdded = true;
 
9810         // @method unbindPopup(): this
 
9811         // Removes the popup previously bound with `bindPopup`.
 
9812         unbindPopup: function () {
 
9815                                 click: this._openPopup,
 
9816                                 keypress: this._onKeyPress,
 
9817                                 remove: this.closePopup,
 
9818                                 move: this._movePopup
 
9820                         this._popupHandlersAdded = false;
 
9826         // @method openPopup(latlng?: LatLng): this
 
9827         // Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
 
9828         openPopup: function (layer, latlng) {
 
9829                 if (!(layer instanceof Layer)) {
 
9834                 if (layer instanceof FeatureGroup) {
 
9835                         for (var id in this._layers) {
 
9836                                 layer = this._layers[id];
 
9842                         latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
 
9845                 if (this._popup && this._map) {
 
9846                         // set popup source to this layer
 
9847                         this._popup._source = layer;
 
9849                         // update the popup (content, layout, ect...)
 
9850                         this._popup.update();
 
9852                         // open the popup on the map
 
9853                         this._map.openPopup(this._popup, latlng);
 
9859         // @method closePopup(): this
 
9860         // Closes the popup bound to this layer if it is open.
 
9861         closePopup: function () {
 
9863                         this._popup._close();
 
9868         // @method togglePopup(): this
 
9869         // Opens or closes the popup bound to this layer depending on its current state.
 
9870         togglePopup: function (target) {
 
9872                         if (this._popup._map) {
 
9875                                 this.openPopup(target);
 
9881         // @method isPopupOpen(): boolean
 
9882         // Returns `true` if the popup bound to this layer is currently open.
 
9883         isPopupOpen: function () {
 
9884                 return (this._popup ? this._popup.isOpen() : false);
 
9887         // @method setPopupContent(content: String|HTMLElement|Popup): this
 
9888         // Sets the content of the popup bound to this layer.
 
9889         setPopupContent: function (content) {
 
9891                         this._popup.setContent(content);
 
9896         // @method getPopup(): Popup
 
9897         // Returns the popup bound to this layer.
 
9898         getPopup: function () {
 
9902         _openPopup: function (e) {
 
9903                 var layer = e.layer || e.target;
 
9913                 // prevent map click
 
9916                 // if this inherits from Path its a vector and we can just
 
9917                 // open the popup at the new location
 
9918                 if (layer instanceof Path) {
 
9919                         this.openPopup(e.layer || e.target, e.latlng);
 
9923                 // otherwise treat it like a marker and figure out
 
9924                 // if we should toggle it open/closed
 
9925                 if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
 
9928                         this.openPopup(layer, e.latlng);
 
9932         _movePopup: function (e) {
 
9933                 this._popup.setLatLng(e.latlng);
 
9936         _onKeyPress: function (e) {
 
9937                 if (e.originalEvent.keyCode === 13) {
 
9945  * @inherits DivOverlay
 
9947  * Used to display small texts on top of map layers.
 
9952  * marker.bindTooltip("my tooltip text").openTooltip();
 
9954  * Note about tooltip offset. Leaflet takes two options in consideration
 
9955  * for computing tooltip offsetting:
 
9956  * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
 
9957  *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
 
9958  *   move it to the bottom. Negatives will move to the left and top.
 
9959  * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
 
9960  *   should adapt this value if you use a custom icon.
 
9964 // @namespace Tooltip
 
9965 var Tooltip = DivOverlay.extend({
 
9968         // @aka Tooltip options
 
9970                 // @option pane: String = 'tooltipPane'
 
9971                 // `Map pane` where the tooltip will be added.
 
9972                 pane: 'tooltipPane',
 
9974                 // @option offset: Point = Point(0, 0)
 
9975                 // Optional offset of the tooltip position.
 
9978                 // @option direction: String = 'auto'
 
9979                 // Direction where to open the tooltip. Possible values are: `right`, `left`,
 
9980                 // `top`, `bottom`, `center`, `auto`.
 
9981                 // `auto` will dynamically switch between `right` and `left` according to the tooltip
 
9982                 // position on the map.
 
9985                 // @option permanent: Boolean = false
 
9986                 // Whether to open the tooltip permanently or only on mouseover.
 
9989                 // @option sticky: Boolean = false
 
9990                 // If true, the tooltip will follow the mouse instead of being fixed at the feature center.
 
9993                 // @option interactive: Boolean = false
 
9994                 // If true, the tooltip will listen to the feature events.
 
9997                 // @option opacity: Number = 0.9
 
9998                 // Tooltip container opacity.
 
10002         onAdd: function (map) {
 
10003                 DivOverlay.prototype.onAdd.call(this, map);
 
10004                 this.setOpacity(this.options.opacity);
 
10007                 // @section Tooltip events
 
10008                 // @event tooltipopen: TooltipEvent
 
10009                 // Fired when a tooltip is opened in the map.
 
10010                 map.fire('tooltipopen', {tooltip: this});
 
10012                 if (this._source) {
 
10013                         // @namespace Layer
 
10014                         // @section Tooltip events
 
10015                         // @event tooltipopen: TooltipEvent
 
10016                         // Fired when a tooltip bound to this layer is opened.
 
10017                         this._source.fire('tooltipopen', {tooltip: this}, true);
 
10021         onRemove: function (map) {
 
10022                 DivOverlay.prototype.onRemove.call(this, map);
 
10025                 // @section Tooltip events
 
10026                 // @event tooltipclose: TooltipEvent
 
10027                 // Fired when a tooltip in the map is closed.
 
10028                 map.fire('tooltipclose', {tooltip: this});
 
10030                 if (this._source) {
 
10031                         // @namespace Layer
 
10032                         // @section Tooltip events
 
10033                         // @event tooltipclose: TooltipEvent
 
10034                         // Fired when a tooltip bound to this layer is closed.
 
10035                         this._source.fire('tooltipclose', {tooltip: this}, true);
 
10039         getEvents: function () {
 
10040                 var events = DivOverlay.prototype.getEvents.call(this);
 
10042                 if (touch && !this.options.permanent) {
 
10043                         events.preclick = this._close;
 
10049         _close: function () {
 
10051                         this._map.closeTooltip(this);
 
10055         _initLayout: function () {
 
10056                 var prefix = 'leaflet-tooltip',
 
10057                     className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
 
10059                 this._contentNode = this._container = create$1('div', className);
 
10062         _updateLayout: function () {},
 
10064         _adjustPan: function () {},
 
10066         _setPosition: function (pos) {
 
10067                 var map = this._map,
 
10068                     container = this._container,
 
10069                     centerPoint = map.latLngToContainerPoint(map.getCenter()),
 
10070                     tooltipPoint = map.layerPointToContainerPoint(pos),
 
10071                     direction = this.options.direction,
 
10072                     tooltipWidth = container.offsetWidth,
 
10073                     tooltipHeight = container.offsetHeight,
 
10074                     offset = toPoint(this.options.offset),
 
10075                     anchor = this._getAnchor();
 
10077                 if (direction === 'top') {
 
10078                         pos = pos.add(toPoint(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true));
 
10079                 } else if (direction === 'bottom') {
 
10080                         pos = pos.subtract(toPoint(tooltipWidth / 2 - offset.x, -offset.y, true));
 
10081                 } else if (direction === 'center') {
 
10082                         pos = pos.subtract(toPoint(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true));
 
10083                 } else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
 
10084                         direction = 'right';
 
10085                         pos = pos.add(toPoint(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true));
 
10087                         direction = 'left';
 
10088                         pos = pos.subtract(toPoint(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true));
 
10091                 removeClass(container, 'leaflet-tooltip-right');
 
10092                 removeClass(container, 'leaflet-tooltip-left');
 
10093                 removeClass(container, 'leaflet-tooltip-top');
 
10094                 removeClass(container, 'leaflet-tooltip-bottom');
 
10095                 addClass(container, 'leaflet-tooltip-' + direction);
 
10096                 setPosition(container, pos);
 
10099         _updatePosition: function () {
 
10100                 var pos = this._map.latLngToLayerPoint(this._latlng);
 
10101                 this._setPosition(pos);
 
10104         setOpacity: function (opacity) {
 
10105                 this.options.opacity = opacity;
 
10107                 if (this._container) {
 
10108                         setOpacity(this._container, opacity);
 
10112         _animateZoom: function (e) {
 
10113                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
 
10114                 this._setPosition(pos);
 
10117         _getAnchor: function () {
 
10118                 // Where should we anchor the tooltip on the source layer?
 
10119                 return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
 
10124 // @namespace Tooltip
 
10125 // @factory L.tooltip(options?: Tooltip options, source?: Layer)
 
10126 // 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.
 
10127 var tooltip = function (options, source) {
 
10128         return new Tooltip(options, source);
 
10132 // @section Methods for Layers and Controls
 
10135         // @method openTooltip(tooltip: Tooltip): this
 
10136         // Opens the specified tooltip.
 
10138         // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
 
10139         // Creates a tooltip with the specified content and options and open it.
 
10140         openTooltip: function (tooltip, latlng, options) {
 
10141                 if (!(tooltip instanceof Tooltip)) {
 
10142                         tooltip = new Tooltip(options).setContent(tooltip);
 
10146                         tooltip.setLatLng(latlng);
 
10149                 if (this.hasLayer(tooltip)) {
 
10153                 return this.addLayer(tooltip);
 
10156         // @method closeTooltip(tooltip?: Tooltip): this
 
10157         // Closes the tooltip given as parameter.
 
10158         closeTooltip: function (tooltip) {
 
10160                         this.removeLayer(tooltip);
 
10169  * @section Tooltip methods example
 
10171  * All layers share a set of methods convenient for binding tooltips to it.
 
10174  * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
 
10175  * layer.openTooltip();
 
10176  * layer.closeTooltip();
 
10180 // @section Tooltip methods
 
10183         // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
 
10184         // Binds a tooltip to the layer with the passed `content` and sets up the
 
10185         // necessary event listeners. If a `Function` is passed it will receive
 
10186         // the layer as the first argument and should return a `String` or `HTMLElement`.
 
10187         bindTooltip: function (content, options) {
 
10189                 if (content instanceof Tooltip) {
 
10190                         setOptions(content, options);
 
10191                         this._tooltip = content;
 
10192                         content._source = this;
 
10194                         if (!this._tooltip || options) {
 
10195                                 this._tooltip = new Tooltip(options, this);
 
10197                         this._tooltip.setContent(content);
 
10201                 this._initTooltipInteractions();
 
10203                 if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
 
10204                         this.openTooltip();
 
10210         // @method unbindTooltip(): this
 
10211         // Removes the tooltip previously bound with `bindTooltip`.
 
10212         unbindTooltip: function () {
 
10213                 if (this._tooltip) {
 
10214                         this._initTooltipInteractions(true);
 
10215                         this.closeTooltip();
 
10216                         this._tooltip = null;
 
10221         _initTooltipInteractions: function (remove$$1) {
 
10222                 if (!remove$$1 && this._tooltipHandlersAdded) { return; }
 
10223                 var onOff = remove$$1 ? 'off' : 'on',
 
10225                         remove: this.closeTooltip,
 
10226                         move: this._moveTooltip
 
10228                 if (!this._tooltip.options.permanent) {
 
10229                         events.mouseover = this._openTooltip;
 
10230                         events.mouseout = this.closeTooltip;
 
10231                         if (this._tooltip.options.sticky) {
 
10232                                 events.mousemove = this._moveTooltip;
 
10235                                 events.click = this._openTooltip;
 
10238                         events.add = this._openTooltip;
 
10240                 this[onOff](events);
 
10241                 this._tooltipHandlersAdded = !remove$$1;
 
10244         // @method openTooltip(latlng?: LatLng): this
 
10245         // Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
 
10246         openTooltip: function (layer, latlng) {
 
10247                 if (!(layer instanceof Layer)) {
 
10252                 if (layer instanceof FeatureGroup) {
 
10253                         for (var id in this._layers) {
 
10254                                 layer = this._layers[id];
 
10260                         latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
 
10263                 if (this._tooltip && this._map) {
 
10265                         // set tooltip source to this layer
 
10266                         this._tooltip._source = layer;
 
10268                         // update the tooltip (content, layout, ect...)
 
10269                         this._tooltip.update();
 
10271                         // open the tooltip on the map
 
10272                         this._map.openTooltip(this._tooltip, latlng);
 
10274                         // Tooltip container may not be defined if not permanent and never
 
10276                         if (this._tooltip.options.interactive && this._tooltip._container) {
 
10277                                 addClass(this._tooltip._container, 'leaflet-clickable');
 
10278                                 this.addInteractiveTarget(this._tooltip._container);
 
10285         // @method closeTooltip(): this
 
10286         // Closes the tooltip bound to this layer if it is open.
 
10287         closeTooltip: function () {
 
10288                 if (this._tooltip) {
 
10289                         this._tooltip._close();
 
10290                         if (this._tooltip.options.interactive && this._tooltip._container) {
 
10291                                 removeClass(this._tooltip._container, 'leaflet-clickable');
 
10292                                 this.removeInteractiveTarget(this._tooltip._container);
 
10298         // @method toggleTooltip(): this
 
10299         // Opens or closes the tooltip bound to this layer depending on its current state.
 
10300         toggleTooltip: function (target) {
 
10301                 if (this._tooltip) {
 
10302                         if (this._tooltip._map) {
 
10303                                 this.closeTooltip();
 
10305                                 this.openTooltip(target);
 
10311         // @method isTooltipOpen(): boolean
 
10312         // Returns `true` if the tooltip bound to this layer is currently open.
 
10313         isTooltipOpen: function () {
 
10314                 return this._tooltip.isOpen();
 
10317         // @method setTooltipContent(content: String|HTMLElement|Tooltip): this
 
10318         // Sets the content of the tooltip bound to this layer.
 
10319         setTooltipContent: function (content) {
 
10320                 if (this._tooltip) {
 
10321                         this._tooltip.setContent(content);
 
10326         // @method getTooltip(): Tooltip
 
10327         // Returns the tooltip bound to this layer.
 
10328         getTooltip: function () {
 
10329                 return this._tooltip;
 
10332         _openTooltip: function (e) {
 
10333                 var layer = e.layer || e.target;
 
10335                 if (!this._tooltip || !this._map) {
 
10338                 this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
 
10341         _moveTooltip: function (e) {
 
10342                 var latlng = e.latlng, containerPoint, layerPoint;
 
10343                 if (this._tooltip.options.sticky && e.originalEvent) {
 
10344                         containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
 
10345                         layerPoint = this._map.containerPointToLayerPoint(containerPoint);
 
10346                         latlng = this._map.layerPointToLatLng(layerPoint);
 
10348                 this._tooltip.setLatLng(latlng);
 
10357  * Represents a lightweight icon for markers that uses a simple `<div>`
 
10358  * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
 
10362  * var myIcon = L.divIcon({className: 'my-div-icon'});
 
10363  * // you can set .my-div-icon styles in CSS
 
10365  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
 
10368  * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
 
10371 var DivIcon = Icon.extend({
 
10374                 // @aka DivIcon options
 
10375                 iconSize: [12, 12], // also can be set through CSS
 
10377                 // iconAnchor: (Point),
 
10378                 // popupAnchor: (Point),
 
10380                 // @option html: String = ''
 
10381                 // Custom HTML code to put inside the div element, empty by default.
 
10384                 // @option bgPos: Point = [0, 0]
 
10385                 // Optional relative position of the background, in pixels
 
10388                 className: 'leaflet-div-icon'
 
10391         createIcon: function (oldIcon) {
 
10392                 var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
 
10393                     options = this.options;
 
10395                 div.innerHTML = options.html !== false ? options.html : '';
 
10397                 if (options.bgPos) {
 
10398                         var bgPos = toPoint(options.bgPos);
 
10399                         div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
 
10401                 this._setIconStyles(div, 'icon');
 
10406         createShadow: function () {
 
10411 // @factory L.divIcon(options: DivIcon options)
 
10412 // Creates a `DivIcon` instance with the given options.
 
10413 function divIcon(options) {
 
10414         return new DivIcon(options);
 
10417 Icon.Default = IconDefault;
 
10424  * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
 
10425  * 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.
 
10428  * @section Synchronous usage
 
10431  * 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.
 
10434  * var CanvasLayer = L.GridLayer.extend({
 
10435  *     createTile: function(coords){
 
10436  *         // create a <canvas> element for drawing
 
10437  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
10439  *         // setup tile width and height according to the options
 
10440  *         var size = this.getTileSize();
 
10441  *         tile.width = size.x;
 
10442  *         tile.height = size.y;
 
10444  *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
 
10445  *         var ctx = tile.getContext('2d');
 
10447  *         // return the tile so it can be rendered on screen
 
10453  * @section Asynchronous usage
 
10456  * 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.
 
10459  * var CanvasLayer = L.GridLayer.extend({
 
10460  *     createTile: function(coords, done){
 
10463  *         // create a <canvas> element for drawing
 
10464  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
10466  *         // setup tile width and height according to the options
 
10467  *         var size = this.getTileSize();
 
10468  *         tile.width = size.x;
 
10469  *         tile.height = size.y;
 
10471  *         // draw something asynchronously and pass the tile to the done() callback
 
10472  *         setTimeout(function() {
 
10473  *             done(error, tile);
 
10485 var GridLayer = Layer.extend({
 
10488         // @aka GridLayer options
 
10490                 // @option tileSize: Number|Point = 256
 
10491                 // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
 
10494                 // @option opacity: Number = 1.0
 
10495                 // Opacity of the tiles. Can be used in the `createTile()` function.
 
10498                 // @option updateWhenIdle: Boolean = (depends)
 
10499                 // Load new tiles only when panning ends.
 
10500                 // `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation.
 
10501                 // `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the
 
10502                 // [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers.
 
10503                 updateWhenIdle: mobile,
 
10505                 // @option updateWhenZooming: Boolean = true
 
10506                 // 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.
 
10507                 updateWhenZooming: true,
 
10509                 // @option updateInterval: Number = 200
 
10510                 // Tiles will not update more than once every `updateInterval` milliseconds when panning.
 
10511                 updateInterval: 200,
 
10513                 // @option zIndex: Number = 1
 
10514                 // The explicit zIndex of the tile layer.
 
10517                 // @option bounds: LatLngBounds = undefined
 
10518                 // If set, tiles will only be loaded inside the set `LatLngBounds`.
 
10521                 // @option minZoom: Number = 0
 
10522                 // The minimum zoom level down to which this layer will be displayed (inclusive).
 
10525                 // @option maxZoom: Number = undefined
 
10526                 // The maximum zoom level up to which this layer will be displayed (inclusive).
 
10527                 maxZoom: undefined,
 
10529                 // @option maxNativeZoom: Number = undefined
 
10530                 // Maximum zoom number the tile source has available. If it is specified,
 
10531                 // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
 
10532                 // from `maxNativeZoom` level and auto-scaled.
 
10533                 maxNativeZoom: undefined,
 
10535                 // @option minNativeZoom: Number = undefined
 
10536                 // Minimum zoom number the tile source has available. If it is specified,
 
10537                 // the tiles on all zoom levels lower than `minNativeZoom` will be loaded
 
10538                 // from `minNativeZoom` level and auto-scaled.
 
10539                 minNativeZoom: undefined,
 
10541                 // @option noWrap: Boolean = false
 
10542                 // Whether the layer is wrapped around the antimeridian. If `true`, the
 
10543                 // GridLayer will only be displayed once at low zoom levels. Has no
 
10544                 // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
 
10545                 // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
 
10546                 // tiles outside the CRS limits.
 
10549                 // @option pane: String = 'tilePane'
 
10550                 // `Map pane` where the grid layer will be added.
 
10553                 // @option className: String = ''
 
10554                 // A custom class name to assign to the tile layer. Empty by default.
 
10557                 // @option keepBuffer: Number = 2
 
10558                 // When panning the map, keep this many rows and columns of tiles before unloading them.
 
10562         initialize: function (options) {
 
10563                 setOptions(this, options);
 
10566         onAdd: function () {
 
10567                 this._initContainer();
 
10576         beforeAdd: function (map) {
 
10577                 map._addZoomLimit(this);
 
10580         onRemove: function (map) {
 
10581                 this._removeAllTiles();
 
10582                 remove(this._container);
 
10583                 map._removeZoomLimit(this);
 
10584                 this._container = null;
 
10585                 this._tileZoom = undefined;
 
10588         // @method bringToFront: this
 
10589         // Brings the tile layer to the top of all tile layers.
 
10590         bringToFront: function () {
 
10592                         toFront(this._container);
 
10593                         this._setAutoZIndex(Math.max);
 
10598         // @method bringToBack: this
 
10599         // Brings the tile layer to the bottom of all tile layers.
 
10600         bringToBack: function () {
 
10602                         toBack(this._container);
 
10603                         this._setAutoZIndex(Math.min);
 
10608         // @method getContainer: HTMLElement
 
10609         // Returns the HTML element that contains the tiles for this layer.
 
10610         getContainer: function () {
 
10611                 return this._container;
 
10614         // @method setOpacity(opacity: Number): this
 
10615         // Changes the [opacity](#gridlayer-opacity) of the grid layer.
 
10616         setOpacity: function (opacity) {
 
10617                 this.options.opacity = opacity;
 
10618                 this._updateOpacity();
 
10622         // @method setZIndex(zIndex: Number): this
 
10623         // Changes the [zIndex](#gridlayer-zindex) of the grid layer.
 
10624         setZIndex: function (zIndex) {
 
10625                 this.options.zIndex = zIndex;
 
10626                 this._updateZIndex();
 
10631         // @method isLoading: Boolean
 
10632         // Returns `true` if any tile in the grid layer has not finished loading.
 
10633         isLoading: function () {
 
10634                 return this._loading;
 
10637         // @method redraw: this
 
10638         // Causes the layer to clear all the tiles and request them again.
 
10639         redraw: function () {
 
10641                         this._removeAllTiles();
 
10647         getEvents: function () {
 
10649                         viewprereset: this._invalidateAll,
 
10650                         viewreset: this._resetView,
 
10651                         zoom: this._resetView,
 
10652                         moveend: this._onMoveEnd
 
10655                 if (!this.options.updateWhenIdle) {
 
10656                         // update tiles on move, but not more often than once per given interval
 
10657                         if (!this._onMove) {
 
10658                                 this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
 
10661                         events.move = this._onMove;
 
10664                 if (this._zoomAnimated) {
 
10665                         events.zoomanim = this._animateZoom;
 
10671         // @section Extension methods
 
10672         // Layers extending `GridLayer` shall reimplement the following method.
 
10673         // @method createTile(coords: Object, done?: Function): HTMLElement
 
10674         // Called only internally, must be overridden by classes extending `GridLayer`.
 
10675         // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
 
10676         // is specified, it must be called when the tile has finished loading and drawing.
 
10677         createTile: function () {
 
10678                 return document.createElement('div');
 
10682         // @method getTileSize: Point
 
10683         // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
 
10684         getTileSize: function () {
 
10685                 var s = this.options.tileSize;
 
10686                 return s instanceof Point ? s : new Point(s, s);
 
10689         _updateZIndex: function () {
 
10690                 if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
 
10691                         this._container.style.zIndex = this.options.zIndex;
 
10695         _setAutoZIndex: function (compare) {
 
10696                 // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
 
10698                 var layers = this.getPane().children,
 
10699                     edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
 
10701                 for (var i = 0, len = layers.length, zIndex; i < len; i++) {
 
10703                         zIndex = layers[i].style.zIndex;
 
10705                         if (layers[i] !== this._container && zIndex) {
 
10706                                 edgeZIndex = compare(edgeZIndex, +zIndex);
 
10710                 if (isFinite(edgeZIndex)) {
 
10711                         this.options.zIndex = edgeZIndex + compare(-1, 1);
 
10712                         this._updateZIndex();
 
10716         _updateOpacity: function () {
 
10717                 if (!this._map) { return; }
 
10719                 // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
 
10720                 if (ielt9) { return; }
 
10722                 setOpacity(this._container, this.options.opacity);
 
10724                 var now = +new Date(),
 
10728                 for (var key in this._tiles) {
 
10729                         var tile = this._tiles[key];
 
10730                         if (!tile.current || !tile.loaded) { continue; }
 
10732                         var fade = Math.min(1, (now - tile.loaded) / 200);
 
10734                         setOpacity(tile.el, fade);
 
10741                                         this._onOpaqueTile(tile);
 
10743                                 tile.active = true;
 
10747                 if (willPrune && !this._noPrune) { this._pruneTiles(); }
 
10750                         cancelAnimFrame(this._fadeFrame);
 
10751                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
 
10755         _onOpaqueTile: falseFn,
 
10757         _initContainer: function () {
 
10758                 if (this._container) { return; }
 
10760                 this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
 
10761                 this._updateZIndex();
 
10763                 if (this.options.opacity < 1) {
 
10764                         this._updateOpacity();
 
10767                 this.getPane().appendChild(this._container);
 
10770         _updateLevels: function () {
 
10772                 var zoom = this._tileZoom,
 
10773                     maxZoom = this.options.maxZoom;
 
10775                 if (zoom === undefined) { return undefined; }
 
10777                 for (var z in this._levels) {
 
10778                         if (this._levels[z].el.children.length || z === zoom) {
 
10779                                 this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
 
10780                                 this._onUpdateLevel(z);
 
10782                                 remove(this._levels[z].el);
 
10783                                 this._removeTilesAtZoom(z);
 
10784                                 this._onRemoveLevel(z);
 
10785                                 delete this._levels[z];
 
10789                 var level = this._levels[zoom],
 
10793                         level = this._levels[zoom] = {};
 
10795                         level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
 
10796                         level.el.style.zIndex = maxZoom;
 
10798                         level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
 
10801                         this._setZoomTransform(level, map.getCenter(), map.getZoom());
 
10803                         // force the browser to consider the newly added element for transition
 
10804                         falseFn(level.el.offsetWidth);
 
10806                         this._onCreateLevel(level);
 
10809                 this._level = level;
 
10814         _onUpdateLevel: falseFn,
 
10816         _onRemoveLevel: falseFn,
 
10818         _onCreateLevel: falseFn,
 
10820         _pruneTiles: function () {
 
10827                 var zoom = this._map.getZoom();
 
10828                 if (zoom > this.options.maxZoom ||
 
10829                         zoom < this.options.minZoom) {
 
10830                         this._removeAllTiles();
 
10834                 for (key in this._tiles) {
 
10835                         tile = this._tiles[key];
 
10836                         tile.retain = tile.current;
 
10839                 for (key in this._tiles) {
 
10840                         tile = this._tiles[key];
 
10841                         if (tile.current && !tile.active) {
 
10842                                 var coords = tile.coords;
 
10843                                 if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
 
10844                                         this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
 
10849                 for (key in this._tiles) {
 
10850                         if (!this._tiles[key].retain) {
 
10851                                 this._removeTile(key);
 
10856         _removeTilesAtZoom: function (zoom) {
 
10857                 for (var key in this._tiles) {
 
10858                         if (this._tiles[key].coords.z !== zoom) {
 
10861                         this._removeTile(key);
 
10865         _removeAllTiles: function () {
 
10866                 for (var key in this._tiles) {
 
10867                         this._removeTile(key);
 
10871         _invalidateAll: function () {
 
10872                 for (var z in this._levels) {
 
10873                         remove(this._levels[z].el);
 
10874                         this._onRemoveLevel(z);
 
10875                         delete this._levels[z];
 
10877                 this._removeAllTiles();
 
10879                 this._tileZoom = undefined;
 
10882         _retainParent: function (x, y, z, minZoom) {
 
10883                 var x2 = Math.floor(x / 2),
 
10884                     y2 = Math.floor(y / 2),
 
10886                     coords2 = new Point(+x2, +y2);
 
10889                 var key = this._tileCoordsToKey(coords2),
 
10890                     tile = this._tiles[key];
 
10892                 if (tile && tile.active) {
 
10893                         tile.retain = true;
 
10896                 } else if (tile && tile.loaded) {
 
10897                         tile.retain = true;
 
10900                 if (z2 > minZoom) {
 
10901                         return this._retainParent(x2, y2, z2, minZoom);
 
10907         _retainChildren: function (x, y, z, maxZoom) {
 
10909                 for (var i = 2 * x; i < 2 * x + 2; i++) {
 
10910                         for (var j = 2 * y; j < 2 * y + 2; j++) {
 
10912                                 var coords = new Point(i, j);
 
10915                                 var key = this._tileCoordsToKey(coords),
 
10916                                     tile = this._tiles[key];
 
10918                                 if (tile && tile.active) {
 
10919                                         tile.retain = true;
 
10922                                 } else if (tile && tile.loaded) {
 
10923                                         tile.retain = true;
 
10926                                 if (z + 1 < maxZoom) {
 
10927                                         this._retainChildren(i, j, z + 1, maxZoom);
 
10933         _resetView: function (e) {
 
10934                 var animating = e && (e.pinch || e.flyTo);
 
10935                 this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
 
10938         _animateZoom: function (e) {
 
10939                 this._setView(e.center, e.zoom, true, e.noUpdate);
 
10942         _clampZoom: function (zoom) {
 
10943                 var options = this.options;
 
10945                 if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
 
10946                         return options.minNativeZoom;
 
10949                 if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
 
10950                         return options.maxNativeZoom;
 
10956         _setView: function (center, zoom, noPrune, noUpdate) {
 
10957                 var tileZoom = this._clampZoom(Math.round(zoom));
 
10958                 if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
 
10959                     (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
 
10960                         tileZoom = undefined;
 
10963                 var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
 
10965                 if (!noUpdate || tileZoomChanged) {
 
10967                         this._tileZoom = tileZoom;
 
10969                         if (this._abortLoading) {
 
10970                                 this._abortLoading();
 
10973                         this._updateLevels();
 
10976                         if (tileZoom !== undefined) {
 
10977                                 this._update(center);
 
10981                                 this._pruneTiles();
 
10984                         // Flag to prevent _updateOpacity from pruning tiles during
 
10985                         // a zoom anim or a pinch gesture
 
10986                         this._noPrune = !!noPrune;
 
10989                 this._setZoomTransforms(center, zoom);
 
10992         _setZoomTransforms: function (center, zoom) {
 
10993                 for (var i in this._levels) {
 
10994                         this._setZoomTransform(this._levels[i], center, zoom);
 
10998         _setZoomTransform: function (level, center, zoom) {
 
10999                 var scale = this._map.getZoomScale(zoom, level.zoom),
 
11000                     translate = level.origin.multiplyBy(scale)
 
11001                         .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
 
11004                         setTransform(level.el, translate, scale);
 
11006                         setPosition(level.el, translate);
 
11010         _resetGrid: function () {
 
11011                 var map = this._map,
 
11012                     crs = map.options.crs,
 
11013                     tileSize = this._tileSize = this.getTileSize(),
 
11014                     tileZoom = this._tileZoom;
 
11016                 var bounds = this._map.getPixelWorldBounds(this._tileZoom);
 
11018                         this._globalTileRange = this._pxBoundsToTileRange(bounds);
 
11021                 this._wrapX = crs.wrapLng && !this.options.noWrap && [
 
11022                         Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
 
11023                         Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
 
11025                 this._wrapY = crs.wrapLat && !this.options.noWrap && [
 
11026                         Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
 
11027                         Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
 
11031         _onMoveEnd: function () {
 
11032                 if (!this._map || this._map._animatingZoom) { return; }
 
11037         _getTiledPixelBounds: function (center) {
 
11038                 var map = this._map,
 
11039                     mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
 
11040                     scale = map.getZoomScale(mapZoom, this._tileZoom),
 
11041                     pixelCenter = map.project(center, this._tileZoom).floor(),
 
11042                     halfSize = map.getSize().divideBy(scale * 2);
 
11044                 return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
 
11047         // Private method to load tiles in the grid's active zoom level according to map bounds
 
11048         _update: function (center) {
 
11049                 var map = this._map;
 
11050                 if (!map) { return; }
 
11051                 var zoom = this._clampZoom(map.getZoom());
 
11053                 if (center === undefined) { center = map.getCenter(); }
 
11054                 if (this._tileZoom === undefined) { return; }   // if out of minzoom/maxzoom
 
11056                 var pixelBounds = this._getTiledPixelBounds(center),
 
11057                     tileRange = this._pxBoundsToTileRange(pixelBounds),
 
11058                     tileCenter = tileRange.getCenter(),
 
11060                     margin = this.options.keepBuffer,
 
11061                     noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
 
11062                                               tileRange.getTopRight().add([margin, -margin]));
 
11064                 // Sanity check: panic if the tile range contains Infinity somewhere.
 
11065                 if (!(isFinite(tileRange.min.x) &&
 
11066                       isFinite(tileRange.min.y) &&
 
11067                       isFinite(tileRange.max.x) &&
 
11068                       isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
 
11070                 for (var key in this._tiles) {
 
11071                         var c = this._tiles[key].coords;
 
11072                         if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
 
11073                                 this._tiles[key].current = false;
 
11077                 // _update just loads more tiles. If the tile zoom level differs too much
 
11078                 // from the map's, let _setView reset levels and prune old tiles.
 
11079                 if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
 
11081                 // create a queue of coordinates to load tiles from
 
11082                 for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
 
11083                         for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
 
11084                                 var coords = new Point(i, j);
 
11085                                 coords.z = this._tileZoom;
 
11087                                 if (!this._isValidTile(coords)) { continue; }
 
11089                                 var tile = this._tiles[this._tileCoordsToKey(coords)];
 
11091                                         tile.current = true;
 
11093                                         queue.push(coords);
 
11098                 // sort tile queue to load tiles in order of their distance to center
 
11099                 queue.sort(function (a, b) {
 
11100                         return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
 
11103                 if (queue.length !== 0) {
 
11104                         // if it's the first batch of tiles to load
 
11105                         if (!this._loading) {
 
11106                                 this._loading = true;
 
11107                                 // @event loading: Event
 
11108                                 // Fired when the grid layer starts loading tiles.
 
11109                                 this.fire('loading');
 
11112                         // create DOM fragment to append tiles in one batch
 
11113                         var fragment = document.createDocumentFragment();
 
11115                         for (i = 0; i < queue.length; i++) {
 
11116                                 this._addTile(queue[i], fragment);
 
11119                         this._level.el.appendChild(fragment);
 
11123         _isValidTile: function (coords) {
 
11124                 var crs = this._map.options.crs;
 
11126                 if (!crs.infinite) {
 
11127                         // don't load tile if it's out of bounds and not wrapped
 
11128                         var bounds = this._globalTileRange;
 
11129                         if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
 
11130                             (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
 
11133                 if (!this.options.bounds) { return true; }
 
11135                 // don't load tile if it doesn't intersect the bounds in options
 
11136                 var tileBounds = this._tileCoordsToBounds(coords);
 
11137                 return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
 
11140         _keyToBounds: function (key) {
 
11141                 return this._tileCoordsToBounds(this._keyToTileCoords(key));
 
11144         _tileCoordsToNwSe: function (coords) {
 
11145                 var map = this._map,
 
11146                     tileSize = this.getTileSize(),
 
11147                     nwPoint = coords.scaleBy(tileSize),
 
11148                     sePoint = nwPoint.add(tileSize),
 
11149                     nw = map.unproject(nwPoint, coords.z),
 
11150                     se = map.unproject(sePoint, coords.z);
 
11154         // converts tile coordinates to its geographical bounds
 
11155         _tileCoordsToBounds: function (coords) {
 
11156                 var bp = this._tileCoordsToNwSe(coords),
 
11157                     bounds = new LatLngBounds(bp[0], bp[1]);
 
11159                 if (!this.options.noWrap) {
 
11160                         bounds = this._map.wrapLatLngBounds(bounds);
 
11164         // converts tile coordinates to key for the tile cache
 
11165         _tileCoordsToKey: function (coords) {
 
11166                 return coords.x + ':' + coords.y + ':' + coords.z;
 
11169         // converts tile cache key to coordinates
 
11170         _keyToTileCoords: function (key) {
 
11171                 var k = key.split(':'),
 
11172                     coords = new Point(+k[0], +k[1]);
 
11177         _removeTile: function (key) {
 
11178                 var tile = this._tiles[key];
 
11179                 if (!tile) { return; }
 
11181                 // Cancels any pending http requests associated with the tile
 
11182                 // unless we're on Android's stock browser,
 
11183                 // see https://github.com/Leaflet/Leaflet/issues/137
 
11184                 if (!androidStock) {
 
11185                         tile.el.setAttribute('src', emptyImageUrl);
 
11189                 delete this._tiles[key];
 
11191                 // @event tileunload: TileEvent
 
11192                 // Fired when a tile is removed (e.g. when a tile goes off the screen).
 
11193                 this.fire('tileunload', {
 
11195                         coords: this._keyToTileCoords(key)
 
11199         _initTile: function (tile) {
 
11200                 addClass(tile, 'leaflet-tile');
 
11202                 var tileSize = this.getTileSize();
 
11203                 tile.style.width = tileSize.x + 'px';
 
11204                 tile.style.height = tileSize.y + 'px';
 
11206                 tile.onselectstart = falseFn;
 
11207                 tile.onmousemove = falseFn;
 
11209                 // update opacity on tiles in IE7-8 because of filter inheritance problems
 
11210                 if (ielt9 && this.options.opacity < 1) {
 
11211                         setOpacity(tile, this.options.opacity);
 
11214                 // without this hack, tiles disappear after zoom on Chrome for Android
 
11215                 // https://github.com/Leaflet/Leaflet/issues/2078
 
11216                 if (android && !android23) {
 
11217                         tile.style.WebkitBackfaceVisibility = 'hidden';
 
11221         _addTile: function (coords, container) {
 
11222                 var tilePos = this._getTilePos(coords),
 
11223                     key = this._tileCoordsToKey(coords);
 
11225                 var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
 
11227                 this._initTile(tile);
 
11229                 // if createTile is defined with a second argument ("done" callback),
 
11230                 // we know that tile is async and will be ready later; otherwise
 
11231                 if (this.createTile.length < 2) {
 
11232                         // mark tile as ready, but delay one frame for opacity animation to happen
 
11233                         requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
 
11236                 setPosition(tile, tilePos);
 
11238                 // save tile in cache
 
11239                 this._tiles[key] = {
 
11245                 container.appendChild(tile);
 
11246                 // @event tileloadstart: TileEvent
 
11247                 // Fired when a tile is requested and starts loading.
 
11248                 this.fire('tileloadstart', {
 
11254         _tileReady: function (coords, err, tile) {
 
11255                 if (!this._map) { return; }
 
11258                         // @event tileerror: TileErrorEvent
 
11259                         // Fired when there is an error loading a tile.
 
11260                         this.fire('tileerror', {
 
11267                 var key = this._tileCoordsToKey(coords);
 
11269                 tile = this._tiles[key];
 
11270                 if (!tile) { return; }
 
11272                 tile.loaded = +new Date();
 
11273                 if (this._map._fadeAnimated) {
 
11274                         setOpacity(tile.el, 0);
 
11275                         cancelAnimFrame(this._fadeFrame);
 
11276                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
 
11278                         tile.active = true;
 
11279                         this._pruneTiles();
 
11283                         addClass(tile.el, 'leaflet-tile-loaded');
 
11285                         // @event tileload: TileEvent
 
11286                         // Fired when a tile loads.
 
11287                         this.fire('tileload', {
 
11293                 if (this._noTilesToLoad()) {
 
11294                         this._loading = false;
 
11295                         // @event load: Event
 
11296                         // Fired when the grid layer loaded all visible tiles.
 
11299                         if (ielt9 || !this._map._fadeAnimated) {
 
11300                                 requestAnimFrame(this._pruneTiles, this);
 
11302                                 // Wait a bit more than 0.2 secs (the duration of the tile fade-in)
 
11303                                 // to trigger a pruning.
 
11304                                 setTimeout(bind(this._pruneTiles, this), 250);
 
11309         _getTilePos: function (coords) {
 
11310                 return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
 
11313         _wrapCoords: function (coords) {
 
11314                 var newCoords = new Point(
 
11315                         this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
 
11316                         this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
 
11317                 newCoords.z = coords.z;
 
11321         _pxBoundsToTileRange: function (bounds) {
 
11322                 var tileSize = this.getTileSize();
 
11324                         bounds.min.unscaleBy(tileSize).floor(),
 
11325                         bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
 
11328         _noTilesToLoad: function () {
 
11329                 for (var key in this._tiles) {
 
11330                         if (!this._tiles[key].loaded) { return false; }
 
11336 // @factory L.gridLayer(options?: GridLayer options)
 
11337 // Creates a new instance of GridLayer with the supplied options.
 
11338 function gridLayer(options) {
 
11339         return new GridLayer(options);
 
11344  * @inherits GridLayer
 
11346  * Used to load and display tile layers on the map. Extends `GridLayer`.
 
11351  * L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar'}).addTo(map);
 
11354  * @section URL template
 
11357  * A string of the following form:
 
11360  * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
 
11363  * `{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.
 
11365  * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
 
11368  * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
 
11373 var TileLayer = GridLayer.extend({
 
11376         // @aka TileLayer options
 
11378                 // @option minZoom: Number = 0
 
11379                 // The minimum zoom level down to which this layer will be displayed (inclusive).
 
11382                 // @option maxZoom: Number = 18
 
11383                 // The maximum zoom level up to which this layer will be displayed (inclusive).
 
11386                 // @option subdomains: String|String[] = 'abc'
 
11387                 // 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.
 
11390                 // @option errorTileUrl: String = ''
 
11391                 // URL to the tile image to show in place of the tile that failed to load.
 
11394                 // @option zoomOffset: Number = 0
 
11395                 // The zoom number used in tile URLs will be offset with this value.
 
11398                 // @option tms: Boolean = false
 
11399                 // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
 
11402                 // @option zoomReverse: Boolean = false
 
11403                 // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
 
11404                 zoomReverse: false,
 
11406                 // @option detectRetina: Boolean = false
 
11407                 // 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.
 
11408                 detectRetina: false,
 
11410                 // @option crossOrigin: Boolean = false
 
11411                 // If true, all tiles will have their crossOrigin attribute set to ''. This is needed if you want to access tile pixel data.
 
11415         initialize: function (url, options) {
 
11419                 options = setOptions(this, options);
 
11421                 // detecting retina displays, adjusting tileSize and zoom levels
 
11422                 if (options.detectRetina && retina && options.maxZoom > 0) {
 
11424                         options.tileSize = Math.floor(options.tileSize / 2);
 
11426                         if (!options.zoomReverse) {
 
11427                                 options.zoomOffset++;
 
11430                                 options.zoomOffset--;
 
11434                         options.minZoom = Math.max(0, options.minZoom);
 
11437                 if (typeof options.subdomains === 'string') {
 
11438                         options.subdomains = options.subdomains.split('');
 
11441                 // for https://github.com/Leaflet/Leaflet/issues/137
 
11443                         this.on('tileunload', this._onTileRemove);
 
11447         // @method setUrl(url: String, noRedraw?: Boolean): this
 
11448         // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
 
11449         setUrl: function (url, noRedraw) {
 
11458         // @method createTile(coords: Object, done?: Function): HTMLElement
 
11459         // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
 
11460         // to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
 
11461         // callback is called when the tile has been loaded.
 
11462         createTile: function (coords, done) {
 
11463                 var tile = document.createElement('img');
 
11465                 on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
 
11466                 on(tile, 'error', bind(this._tileOnError, this, done, tile));
 
11468                 if (this.options.crossOrigin) {
 
11469                         tile.crossOrigin = '';
 
11473                  Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
 
11474                  http://www.w3.org/TR/WCAG20-TECHS/H67
 
11479                  Set role="presentation" to force screen readers to ignore this
 
11480                  https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
 
11482                 tile.setAttribute('role', 'presentation');
 
11484                 tile.src = this.getTileUrl(coords);
 
11489         // @section Extension methods
 
11491         // Layers extending `TileLayer` might reimplement the following method.
 
11492         // @method getTileUrl(coords: Object): String
 
11493         // Called only internally, returns the URL for a tile given its coordinates.
 
11494         // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
 
11495         getTileUrl: function (coords) {
 
11497                         r: retina ? '@2x' : '',
 
11498                         s: this._getSubdomain(coords),
 
11501                         z: this._getZoomForUrl()
 
11503                 if (this._map && !this._map.options.crs.infinite) {
 
11504                         var invertedY = this._globalTileRange.max.y - coords.y;
 
11505                         if (this.options.tms) {
 
11506                                 data['y'] = invertedY;
 
11508                         data['-y'] = invertedY;
 
11511                 return template(this._url, extend(data, this.options));
 
11514         _tileOnLoad: function (done, tile) {
 
11515                 // For https://github.com/Leaflet/Leaflet/issues/3332
 
11517                         setTimeout(bind(done, this, null, tile), 0);
 
11523         _tileOnError: function (done, tile, e) {
 
11524                 var errorUrl = this.options.errorTileUrl;
 
11525                 if (errorUrl && tile.getAttribute('src') !== errorUrl) {
 
11526                         tile.src = errorUrl;
 
11531         _onTileRemove: function (e) {
 
11532                 e.tile.onload = null;
 
11535         _getZoomForUrl: function () {
 
11536                 var zoom = this._tileZoom,
 
11537                 maxZoom = this.options.maxZoom,
 
11538                 zoomReverse = this.options.zoomReverse,
 
11539                 zoomOffset = this.options.zoomOffset;
 
11542                         zoom = maxZoom - zoom;
 
11545                 return zoom + zoomOffset;
 
11548         _getSubdomain: function (tilePoint) {
 
11549                 var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
 
11550                 return this.options.subdomains[index];
 
11553         // stops loading all tiles in the background layer
 
11554         _abortLoading: function () {
 
11556                 for (i in this._tiles) {
 
11557                         if (this._tiles[i].coords.z !== this._tileZoom) {
 
11558                                 tile = this._tiles[i].el;
 
11560                                 tile.onload = falseFn;
 
11561                                 tile.onerror = falseFn;
 
11563                                 if (!tile.complete) {
 
11564                                         tile.src = emptyImageUrl;
 
11566                                         delete this._tiles[i];
 
11574 // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
 
11575 // Instantiates a tile layer object given a `URL template` and optionally an options object.
 
11577 function tileLayer(url, options) {
 
11578         return new TileLayer(url, options);
 
11582  * @class TileLayer.WMS
 
11583  * @inherits TileLayer
 
11584  * @aka L.TileLayer.WMS
 
11585  * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
 
11590  * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
 
11591  *      layers: 'nexrad-n0r-900913',
 
11592  *      format: 'image/png',
 
11593  *      transparent: true,
 
11594  *      attribution: "Weather data © 2012 IEM Nexrad"
 
11599 var TileLayerWMS = TileLayer.extend({
 
11602         // @aka TileLayer.WMS options
 
11603         // If any custom options not documented here are used, they will be sent to the
 
11604         // WMS server as extra parameters in each request URL. This can be useful for
 
11605         // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
 
11606         defaultWmsParams: {
 
11610                 // @option layers: String = ''
 
11611                 // **(required)** Comma-separated list of WMS layers to show.
 
11614                 // @option styles: String = ''
 
11615                 // Comma-separated list of WMS styles.
 
11618                 // @option format: String = 'image/jpeg'
 
11619                 // WMS image format (use `'image/png'` for layers with transparency).
 
11620                 format: 'image/jpeg',
 
11622                 // @option transparent: Boolean = false
 
11623                 // If `true`, the WMS service will return images with transparency.
 
11624                 transparent: false,
 
11626                 // @option version: String = '1.1.1'
 
11627                 // Version of the WMS service to use
 
11632                 // @option crs: CRS = null
 
11633                 // Coordinate Reference System to use for the WMS requests, defaults to
 
11634                 // map CRS. Don't change this if you're not sure what it means.
 
11637                 // @option uppercase: Boolean = false
 
11638                 // If `true`, WMS request parameter keys will be uppercase.
 
11642         initialize: function (url, options) {
 
11646                 var wmsParams = extend({}, this.defaultWmsParams);
 
11648                 // all keys that are not TileLayer options go to WMS params
 
11649                 for (var i in options) {
 
11650                         if (!(i in this.options)) {
 
11651                                 wmsParams[i] = options[i];
 
11655                 options = setOptions(this, options);
 
11657                 var realRetina = options.detectRetina && retina ? 2 : 1;
 
11658                 var tileSize = this.getTileSize();
 
11659                 wmsParams.width = tileSize.x * realRetina;
 
11660                 wmsParams.height = tileSize.y * realRetina;
 
11662                 this.wmsParams = wmsParams;
 
11665         onAdd: function (map) {
 
11667                 this._crs = this.options.crs || map.options.crs;
 
11668                 this._wmsVersion = parseFloat(this.wmsParams.version);
 
11670                 var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
 
11671                 this.wmsParams[projectionKey] = this._crs.code;
 
11673                 TileLayer.prototype.onAdd.call(this, map);
 
11676         getTileUrl: function (coords) {
 
11678                 var tileBounds = this._tileCoordsToNwSe(coords),
 
11680                     bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])),
 
11683                     bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
 
11684                     [min.y, min.x, max.y, max.x] :
 
11685                     [min.x, min.y, max.x, max.y]).join(','),
 
11686                 url = L.TileLayer.prototype.getTileUrl.call(this, coords);
 
11688                         getParamString(this.wmsParams, url, this.options.uppercase) +
 
11689                         (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
 
11692         // @method setParams(params: Object, noRedraw?: Boolean): this
 
11693         // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
 
11694         setParams: function (params, noRedraw) {
 
11696                 extend(this.wmsParams, params);
 
11707 // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
 
11708 // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
 
11709 function tileLayerWMS(url, options) {
 
11710         return new TileLayerWMS(url, options);
 
11713 TileLayer.WMS = TileLayerWMS;
 
11714 tileLayer.wms = tileLayerWMS;
 
11721  * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
 
11722  * DOM container of the renderer, its bounds, and its zoom animation.
 
11724  * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
 
11725  * itself can be added or removed to the map. All paths use a renderer, which can
 
11726  * be implicit (the map will decide the type of renderer and use it automatically)
 
11727  * or explicit (using the [`renderer`](#path-renderer) option of the path).
 
11729  * Do not use this class directly, use `SVG` and `Canvas` instead.
 
11731  * @event update: Event
 
11732  * Fired when the renderer updates its bounds, center and zoom, for example when
 
11733  * its map has moved
 
11736 var Renderer = Layer.extend({
 
11739         // @aka Renderer options
 
11741                 // @option padding: Number = 0.1
 
11742                 // How much to extend the clip area around the map view (relative to its size)
 
11743                 // e.g. 0.1 would be 10% of map view in each direction
 
11746                 // @option tolerance: Number = 0
 
11747                 // How much to extend click tolerance round a path/object on the map
 
11751         initialize: function (options) {
 
11752                 setOptions(this, options);
 
11754                 this._layers = this._layers || {};
 
11757         onAdd: function () {
 
11758                 if (!this._container) {
 
11759                         this._initContainer(); // defined by renderer implementations
 
11761                         if (this._zoomAnimated) {
 
11762                                 addClass(this._container, 'leaflet-zoom-animated');
 
11766                 this.getPane().appendChild(this._container);
 
11768                 this.on('update', this._updatePaths, this);
 
11771         onRemove: function () {
 
11772                 this.off('update', this._updatePaths, this);
 
11773                 this._destroyContainer();
 
11776         getEvents: function () {
 
11778                         viewreset: this._reset,
 
11779                         zoom: this._onZoom,
 
11780                         moveend: this._update,
 
11781                         zoomend: this._onZoomEnd
 
11783                 if (this._zoomAnimated) {
 
11784                         events.zoomanim = this._onAnimZoom;
 
11789         _onAnimZoom: function (ev) {
 
11790                 this._updateTransform(ev.center, ev.zoom);
 
11793         _onZoom: function () {
 
11794                 this._updateTransform(this._map.getCenter(), this._map.getZoom());
 
11797         _updateTransform: function (center, zoom) {
 
11798                 var scale = this._map.getZoomScale(zoom, this._zoom),
 
11799                     position = getPosition(this._container),
 
11800                     viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
 
11801                     currentCenterPoint = this._map.project(this._center, zoom),
 
11802                     destCenterPoint = this._map.project(center, zoom),
 
11803                     centerOffset = destCenterPoint.subtract(currentCenterPoint),
 
11805                     topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
 
11808                         setTransform(this._container, topLeftOffset, scale);
 
11810                         setPosition(this._container, topLeftOffset);
 
11814         _reset: function () {
 
11816                 this._updateTransform(this._center, this._zoom);
 
11818                 for (var id in this._layers) {
 
11819                         this._layers[id]._reset();
 
11823         _onZoomEnd: function () {
 
11824                 for (var id in this._layers) {
 
11825                         this._layers[id]._project();
 
11829         _updatePaths: function () {
 
11830                 for (var id in this._layers) {
 
11831                         this._layers[id]._update();
 
11835         _update: function () {
 
11836                 // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
 
11837                 // Subclasses are responsible of firing the 'update' event.
 
11838                 var p = this.options.padding,
 
11839                     size = this._map.getSize(),
 
11840                     min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
 
11842                 this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
 
11844                 this._center = this._map.getCenter();
 
11845                 this._zoom = this._map.getZoom();
 
11851  * @inherits Renderer
 
11854  * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
 
11855  * Inherits `Renderer`.
 
11857  * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
 
11858  * available in all web browsers, notably IE8, and overlapping geometries might
 
11859  * not display properly in some edge cases.
 
11863  * Use Canvas by default for all paths in the map:
 
11866  * var map = L.map('map', {
 
11867  *      renderer: L.canvas()
 
11871  * Use a Canvas renderer with extra padding for specific vector geometries:
 
11874  * var map = L.map('map');
 
11875  * var myRenderer = L.canvas({ padding: 0.5 });
 
11876  * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
11877  * var circle = L.circle( center, { renderer: myRenderer } );
 
11881 var Canvas = Renderer.extend({
 
11882         getEvents: function () {
 
11883                 var events = Renderer.prototype.getEvents.call(this);
 
11884                 events.viewprereset = this._onViewPreReset;
 
11888         _onViewPreReset: function () {
 
11889                 // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
 
11890                 this._postponeUpdatePaths = true;
 
11893         onAdd: function () {
 
11894                 Renderer.prototype.onAdd.call(this);
 
11896                 // Redraw vectors since canvas is cleared upon removal,
 
11897                 // in case of removing the renderer itself from the map.
 
11901         _initContainer: function () {
 
11902                 var container = this._container = document.createElement('canvas');
 
11904                 on(container, 'mousemove', throttle(this._onMouseMove, 32, this), this);
 
11905                 on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
 
11906                 on(container, 'mouseout', this._handleMouseOut, this);
 
11908                 this._ctx = container.getContext('2d');
 
11911         _destroyContainer: function () {
 
11913                 remove(this._container);
 
11914                 off(this._container);
 
11915                 delete this._container;
 
11918         _updatePaths: function () {
 
11919                 if (this._postponeUpdatePaths) { return; }
 
11922                 this._redrawBounds = null;
 
11923                 for (var id in this._layers) {
 
11924                         layer = this._layers[id];
 
11930         _update: function () {
 
11931                 if (this._map._animatingZoom && this._bounds) { return; }
 
11933                 this._drawnLayers = {};
 
11935                 Renderer.prototype._update.call(this);
 
11937                 var b = this._bounds,
 
11938                     container = this._container,
 
11939                     size = b.getSize(),
 
11940                     m = retina ? 2 : 1;
 
11942                 setPosition(container, b.min);
 
11944                 // set canvas size (also clearing it); use double size on retina
 
11945                 container.width = m * size.x;
 
11946                 container.height = m * size.y;
 
11947                 container.style.width = size.x + 'px';
 
11948                 container.style.height = size.y + 'px';
 
11951                         this._ctx.scale(2, 2);
 
11954                 // translate so we use the same path coordinates after canvas element moves
 
11955                 this._ctx.translate(-b.min.x, -b.min.y);
 
11957                 // Tell paths to redraw themselves
 
11958                 this.fire('update');
 
11961         _reset: function () {
 
11962                 Renderer.prototype._reset.call(this);
 
11964                 if (this._postponeUpdatePaths) {
 
11965                         this._postponeUpdatePaths = false;
 
11966                         this._updatePaths();
 
11970         _initPath: function (layer) {
 
11971                 this._updateDashArray(layer);
 
11972                 this._layers[stamp(layer)] = layer;
 
11974                 var order = layer._order = {
 
11976                         prev: this._drawLast,
 
11979                 if (this._drawLast) { this._drawLast.next = order; }
 
11980                 this._drawLast = order;
 
11981                 this._drawFirst = this._drawFirst || this._drawLast;
 
11984         _addPath: function (layer) {
 
11985                 this._requestRedraw(layer);
 
11988         _removePath: function (layer) {
 
11989                 var order = layer._order;
 
11990                 var next = order.next;
 
11991                 var prev = order.prev;
 
11996                         this._drawLast = prev;
 
12001                         this._drawFirst = next;
 
12004                 delete layer._order;
 
12006                 delete this._layers[L.stamp(layer)];
 
12008                 this._requestRedraw(layer);
 
12011         _updatePath: function (layer) {
 
12012                 // Redraw the union of the layer's old pixel
 
12013                 // bounds and the new pixel bounds.
 
12014                 this._extendRedrawBounds(layer);
 
12017                 // The redraw will extend the redraw bounds
 
12018                 // with the new pixel bounds.
 
12019                 this._requestRedraw(layer);
 
12022         _updateStyle: function (layer) {
 
12023                 this._updateDashArray(layer);
 
12024                 this._requestRedraw(layer);
 
12027         _updateDashArray: function (layer) {
 
12028                 if (layer.options.dashArray) {
 
12029                         var parts = layer.options.dashArray.split(','),
 
12032                         for (i = 0; i < parts.length; i++) {
 
12033                                 dashArray.push(Number(parts[i]));
 
12035                         layer.options._dashArray = dashArray;
 
12039         _requestRedraw: function (layer) {
 
12040                 if (!this._map) { return; }
 
12042                 this._extendRedrawBounds(layer);
 
12043                 this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
 
12046         _extendRedrawBounds: function (layer) {
 
12047                 if (layer._pxBounds) {
 
12048                         var padding = (layer.options.weight || 0) + 1;
 
12049                         this._redrawBounds = this._redrawBounds || new Bounds();
 
12050                         this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
 
12051                         this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
 
12055         _redraw: function () {
 
12056                 this._redrawRequest = null;
 
12058                 if (this._redrawBounds) {
 
12059                         this._redrawBounds.min._floor();
 
12060                         this._redrawBounds.max._ceil();
 
12063                 this._clear(); // clear layers in redraw bounds
 
12064                 this._draw(); // draw layers
 
12066                 this._redrawBounds = null;
 
12069         _clear: function () {
 
12070                 var bounds = this._redrawBounds;
 
12072                         var size = bounds.getSize();
 
12073                         this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
 
12075                         this._ctx.clearRect(0, 0, this._container.width, this._container.height);
 
12079         _draw: function () {
 
12080                 var layer, bounds = this._redrawBounds;
 
12083                         var size = bounds.getSize();
 
12084                         this._ctx.beginPath();
 
12085                         this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
 
12089                 this._drawing = true;
 
12091                 for (var order = this._drawFirst; order; order = order.next) {
 
12092                         layer = order.layer;
 
12093                         if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
 
12094                                 layer._updatePath();
 
12098                 this._drawing = false;
 
12100                 this._ctx.restore();  // Restore state before clipping.
 
12103         _updatePoly: function (layer, closed) {
 
12104                 if (!this._drawing) { return; }
 
12107                     parts = layer._parts,
 
12108                     len = parts.length,
 
12111                 if (!len) { return; }
 
12113                 this._drawnLayers[layer._leaflet_id] = layer;
 
12117                 for (i = 0; i < len; i++) {
 
12118                         for (j = 0, len2 = parts[i].length; j < len2; j++) {
 
12120                                 ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
 
12127                 this._fillStroke(ctx, layer);
 
12129                 // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
 
12132         _updateCircle: function (layer) {
 
12134                 if (!this._drawing || layer._empty()) { return; }
 
12136                 var p = layer._point,
 
12138                     r = Math.max(Math.round(layer._radius), 1),
 
12139                     s = (Math.max(Math.round(layer._radiusY), 1) || r) / r;
 
12141                 this._drawnLayers[layer._leaflet_id] = layer;
 
12149                 ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
 
12155                 this._fillStroke(ctx, layer);
 
12158         _fillStroke: function (ctx, layer) {
 
12159                 var options = layer.options;
 
12161                 if (options.fill) {
 
12162                         ctx.globalAlpha = options.fillOpacity;
 
12163                         ctx.fillStyle = options.fillColor || options.color;
 
12164                         ctx.fill(options.fillRule || 'evenodd');
 
12167                 if (options.stroke && options.weight !== 0) {
 
12168                         if (ctx.setLineDash) {
 
12169                                 ctx.setLineDash(layer.options && layer.options._dashArray || []);
 
12171                         ctx.globalAlpha = options.opacity;
 
12172                         ctx.lineWidth = options.weight;
 
12173                         ctx.strokeStyle = options.color;
 
12174                         ctx.lineCap = options.lineCap;
 
12175                         ctx.lineJoin = options.lineJoin;
 
12180         // Canvas obviously doesn't have mouse events for individual drawn objects,
 
12181         // so we emulate that by calculating what's under the mouse on mousemove/click manually
 
12183         _onClick: function (e) {
 
12184                 var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
 
12186                 for (var order = this._drawFirst; order; order = order.next) {
 
12187                         layer = order.layer;
 
12188                         if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
 
12189                                 clickedLayer = layer;
 
12192                 if (clickedLayer)  {
 
12194                         this._fireEvent([clickedLayer], e);
 
12198         _onMouseMove: function (e) {
 
12199                 if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
 
12201                 var point = this._map.mouseEventToLayerPoint(e);
 
12202                 this._handleMouseHover(e, point);
 
12206         _handleMouseOut: function (e) {
 
12207                 var layer = this._hoveredLayer;
 
12209                         // if we're leaving the layer, fire mouseout
 
12210                         removeClass(this._container, 'leaflet-interactive');
 
12211                         this._fireEvent([layer], e, 'mouseout');
 
12212                         this._hoveredLayer = null;
 
12216         _handleMouseHover: function (e, point) {
 
12217                 var layer, candidateHoveredLayer;
 
12219                 for (var order = this._drawFirst; order; order = order.next) {
 
12220                         layer = order.layer;
 
12221                         if (layer.options.interactive && layer._containsPoint(point)) {
 
12222                                 candidateHoveredLayer = layer;
 
12226                 if (candidateHoveredLayer !== this._hoveredLayer) {
 
12227                         this._handleMouseOut(e);
 
12229                         if (candidateHoveredLayer) {
 
12230                                 addClass(this._container, 'leaflet-interactive'); // change cursor
 
12231                                 this._fireEvent([candidateHoveredLayer], e, 'mouseover');
 
12232                                 this._hoveredLayer = candidateHoveredLayer;
 
12236                 if (this._hoveredLayer) {
 
12237                         this._fireEvent([this._hoveredLayer], e);
 
12241         _fireEvent: function (layers, e, type) {
 
12242                 this._map._fireDOMEvent(e, type || e.type, layers);
 
12245         _bringToFront: function (layer) {
 
12246                 var order = layer._order;
 
12247                 var next = order.next;
 
12248                 var prev = order.prev;
 
12259                         // Update first entry unless this is the
 
12261                         this._drawFirst = next;
 
12264                 order.prev = this._drawLast;
 
12265                 this._drawLast.next = order;
 
12268                 this._drawLast = order;
 
12270                 this._requestRedraw(layer);
 
12273         _bringToBack: function (layer) {
 
12274                 var order = layer._order;
 
12275                 var next = order.next;
 
12276                 var prev = order.prev;
 
12287                         // Update last entry unless this is the
 
12289                         this._drawLast = prev;
 
12294                 order.next = this._drawFirst;
 
12295                 this._drawFirst.prev = order;
 
12296                 this._drawFirst = order;
 
12298                 this._requestRedraw(layer);
 
12302 // @factory L.canvas(options?: Renderer options)
 
12303 // Creates a Canvas renderer with the given options.
 
12304 function canvas$1(options) {
 
12305         return canvas ? new Canvas(options) : null;
 
12309  * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
 
12313 var vmlCreate = (function () {
 
12315                 document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
 
12316                 return function (name) {
 
12317                         return document.createElement('<lvml:' + name + ' class="lvml">');
 
12320                 return function (name) {
 
12321                         return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
 
12330  * Although SVG is not available on IE7 and IE8, these browsers support [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language), and the SVG renderer will fall back to VML in this case.
 
12332  * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
 
12333  * with old versions of Internet Explorer.
 
12336 // mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
 
12339         _initContainer: function () {
 
12340                 this._container = create$1('div', 'leaflet-vml-container');
 
12343         _update: function () {
 
12344                 if (this._map._animatingZoom) { return; }
 
12345                 Renderer.prototype._update.call(this);
 
12346                 this.fire('update');
 
12349         _initPath: function (layer) {
 
12350                 var container = layer._container = vmlCreate('shape');
 
12352                 addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
 
12354                 container.coordsize = '1 1';
 
12356                 layer._path = vmlCreate('path');
 
12357                 container.appendChild(layer._path);
 
12359                 this._updateStyle(layer);
 
12360                 this._layers[stamp(layer)] = layer;
 
12363         _addPath: function (layer) {
 
12364                 var container = layer._container;
 
12365                 this._container.appendChild(container);
 
12367                 if (layer.options.interactive) {
 
12368                         layer.addInteractiveTarget(container);
 
12372         _removePath: function (layer) {
 
12373                 var container = layer._container;
 
12375                 layer.removeInteractiveTarget(container);
 
12376                 delete this._layers[stamp(layer)];
 
12379         _updateStyle: function (layer) {
 
12380                 var stroke = layer._stroke,
 
12381                     fill = layer._fill,
 
12382                     options = layer.options,
 
12383                     container = layer._container;
 
12385                 container.stroked = !!options.stroke;
 
12386                 container.filled = !!options.fill;
 
12388                 if (options.stroke) {
 
12390                                 stroke = layer._stroke = vmlCreate('stroke');
 
12392                         container.appendChild(stroke);
 
12393                         stroke.weight = options.weight + 'px';
 
12394                         stroke.color = options.color;
 
12395                         stroke.opacity = options.opacity;
 
12397                         if (options.dashArray) {
 
12398                                 stroke.dashStyle = isArray(options.dashArray) ?
 
12399                                     options.dashArray.join(' ') :
 
12400                                     options.dashArray.replace(/( *, *)/g, ' ');
 
12402                                 stroke.dashStyle = '';
 
12404                         stroke.endcap = options.lineCap.replace('butt', 'flat');
 
12405                         stroke.joinstyle = options.lineJoin;
 
12407                 } else if (stroke) {
 
12408                         container.removeChild(stroke);
 
12409                         layer._stroke = null;
 
12412                 if (options.fill) {
 
12414                                 fill = layer._fill = vmlCreate('fill');
 
12416                         container.appendChild(fill);
 
12417                         fill.color = options.fillColor || options.color;
 
12418                         fill.opacity = options.fillOpacity;
 
12421                         container.removeChild(fill);
 
12422                         layer._fill = null;
 
12426         _updateCircle: function (layer) {
 
12427                 var p = layer._point.round(),
 
12428                     r = Math.round(layer._radius),
 
12429                     r2 = Math.round(layer._radiusY || r);
 
12431                 this._setPath(layer, layer._empty() ? 'M0 0' :
 
12432                         'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
 
12435         _setPath: function (layer, path) {
 
12436                 layer._path.v = path;
 
12439         _bringToFront: function (layer) {
 
12440                 toFront(layer._container);
 
12443         _bringToBack: function (layer) {
 
12444                 toBack(layer._container);
 
12448 var create$2 = vml ? vmlCreate : svgCreate;
 
12452  * @inherits Renderer
 
12455  * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
 
12456  * Inherits `Renderer`.
 
12458  * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
 
12459  * available in all web browsers, notably Android 2.x and 3.x.
 
12461  * Although SVG is not available on IE7 and IE8, these browsers support
 
12462  * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
 
12463  * (a now deprecated technology), and the SVG renderer will fall back to VML in
 
12468  * Use SVG by default for all paths in the map:
 
12471  * var map = L.map('map', {
 
12472  *      renderer: L.svg()
 
12476  * Use a SVG renderer with extra padding for specific vector geometries:
 
12479  * var map = L.map('map');
 
12480  * var myRenderer = L.svg({ padding: 0.5 });
 
12481  * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
12482  * var circle = L.circle( center, { renderer: myRenderer } );
 
12486 var SVG = Renderer.extend({
 
12488         getEvents: function () {
 
12489                 var events = Renderer.prototype.getEvents.call(this);
 
12490                 events.zoomstart = this._onZoomStart;
 
12494         _initContainer: function () {
 
12495                 this._container = create$2('svg');
 
12497                 // makes it possible to click through svg root; we'll reset it back in individual paths
 
12498                 this._container.setAttribute('pointer-events', 'none');
 
12500                 this._rootGroup = create$2('g');
 
12501                 this._container.appendChild(this._rootGroup);
 
12504         _destroyContainer: function () {
 
12505                 remove(this._container);
 
12506                 off(this._container);
 
12507                 delete this._container;
 
12508                 delete this._rootGroup;
 
12509                 delete this._svgSize;
 
12512         _onZoomStart: function () {
 
12513                 // Drag-then-pinch interactions might mess up the center and zoom.
 
12514                 // In this case, the easiest way to prevent this is re-do the renderer
 
12515                 //   bounds and padding when the zooming starts.
 
12519         _update: function () {
 
12520                 if (this._map._animatingZoom && this._bounds) { return; }
 
12522                 Renderer.prototype._update.call(this);
 
12524                 var b = this._bounds,
 
12525                     size = b.getSize(),
 
12526                     container = this._container;
 
12528                 // set size of svg-container if changed
 
12529                 if (!this._svgSize || !this._svgSize.equals(size)) {
 
12530                         this._svgSize = size;
 
12531                         container.setAttribute('width', size.x);
 
12532                         container.setAttribute('height', size.y);
 
12535                 // movement: update container viewBox so that we don't have to change coordinates of individual layers
 
12536                 setPosition(container, b.min);
 
12537                 container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
 
12539                 this.fire('update');
 
12542         // methods below are called by vector layers implementations
 
12544         _initPath: function (layer) {
 
12545                 var path = layer._path = create$2('path');
 
12548                 // @option className: String = null
 
12549                 // Custom class name set on an element. Only for SVG renderer.
 
12550                 if (layer.options.className) {
 
12551                         addClass(path, layer.options.className);
 
12554                 if (layer.options.interactive) {
 
12555                         addClass(path, 'leaflet-interactive');
 
12558                 this._updateStyle(layer);
 
12559                 this._layers[stamp(layer)] = layer;
 
12562         _addPath: function (layer) {
 
12563                 if (!this._rootGroup) { this._initContainer(); }
 
12564                 this._rootGroup.appendChild(layer._path);
 
12565                 layer.addInteractiveTarget(layer._path);
 
12568         _removePath: function (layer) {
 
12569                 remove(layer._path);
 
12570                 layer.removeInteractiveTarget(layer._path);
 
12571                 delete this._layers[stamp(layer)];
 
12574         _updatePath: function (layer) {
 
12579         _updateStyle: function (layer) {
 
12580                 var path = layer._path,
 
12581                     options = layer.options;
 
12583                 if (!path) { return; }
 
12585                 if (options.stroke) {
 
12586                         path.setAttribute('stroke', options.color);
 
12587                         path.setAttribute('stroke-opacity', options.opacity);
 
12588                         path.setAttribute('stroke-width', options.weight);
 
12589                         path.setAttribute('stroke-linecap', options.lineCap);
 
12590                         path.setAttribute('stroke-linejoin', options.lineJoin);
 
12592                         if (options.dashArray) {
 
12593                                 path.setAttribute('stroke-dasharray', options.dashArray);
 
12595                                 path.removeAttribute('stroke-dasharray');
 
12598                         if (options.dashOffset) {
 
12599                                 path.setAttribute('stroke-dashoffset', options.dashOffset);
 
12601                                 path.removeAttribute('stroke-dashoffset');
 
12604                         path.setAttribute('stroke', 'none');
 
12607                 if (options.fill) {
 
12608                         path.setAttribute('fill', options.fillColor || options.color);
 
12609                         path.setAttribute('fill-opacity', options.fillOpacity);
 
12610                         path.setAttribute('fill-rule', options.fillRule || 'evenodd');
 
12612                         path.setAttribute('fill', 'none');
 
12616         _updatePoly: function (layer, closed) {
 
12617                 this._setPath(layer, pointsToPath(layer._parts, closed));
 
12620         _updateCircle: function (layer) {
 
12621                 var p = layer._point,
 
12622                     r = Math.max(Math.round(layer._radius), 1),
 
12623                     r2 = Math.max(Math.round(layer._radiusY), 1) || r,
 
12624                     arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
 
12626                 // drawing a circle with two half-arcs
 
12627                 var d = layer._empty() ? 'M0 0' :
 
12628                         'M' + (p.x - r) + ',' + p.y +
 
12629                         arc + (r * 2) + ',0 ' +
 
12630                         arc + (-r * 2) + ',0 ';
 
12632                 this._setPath(layer, d);
 
12635         _setPath: function (layer, path) {
 
12636                 layer._path.setAttribute('d', path);
 
12639         // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
 
12640         _bringToFront: function (layer) {
 
12641                 toFront(layer._path);
 
12644         _bringToBack: function (layer) {
 
12645                 toBack(layer._path);
 
12650         SVG.include(vmlMixin);
 
12654 // @factory L.svg(options?: Renderer options)
 
12655 // Creates a SVG renderer with the given options.
 
12656 function svg$1(options) {
 
12657         return svg || vml ? new SVG(options) : null;
 
12661         // @namespace Map; @method getRenderer(layer: Path): Renderer
 
12662         // Returns the instance of `Renderer` that should be used to render the given
 
12663         // `Path`. It will ensure that the `renderer` options of the map and paths
 
12664         // are respected, and that the renderers do exist on the map.
 
12665         getRenderer: function (layer) {
 
12666                 // @namespace Path; @option renderer: Renderer
 
12667                 // Use this specific instance of `Renderer` for this path. Takes
 
12668                 // precedence over the map's [default renderer](#map-renderer).
 
12669                 var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
 
12672                         // @namespace Map; @option preferCanvas: Boolean = false
 
12673                         // Whether `Path`s should be rendered on a `Canvas` renderer.
 
12674                         // By default, all `Path`s are rendered in a `SVG` renderer.
 
12675                         renderer = this._renderer = (this.options.preferCanvas && canvas$1()) || svg$1();
 
12678                 if (!this.hasLayer(renderer)) {
 
12679                         this.addLayer(renderer);
 
12684         _getPaneRenderer: function (name) {
 
12685                 if (name === 'overlayPane' || name === undefined) {
 
12689                 var renderer = this._paneRenderers[name];
 
12690                 if (renderer === undefined) {
 
12691                         renderer = (SVG && svg$1({pane: name})) || (Canvas && canvas$1({pane: name}));
 
12692                         this._paneRenderers[name] = renderer;
 
12699  * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
 
12705  * @inherits Polygon
 
12707  * A class for drawing rectangle overlays on a map. Extends `Polygon`.
 
12712  * // define rectangle geographical bounds
 
12713  * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
 
12715  * // create an orange rectangle
 
12716  * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
 
12718  * // zoom the map to the rectangle bounds
 
12719  * map.fitBounds(bounds);
 
12725 var Rectangle = Polygon.extend({
 
12726         initialize: function (latLngBounds, options) {
 
12727                 Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
 
12730         // @method setBounds(latLngBounds: LatLngBounds): this
 
12731         // Redraws the rectangle with the passed bounds.
 
12732         setBounds: function (latLngBounds) {
 
12733                 return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
 
12736         _boundsToLatLngs: function (latLngBounds) {
 
12737                 latLngBounds = toLatLngBounds(latLngBounds);
 
12739                         latLngBounds.getSouthWest(),
 
12740                         latLngBounds.getNorthWest(),
 
12741                         latLngBounds.getNorthEast(),
 
12742                         latLngBounds.getSouthEast()
 
12748 // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
 
12749 function rectangle(latLngBounds, options) {
 
12750         return new Rectangle(latLngBounds, options);
 
12753 SVG.create = create$2;
 
12754 SVG.pointsToPath = pointsToPath;
 
12756 GeoJSON.geometryToLayer = geometryToLayer;
 
12757 GeoJSON.coordsToLatLng = coordsToLatLng;
 
12758 GeoJSON.coordsToLatLngs = coordsToLatLngs;
 
12759 GeoJSON.latLngToCoords = latLngToCoords;
 
12760 GeoJSON.latLngsToCoords = latLngsToCoords;
 
12761 GeoJSON.getFeature = getFeature;
 
12762 GeoJSON.asFeature = asFeature;
 
12765  * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
 
12766  * (zoom to a selected bounding box), enabled by default.
 
12770 // @section Interaction Options
 
12772         // @option boxZoom: Boolean = true
 
12773         // Whether the map can be zoomed to a rectangular area specified by
 
12774         // dragging the mouse while pressing the shift key.
 
12778 var BoxZoom = Handler.extend({
 
12779         initialize: function (map) {
 
12781                 this._container = map._container;
 
12782                 this._pane = map._panes.overlayPane;
 
12783                 this._resetStateTimeout = 0;
 
12784                 map.on('unload', this._destroy, this);
 
12787         addHooks: function () {
 
12788                 on(this._container, 'mousedown', this._onMouseDown, this);
 
12791         removeHooks: function () {
 
12792                 off(this._container, 'mousedown', this._onMouseDown, this);
 
12795         moved: function () {
 
12796                 return this._moved;
 
12799         _destroy: function () {
 
12800                 remove(this._pane);
 
12804         _resetState: function () {
 
12805                 this._resetStateTimeout = 0;
 
12806                 this._moved = false;
 
12809         _clearDeferredResetState: function () {
 
12810                 if (this._resetStateTimeout !== 0) {
 
12811                         clearTimeout(this._resetStateTimeout);
 
12812                         this._resetStateTimeout = 0;
 
12816         _onMouseDown: function (e) {
 
12817                 if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
 
12819                 // Clear the deferred resetState if it hasn't executed yet, otherwise it
 
12820                 // will interrupt the interaction and orphan a box element in the container.
 
12821                 this._clearDeferredResetState();
 
12822                 this._resetState();
 
12824                 disableTextSelection();
 
12825                 disableImageDrag();
 
12827                 this._startPoint = this._map.mouseEventToContainerPoint(e);
 
12831                         mousemove: this._onMouseMove,
 
12832                         mouseup: this._onMouseUp,
 
12833                         keydown: this._onKeyDown
 
12837         _onMouseMove: function (e) {
 
12838                 if (!this._moved) {
 
12839                         this._moved = true;
 
12841                         this._box = create$1('div', 'leaflet-zoom-box', this._container);
 
12842                         addClass(this._container, 'leaflet-crosshair');
 
12844                         this._map.fire('boxzoomstart');
 
12847                 this._point = this._map.mouseEventToContainerPoint(e);
 
12849                 var bounds = new Bounds(this._point, this._startPoint),
 
12850                     size = bounds.getSize();
 
12852                 setPosition(this._box, bounds.min);
 
12854                 this._box.style.width  = size.x + 'px';
 
12855                 this._box.style.height = size.y + 'px';
 
12858         _finish: function () {
 
12861                         removeClass(this._container, 'leaflet-crosshair');
 
12864                 enableTextSelection();
 
12869                         mousemove: this._onMouseMove,
 
12870                         mouseup: this._onMouseUp,
 
12871                         keydown: this._onKeyDown
 
12875         _onMouseUp: function (e) {
 
12876                 if ((e.which !== 1) && (e.button !== 1)) { return; }
 
12880                 if (!this._moved) { return; }
 
12881                 // Postpone to next JS tick so internal click event handling
 
12882                 // still see it as "moved".
 
12883                 this._clearDeferredResetState();
 
12884                 this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
 
12886                 var bounds = new LatLngBounds(
 
12887                         this._map.containerPointToLatLng(this._startPoint),
 
12888                         this._map.containerPointToLatLng(this._point));
 
12892                         .fire('boxzoomend', {boxZoomBounds: bounds});
 
12895         _onKeyDown: function (e) {
 
12896                 if (e.keyCode === 27) {
 
12902 // @section Handlers
 
12903 // @property boxZoom: Handler
 
12904 // Box (shift-drag with mouse) zoom handler.
 
12905 Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
 
12908  * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
 
12912 // @section Interaction Options
 
12915         // @option doubleClickZoom: Boolean|String = true
 
12916         // Whether the map can be zoomed in by double clicking on it and
 
12917         // zoomed out by double clicking while holding shift. If passed
 
12918         // `'center'`, double-click zoom will zoom to the center of the
 
12919         //  view regardless of where the mouse was.
 
12920         doubleClickZoom: true
 
12923 var DoubleClickZoom = Handler.extend({
 
12924         addHooks: function () {
 
12925                 this._map.on('dblclick', this._onDoubleClick, this);
 
12928         removeHooks: function () {
 
12929                 this._map.off('dblclick', this._onDoubleClick, this);
 
12932         _onDoubleClick: function (e) {
 
12933                 var map = this._map,
 
12934                     oldZoom = map.getZoom(),
 
12935                     delta = map.options.zoomDelta,
 
12936                     zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
 
12938                 if (map.options.doubleClickZoom === 'center') {
 
12941                         map.setZoomAround(e.containerPoint, zoom);
 
12946 // @section Handlers
 
12948 // Map properties include interaction handlers that allow you to control
 
12949 // interaction behavior in runtime, enabling or disabling certain features such
 
12950 // as dragging or touch zoom (see `Handler` methods). For example:
 
12953 // map.doubleClickZoom.disable();
 
12956 // @property doubleClickZoom: Handler
 
12957 // Double click zoom handler.
 
12958 Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
 
12961  * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
 
12965 // @section Interaction Options
 
12967         // @option dragging: Boolean = true
 
12968         // Whether the map be draggable with mouse/touch or not.
 
12971         // @section Panning Inertia Options
 
12972         // @option inertia: Boolean = *
 
12973         // If enabled, panning of the map will have an inertia effect where
 
12974         // the map builds momentum while dragging and continues moving in
 
12975         // the same direction for some time. Feels especially nice on touch
 
12976         // devices. Enabled by default unless running on old Android devices.
 
12977         inertia: !android23,
 
12979         // @option inertiaDeceleration: Number = 3000
 
12980         // The rate with which the inertial movement slows down, in pixels/second².
 
12981         inertiaDeceleration: 3400, // px/s^2
 
12983         // @option inertiaMaxSpeed: Number = Infinity
 
12984         // Max speed of the inertial movement, in pixels/second.
 
12985         inertiaMaxSpeed: Infinity, // px/s
 
12987         // @option easeLinearity: Number = 0.2
 
12988         easeLinearity: 0.2,
 
12990         // TODO refactor, move to CRS
 
12991         // @option worldCopyJump: Boolean = false
 
12992         // With this option enabled, the map tracks when you pan to another "copy"
 
12993         // of the world and seamlessly jumps to the original one so that all overlays
 
12994         // like markers and vector layers are still visible.
 
12995         worldCopyJump: false,
 
12997         // @option maxBoundsViscosity: Number = 0.0
 
12998         // If `maxBounds` is set, this option will control how solid the bounds
 
12999         // are when dragging the map around. The default value of `0.0` allows the
 
13000         // user to drag outside the bounds at normal speed, higher values will
 
13001         // slow down map dragging outside bounds, and `1.0` makes the bounds fully
 
13002         // solid, preventing the user from dragging outside the bounds.
 
13003         maxBoundsViscosity: 0.0
 
13006 var Drag = Handler.extend({
 
13007         addHooks: function () {
 
13008                 if (!this._draggable) {
 
13009                         var map = this._map;
 
13011                         this._draggable = new Draggable(map._mapPane, map._container);
 
13013                         this._draggable.on({
 
13014                                 dragstart: this._onDragStart,
 
13015                                 drag: this._onDrag,
 
13016                                 dragend: this._onDragEnd
 
13019                         this._draggable.on('predrag', this._onPreDragLimit, this);
 
13020                         if (map.options.worldCopyJump) {
 
13021                                 this._draggable.on('predrag', this._onPreDragWrap, this);
 
13022                                 map.on('zoomend', this._onZoomEnd, this);
 
13024                                 map.whenReady(this._onZoomEnd, this);
 
13027                 addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
 
13028                 this._draggable.enable();
 
13029                 this._positions = [];
 
13033         removeHooks: function () {
 
13034                 removeClass(this._map._container, 'leaflet-grab');
 
13035                 removeClass(this._map._container, 'leaflet-touch-drag');
 
13036                 this._draggable.disable();
 
13039         moved: function () {
 
13040                 return this._draggable && this._draggable._moved;
 
13043         moving: function () {
 
13044                 return this._draggable && this._draggable._moving;
 
13047         _onDragStart: function () {
 
13048                 var map = this._map;
 
13051                 if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
 
13052                         var bounds = toLatLngBounds(this._map.options.maxBounds);
 
13054                         this._offsetLimit = toBounds(
 
13055                                 this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
 
13056                                 this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
 
13057                                         .add(this._map.getSize()));
 
13059                         this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
 
13061                         this._offsetLimit = null;
 
13066                     .fire('dragstart');
 
13068                 if (map.options.inertia) {
 
13069                         this._positions = [];
 
13074         _onDrag: function (e) {
 
13075                 if (this._map.options.inertia) {
 
13076                         var time = this._lastTime = +new Date(),
 
13077                             pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
 
13079                         this._positions.push(pos);
 
13080                         this._times.push(time);
 
13082                         this._prunePositions(time);
 
13090         _prunePositions: function (time) {
 
13091                 while (this._positions.length > 1 && time - this._times[0] > 50) {
 
13092                         this._positions.shift();
 
13093                         this._times.shift();
 
13097         _onZoomEnd: function () {
 
13098                 var pxCenter = this._map.getSize().divideBy(2),
 
13099                     pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
 
13101                 this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
 
13102                 this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
 
13105         _viscousLimit: function (value, threshold) {
 
13106                 return value - (value - threshold) * this._viscosity;
 
13109         _onPreDragLimit: function () {
 
13110                 if (!this._viscosity || !this._offsetLimit) { return; }
 
13112                 var offset = this._draggable._newPos.subtract(this._draggable._startPos);
 
13114                 var limit = this._offsetLimit;
 
13115                 if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
 
13116                 if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
 
13117                 if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
 
13118                 if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
 
13120                 this._draggable._newPos = this._draggable._startPos.add(offset);
 
13123         _onPreDragWrap: function () {
 
13124                 // TODO refactor to be able to adjust map pane position after zoom
 
13125                 var worldWidth = this._worldWidth,
 
13126                     halfWidth = Math.round(worldWidth / 2),
 
13127                     dx = this._initialWorldOffset,
 
13128                     x = this._draggable._newPos.x,
 
13129                     newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
 
13130                     newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
 
13131                     newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
 
13133                 this._draggable._absPos = this._draggable._newPos.clone();
 
13134                 this._draggable._newPos.x = newX;
 
13137         _onDragEnd: function (e) {
 
13138                 var map = this._map,
 
13139                     options = map.options,
 
13141                     noInertia = !options.inertia || this._times.length < 2;
 
13143                 map.fire('dragend', e);
 
13146                         map.fire('moveend');
 
13149                         this._prunePositions(+new Date());
 
13151                         var direction = this._lastPos.subtract(this._positions[0]),
 
13152                             duration = (this._lastTime - this._times[0]) / 1000,
 
13153                             ease = options.easeLinearity,
 
13155                             speedVector = direction.multiplyBy(ease / duration),
 
13156                             speed = speedVector.distanceTo([0, 0]),
 
13158                             limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
 
13159                             limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
 
13161                             decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
 
13162                             offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
 
13164                         if (!offset.x && !offset.y) {
 
13165                                 map.fire('moveend');
 
13168                                 offset = map._limitOffset(offset, map.options.maxBounds);
 
13170                                 requestAnimFrame(function () {
 
13171                                         map.panBy(offset, {
 
13172                                                 duration: decelerationDuration,
 
13173                                                 easeLinearity: ease,
 
13183 // @section Handlers
 
13184 // @property dragging: Handler
 
13185 // Map dragging handler (by both mouse and touch).
 
13186 Map.addInitHook('addHandler', 'dragging', Drag);
 
13189  * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
 
13193 // @section Keyboard Navigation Options
 
13195         // @option keyboard: Boolean = true
 
13196         // Makes the map focusable and allows users to navigate the map with keyboard
 
13197         // arrows and `+`/`-` keys.
 
13200         // @option keyboardPanDelta: Number = 80
 
13201         // Amount of pixels to pan when pressing an arrow key.
 
13202         keyboardPanDelta: 80
 
13205 var Keyboard = Handler.extend({
 
13212                 zoomIn:  [187, 107, 61, 171],
 
13213                 zoomOut: [189, 109, 54, 173]
 
13216         initialize: function (map) {
 
13219                 this._setPanDelta(map.options.keyboardPanDelta);
 
13220                 this._setZoomDelta(map.options.zoomDelta);
 
13223         addHooks: function () {
 
13224                 var container = this._map._container;
 
13226                 // make the container focusable by tabbing
 
13227                 if (container.tabIndex <= 0) {
 
13228                         container.tabIndex = '0';
 
13232                         focus: this._onFocus,
 
13233                         blur: this._onBlur,
 
13234                         mousedown: this._onMouseDown
 
13238                         focus: this._addHooks,
 
13239                         blur: this._removeHooks
 
13243         removeHooks: function () {
 
13244                 this._removeHooks();
 
13246                 off(this._map._container, {
 
13247                         focus: this._onFocus,
 
13248                         blur: this._onBlur,
 
13249                         mousedown: this._onMouseDown
 
13253                         focus: this._addHooks,
 
13254                         blur: this._removeHooks
 
13258         _onMouseDown: function () {
 
13259                 if (this._focused) { return; }
 
13261                 var body = document.body,
 
13262                     docEl = document.documentElement,
 
13263                     top = body.scrollTop || docEl.scrollTop,
 
13264                     left = body.scrollLeft || docEl.scrollLeft;
 
13266                 this._map._container.focus();
 
13268                 window.scrollTo(left, top);
 
13271         _onFocus: function () {
 
13272                 this._focused = true;
 
13273                 this._map.fire('focus');
 
13276         _onBlur: function () {
 
13277                 this._focused = false;
 
13278                 this._map.fire('blur');
 
13281         _setPanDelta: function (panDelta) {
 
13282                 var keys = this._panKeys = {},
 
13283                     codes = this.keyCodes,
 
13286                 for (i = 0, len = codes.left.length; i < len; i++) {
 
13287                         keys[codes.left[i]] = [-1 * panDelta, 0];
 
13289                 for (i = 0, len = codes.right.length; i < len; i++) {
 
13290                         keys[codes.right[i]] = [panDelta, 0];
 
13292                 for (i = 0, len = codes.down.length; i < len; i++) {
 
13293                         keys[codes.down[i]] = [0, panDelta];
 
13295                 for (i = 0, len = codes.up.length; i < len; i++) {
 
13296                         keys[codes.up[i]] = [0, -1 * panDelta];
 
13300         _setZoomDelta: function (zoomDelta) {
 
13301                 var keys = this._zoomKeys = {},
 
13302                     codes = this.keyCodes,
 
13305                 for (i = 0, len = codes.zoomIn.length; i < len; i++) {
 
13306                         keys[codes.zoomIn[i]] = zoomDelta;
 
13308                 for (i = 0, len = codes.zoomOut.length; i < len; i++) {
 
13309                         keys[codes.zoomOut[i]] = -zoomDelta;
 
13313         _addHooks: function () {
 
13314                 on(document, 'keydown', this._onKeyDown, this);
 
13317         _removeHooks: function () {
 
13318                 off(document, 'keydown', this._onKeyDown, this);
 
13321         _onKeyDown: function (e) {
 
13322                 if (e.altKey || e.ctrlKey || e.metaKey) { return; }
 
13324                 var key = e.keyCode,
 
13328                 if (key in this._panKeys) {
 
13330                         if (map._panAnim && map._panAnim._inProgress) { return; }
 
13332                         offset = this._panKeys[key];
 
13334                                 offset = toPoint(offset).multiplyBy(3);
 
13339                         if (map.options.maxBounds) {
 
13340                                 map.panInsideBounds(map.options.maxBounds);
 
13343                 } else if (key in this._zoomKeys) {
 
13344                         map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
 
13346                 } else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) {
 
13357 // @section Handlers
 
13358 // @section Handlers
 
13359 // @property keyboard: Handler
 
13360 // Keyboard navigation handler.
 
13361 Map.addInitHook('addHandler', 'keyboard', Keyboard);
 
13364  * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
 
13368 // @section Interaction Options
 
13370         // @section Mousewheel options
 
13371         // @option scrollWheelZoom: Boolean|String = true
 
13372         // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
 
13373         // it will zoom to the center of the view regardless of where the mouse was.
 
13374         scrollWheelZoom: true,
 
13376         // @option wheelDebounceTime: Number = 40
 
13377         // Limits the rate at which a wheel can fire (in milliseconds). By default
 
13378         // user can't zoom via wheel more often than once per 40 ms.
 
13379         wheelDebounceTime: 40,
 
13381         // @option wheelPxPerZoomLevel: Number = 60
 
13382         // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
 
13383         // mean a change of one full zoom level. Smaller values will make wheel-zooming
 
13384         // faster (and vice versa).
 
13385         wheelPxPerZoomLevel: 60
 
13388 var ScrollWheelZoom = Handler.extend({
 
13389         addHooks: function () {
 
13390                 on(this._map._container, 'mousewheel', this._onWheelScroll, this);
 
13395         removeHooks: function () {
 
13396                 off(this._map._container, 'mousewheel', this._onWheelScroll, this);
 
13399         _onWheelScroll: function (e) {
 
13400                 var delta = getWheelDelta(e);
 
13402                 var debounce = this._map.options.wheelDebounceTime;
 
13404                 this._delta += delta;
 
13405                 this._lastMousePos = this._map.mouseEventToContainerPoint(e);
 
13407                 if (!this._startTime) {
 
13408                         this._startTime = +new Date();
 
13411                 var left = Math.max(debounce - (+new Date() - this._startTime), 0);
 
13413                 clearTimeout(this._timer);
 
13414                 this._timer = setTimeout(bind(this._performZoom, this), left);
 
13419         _performZoom: function () {
 
13420                 var map = this._map,
 
13421                     zoom = map.getZoom(),
 
13422                     snap = this._map.options.zoomSnap || 0;
 
13424                 map._stop(); // stop panning and fly animations if any
 
13426                 // map the delta with a sigmoid function to -4..4 range leaning on -1..1
 
13427                 var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
 
13428                     d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
 
13429                     d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
 
13430                     delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
 
13433                 this._startTime = null;
 
13435                 if (!delta) { return; }
 
13437                 if (map.options.scrollWheelZoom === 'center') {
 
13438                         map.setZoom(zoom + delta);
 
13440                         map.setZoomAround(this._lastMousePos, zoom + delta);
 
13445 // @section Handlers
 
13446 // @property scrollWheelZoom: Handler
 
13447 // Scroll wheel zoom handler.
 
13448 Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
 
13451  * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
 
13455 // @section Interaction Options
 
13457         // @section Touch interaction options
 
13458         // @option tap: Boolean = true
 
13459         // Enables mobile hacks for supporting instant taps (fixing 200ms click
 
13460         // delay on iOS/Android) and touch holds (fired as `contextmenu` events).
 
13463         // @option tapTolerance: Number = 15
 
13464         // The max number of pixels a user can shift his finger during touch
 
13465         // for it to be considered a valid tap.
 
13469 var Tap = Handler.extend({
 
13470         addHooks: function () {
 
13471                 on(this._map._container, 'touchstart', this._onDown, this);
 
13474         removeHooks: function () {
 
13475                 off(this._map._container, 'touchstart', this._onDown, this);
 
13478         _onDown: function (e) {
 
13479                 if (!e.touches) { return; }
 
13483                 this._fireClick = true;
 
13485                 // don't simulate click or track longpress if more than 1 touch
 
13486                 if (e.touches.length > 1) {
 
13487                         this._fireClick = false;
 
13488                         clearTimeout(this._holdTimeout);
 
13492                 var first = e.touches[0],
 
13495                 this._startPos = this._newPos = new Point(first.clientX, first.clientY);
 
13497                 // if touching a link, highlight it
 
13498                 if (el.tagName && el.tagName.toLowerCase() === 'a') {
 
13499                         addClass(el, 'leaflet-active');
 
13502                 // simulate long hold but setting a timeout
 
13503                 this._holdTimeout = setTimeout(bind(function () {
 
13504                         if (this._isTapValid()) {
 
13505                                 this._fireClick = false;
 
13507                                 this._simulateEvent('contextmenu', first);
 
13511                 this._simulateEvent('mousedown', first);
 
13514                         touchmove: this._onMove,
 
13515                         touchend: this._onUp
 
13519         _onUp: function (e) {
 
13520                 clearTimeout(this._holdTimeout);
 
13523                         touchmove: this._onMove,
 
13524                         touchend: this._onUp
 
13527                 if (this._fireClick && e && e.changedTouches) {
 
13529                         var first = e.changedTouches[0],
 
13532                         if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
 
13533                                 removeClass(el, 'leaflet-active');
 
13536                         this._simulateEvent('mouseup', first);
 
13538                         // simulate click if the touch didn't move too much
 
13539                         if (this._isTapValid()) {
 
13540                                 this._simulateEvent('click', first);
 
13545         _isTapValid: function () {
 
13546                 return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
 
13549         _onMove: function (e) {
 
13550                 var first = e.touches[0];
 
13551                 this._newPos = new Point(first.clientX, first.clientY);
 
13552                 this._simulateEvent('mousemove', first);
 
13555         _simulateEvent: function (type, e) {
 
13556                 var simulatedEvent = document.createEvent('MouseEvents');
 
13558                 simulatedEvent._simulated = true;
 
13559                 e.target._simulatedClick = true;
 
13561                 simulatedEvent.initMouseEvent(
 
13562                         type, true, true, window, 1,
 
13563                         e.screenX, e.screenY,
 
13564                         e.clientX, e.clientY,
 
13565                         false, false, false, false, 0, null);
 
13567                 e.target.dispatchEvent(simulatedEvent);
 
13571 // @section Handlers
 
13572 // @property tap: Handler
 
13573 // Mobile touch hacks (quick tap and touch hold) handler.
 
13574 if (touch && !pointer) {
 
13575         Map.addInitHook('addHandler', 'tap', Tap);
 
13579  * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
 
13583 // @section Interaction Options
 
13585         // @section Touch interaction options
 
13586         // @option touchZoom: Boolean|String = *
 
13587         // Whether the map can be zoomed by touch-dragging with two fingers. If
 
13588         // passed `'center'`, it will zoom to the center of the view regardless of
 
13589         // where the touch events (fingers) were. Enabled for touch-capable web
 
13590         // browsers except for old Androids.
 
13591         touchZoom: touch && !android23,
 
13593         // @option bounceAtZoomLimits: Boolean = true
 
13594         // Set it to false if you don't want the map to zoom beyond min/max zoom
 
13595         // and then bounce back when pinch-zooming.
 
13596         bounceAtZoomLimits: true
 
13599 var TouchZoom = Handler.extend({
 
13600         addHooks: function () {
 
13601                 addClass(this._map._container, 'leaflet-touch-zoom');
 
13602                 on(this._map._container, 'touchstart', this._onTouchStart, this);
 
13605         removeHooks: function () {
 
13606                 removeClass(this._map._container, 'leaflet-touch-zoom');
 
13607                 off(this._map._container, 'touchstart', this._onTouchStart, this);
 
13610         _onTouchStart: function (e) {
 
13611                 var map = this._map;
 
13612                 if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
 
13614                 var p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
13615                     p2 = map.mouseEventToContainerPoint(e.touches[1]);
 
13617                 this._centerPoint = map.getSize()._divideBy(2);
 
13618                 this._startLatLng = map.containerPointToLatLng(this._centerPoint);
 
13619                 if (map.options.touchZoom !== 'center') {
 
13620                         this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
 
13623                 this._startDist = p1.distanceTo(p2);
 
13624                 this._startZoom = map.getZoom();
 
13626                 this._moved = false;
 
13627                 this._zooming = true;
 
13631                 on(document, 'touchmove', this._onTouchMove, this);
 
13632                 on(document, 'touchend', this._onTouchEnd, this);
 
13637         _onTouchMove: function (e) {
 
13638                 if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
 
13640                 var map = this._map,
 
13641                     p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
13642                     p2 = map.mouseEventToContainerPoint(e.touches[1]),
 
13643                     scale = p1.distanceTo(p2) / this._startDist;
 
13645                 this._zoom = map.getScaleZoom(scale, this._startZoom);
 
13647                 if (!map.options.bounceAtZoomLimits && (
 
13648                         (this._zoom < map.getMinZoom() && scale < 1) ||
 
13649                         (this._zoom > map.getMaxZoom() && scale > 1))) {
 
13650                         this._zoom = map._limitZoom(this._zoom);
 
13653                 if (map.options.touchZoom === 'center') {
 
13654                         this._center = this._startLatLng;
 
13655                         if (scale === 1) { return; }
 
13657                         // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
 
13658                         var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
 
13659                         if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
 
13660                         this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
 
13663                 if (!this._moved) {
 
13664                         map._moveStart(true, false);
 
13665                         this._moved = true;
 
13668                 cancelAnimFrame(this._animRequest);
 
13670                 var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
 
13671                 this._animRequest = requestAnimFrame(moveFn, this, true);
 
13676         _onTouchEnd: function () {
 
13677                 if (!this._moved || !this._zooming) {
 
13678                         this._zooming = false;
 
13682                 this._zooming = false;
 
13683                 cancelAnimFrame(this._animRequest);
 
13685                 off(document, 'touchmove', this._onTouchMove);
 
13686                 off(document, 'touchend', this._onTouchEnd);
 
13688                 // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
 
13689                 if (this._map.options.zoomAnimation) {
 
13690                         this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
 
13692                         this._map._resetView(this._center, this._map._limitZoom(this._zoom));
 
13697 // @section Handlers
 
13698 // @property touchZoom: Handler
 
13699 // Touch zoom handler.
 
13700 Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
 
13702 Map.BoxZoom = BoxZoom;
 
13703 Map.DoubleClickZoom = DoubleClickZoom;
 
13705 Map.Keyboard = Keyboard;
 
13706 Map.ScrollWheelZoom = ScrollWheelZoom;
 
13708 Map.TouchZoom = TouchZoom;
 
13712 var oldL = window.L;
 
13713 function noConflict() {
 
13718 // Always export us to window global (see #2364)
 
13719 window.L = exports;
 
13721 Object.freeze = freeze;
 
13723 exports.version = version;
 
13724 exports.noConflict = noConflict;
 
13725 exports.Control = Control;
 
13726 exports.control = control;
 
13727 exports.Browser = Browser;
 
13728 exports.Evented = Evented;
 
13729 exports.Mixin = Mixin;
 
13730 exports.Util = Util;
 
13731 exports.Class = Class;
 
13732 exports.Handler = Handler;
 
13733 exports.extend = extend;
 
13734 exports.bind = bind;
 
13735 exports.stamp = stamp;
 
13736 exports.setOptions = setOptions;
 
13737 exports.DomEvent = DomEvent;
 
13738 exports.DomUtil = DomUtil;
 
13739 exports.PosAnimation = PosAnimation;
 
13740 exports.Draggable = Draggable;
 
13741 exports.LineUtil = LineUtil;
 
13742 exports.PolyUtil = PolyUtil;
 
13743 exports.Point = Point;
 
13744 exports.point = toPoint;
 
13745 exports.Bounds = Bounds;
 
13746 exports.bounds = toBounds;
 
13747 exports.Transformation = Transformation;
 
13748 exports.transformation = toTransformation;
 
13749 exports.Projection = index;
 
13750 exports.LatLng = LatLng;
 
13751 exports.latLng = toLatLng;
 
13752 exports.LatLngBounds = LatLngBounds;
 
13753 exports.latLngBounds = toLatLngBounds;
 
13755 exports.GeoJSON = GeoJSON;
 
13756 exports.geoJSON = geoJSON;
 
13757 exports.geoJson = geoJson;
 
13758 exports.Layer = Layer;
 
13759 exports.LayerGroup = LayerGroup;
 
13760 exports.layerGroup = layerGroup;
 
13761 exports.FeatureGroup = FeatureGroup;
 
13762 exports.featureGroup = featureGroup;
 
13763 exports.ImageOverlay = ImageOverlay;
 
13764 exports.imageOverlay = imageOverlay;
 
13765 exports.VideoOverlay = VideoOverlay;
 
13766 exports.videoOverlay = videoOverlay;
 
13767 exports.DivOverlay = DivOverlay;
 
13768 exports.Popup = Popup;
 
13769 exports.popup = popup;
 
13770 exports.Tooltip = Tooltip;
 
13771 exports.tooltip = tooltip;
 
13772 exports.Icon = Icon;
 
13773 exports.icon = icon;
 
13774 exports.DivIcon = DivIcon;
 
13775 exports.divIcon = divIcon;
 
13776 exports.Marker = Marker;
 
13777 exports.marker = marker;
 
13778 exports.TileLayer = TileLayer;
 
13779 exports.tileLayer = tileLayer;
 
13780 exports.GridLayer = GridLayer;
 
13781 exports.gridLayer = gridLayer;
 
13783 exports.svg = svg$1;
 
13784 exports.Renderer = Renderer;
 
13785 exports.Canvas = Canvas;
 
13786 exports.canvas = canvas$1;
 
13787 exports.Path = Path;
 
13788 exports.CircleMarker = CircleMarker;
 
13789 exports.circleMarker = circleMarker;
 
13790 exports.Circle = Circle;
 
13791 exports.circle = circle;
 
13792 exports.Polyline = Polyline;
 
13793 exports.polyline = polyline;
 
13794 exports.Polygon = Polygon;
 
13795 exports.polygon = polygon;
 
13796 exports.Rectangle = Rectangle;
 
13797 exports.rectangle = rectangle;
 
13799 exports.map = createMap;
 
13802 //# sourceMappingURL=leaflet-src.js.map