2  * Leaflet 1.2.0, a JS library for interactive maps. http://leafletjs.com
 
   3  * (c) 2010-2017 Vladimir Agafonkin, (c) 2010-2011 CloudMade
 
   5 (function (global, factory) {
 
   6         typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
 
   7         typeof define === 'function' && define.amd ? define(['exports'], factory) :
 
   8         (factory((global.L = {})));
 
   9 }(this, (function (exports) { 'use strict';
 
  11 var version = "1.2.0";
 
  16  * Various utility functions, used by Leaflet internally.
 
  19 var freeze = Object.freeze;
 
  20 Object.freeze = function (obj) { return obj; };
 
  22 // @function extend(dest: Object, src?: Object): Object
 
  23 // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
 
  24 function extend(dest) {
 
  27         for (j = 1, len = arguments.length; j < len; j++) {
 
  36 // @function create(proto: Object, properties?: Object): Object
 
  37 // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
 
  38 var create = Object.create || (function () {
 
  40         return function (proto) {
 
  46 // @function bind(fn: Function, …): Function
 
  47 // 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).
 
  48 // Has a `L.bind()` shortcut.
 
  49 function bind(fn, obj) {
 
  50         var slice = Array.prototype.slice;
 
  53                 return fn.bind.apply(fn, slice.call(arguments, 1));
 
  56         var args = slice.call(arguments, 2);
 
  59                 return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
 
  63 // @property lastId: Number
 
  64 // Last unique ID used by [`stamp()`](#util-stamp)
 
  67 // @function stamp(obj: Object): Number
 
  68 // Returns the unique ID of an object, assiging it one if it doesn't have it.
 
  71         obj._leaflet_id = obj._leaflet_id || ++lastId;
 
  72         return obj._leaflet_id;
 
  76 // @function throttle(fn: Function, time: Number, context: Object): Function
 
  77 // Returns a function which executes function `fn` with the given scope `context`
 
  78 // (so that the `this` keyword refers to `context` inside `fn`'s code). The function
 
  79 // `fn` will be called no more than one time per given amount of `time`. The arguments
 
  80 // received by the bound function will be any arguments passed when binding the
 
  81 // function, followed by any arguments passed when invoking the bound function.
 
  82 // Has an `L.throttle` shortcut.
 
  83 function throttle(fn, time, context) {
 
  84         var lock, args, wrapperFn, later;
 
  87                 // reset lock and call if queued
 
  90                         wrapperFn.apply(context, args);
 
  95         wrapperFn = function () {
 
  97                         // called too soon, queue to call later
 
 101                         // call and lock until later
 
 102                         fn.apply(context, arguments);
 
 103                         setTimeout(later, time);
 
 111 // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
 
 112 // Returns the number `num` modulo `range` in such a way so it lies within
 
 113 // `range[0]` and `range[1]`. The returned value will be always smaller than
 
 114 // `range[1]` unless `includeMax` is set to `true`.
 
 115 function wrapNum(x, range, includeMax) {
 
 119         return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
 
 122 // @function falseFn(): Function
 
 123 // Returns a function which always returns `false`.
 
 124 function falseFn() { return false; }
 
 126 // @function formatNum(num: Number, digits?: Number): Number
 
 127 // Returns the number `num` rounded to `digits` decimals, or to 5 decimals by default.
 
 128 function formatNum(num, digits) {
 
 129         var pow = Math.pow(10, digits || 5);
 
 130         return Math.round(num * pow) / pow;
 
 133 // @function trim(str: String): String
 
 134 // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
 
 136         return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
 
 139 // @function splitWords(str: String): String[]
 
 140 // Trims and splits the string on whitespace and returns the array of parts.
 
 141 function splitWords(str) {
 
 142         return trim(str).split(/\s+/);
 
 145 // @function setOptions(obj: Object, options: Object): Object
 
 146 // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
 
 147 function setOptions(obj, options) {
 
 148         if (!obj.hasOwnProperty('options')) {
 
 149                 obj.options = obj.options ? create(obj.options) : {};
 
 151         for (var i in options) {
 
 152                 obj.options[i] = options[i];
 
 157 // @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
 
 158 // Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
 
 159 // translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
 
 160 // be appended at the end. If `uppercase` is `true`, the parameter names will
 
 161 // be uppercased (e.g. `'?A=foo&B=bar'`)
 
 162 function getParamString(obj, existingUrl, uppercase) {
 
 165                 params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
 
 167         return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
 
 170 var templateRe = /\{ *([\w_\-]+) *\}/g;
 
 172 // @function template(str: String, data: Object): String
 
 173 // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
 
 174 // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
 
 175 // `('Hello foo, bar')`. You can also specify functions instead of strings for
 
 176 // data values — they will be evaluated passing `data` as an argument.
 
 177 function template(str, data) {
 
 178         return str.replace(templateRe, function (str, key) {
 
 179                 var value = data[key];
 
 181                 if (value === undefined) {
 
 182                         throw new Error('No value provided for variable ' + str);
 
 184                 } else if (typeof value === 'function') {
 
 191 // @function isArray(obj): Boolean
 
 192 // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
 
 193 var isArray = Array.isArray || function (obj) {
 
 194         return (Object.prototype.toString.call(obj) === '[object Array]');
 
 197 // @function indexOf(array: Array, el: Object): Number
 
 198 // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
 
 199 function indexOf(array, el) {
 
 200         for (var i = 0; i < array.length; i++) {
 
 201                 if (array[i] === el) { return i; }
 
 206 // @property emptyImageUrl: String
 
 207 // Data URI string containing a base64-encoded empty GIF image.
 
 208 // Used as a hack to free memory from unused images on WebKit-powered
 
 209 // mobile devices (by setting image `src` to this string).
 
 210 var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
 
 212 // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
 
 214 function getPrefixed(name) {
 
 215         return window['webkit' + name] || window['moz' + name] || window['ms' + name];
 
 220 // fallback for IE 7-8
 
 221 function timeoutDefer(fn) {
 
 222         var time = +new Date(),
 
 223             timeToCall = Math.max(0, 16 - (time - lastTime));
 
 225         lastTime = time + timeToCall;
 
 226         return window.setTimeout(fn, timeToCall);
 
 229 var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer;
 
 230 var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
 
 231                 getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
 
 233 // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
 
 234 // Schedules `fn` to be executed when the browser repaints. `fn` is bound to
 
 235 // `context` if given. When `immediate` is set, `fn` is called immediately if
 
 236 // the browser doesn't have native support for
 
 237 // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
 
 238 // otherwise it's delayed. Returns a request ID that can be used to cancel the request.
 
 239 function requestAnimFrame(fn, context, immediate) {
 
 240         if (immediate && requestFn === timeoutDefer) {
 
 243                 return requestFn.call(window, bind(fn, context));
 
 247 // @function cancelAnimFrame(id: Number): undefined
 
 248 // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
 
 249 function cancelAnimFrame(id) {
 
 251                 cancelFn.call(window, id);
 
 256 var Util = (Object.freeze || Object)({
 
 266         formatNum: formatNum,
 
 268         splitWords: splitWords,
 
 269         setOptions: setOptions,
 
 270         getParamString: getParamString,
 
 274         emptyImageUrl: emptyImageUrl,
 
 275         requestFn: requestFn,
 
 277         requestAnimFrame: requestAnimFrame,
 
 278         cancelAnimFrame: cancelAnimFrame
 
 287 // Thanks to John Resig and Dean Edwards for inspiration!
 
 291 Class.extend = function (props) {
 
 293         // @function extend(props: Object): Function
 
 294         // [Extends the current class](#class-inheritance) given the properties to be included.
 
 295         // Returns a Javascript function that is a class constructor (to be called with `new`).
 
 296         var NewClass = function () {
 
 298                 // call the constructor
 
 299                 if (this.initialize) {
 
 300                         this.initialize.apply(this, arguments);
 
 303                 // call all constructor hooks
 
 304                 this.callInitHooks();
 
 307         var parentProto = NewClass.__super__ = this.prototype;
 
 309         var proto = create(parentProto);
 
 310         proto.constructor = NewClass;
 
 312         NewClass.prototype = proto;
 
 314         // inherit parent's statics
 
 315         for (var i in this) {
 
 316                 if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') {
 
 317                         NewClass[i] = this[i];
 
 321         // mix static properties into the class
 
 323                 extend(NewClass, props.statics);
 
 324                 delete props.statics;
 
 327         // mix includes into the prototype
 
 328         if (props.includes) {
 
 329                 checkDeprecatedMixinEvents(props.includes);
 
 330                 extend.apply(null, [proto].concat(props.includes));
 
 331                 delete props.includes;
 
 336                 props.options = extend(create(proto.options), props.options);
 
 339         // mix given properties into the prototype
 
 340         extend(proto, props);
 
 342         proto._initHooks = [];
 
 344         // add method for calling all hooks
 
 345         proto.callInitHooks = function () {
 
 347                 if (this._initHooksCalled) { return; }
 
 349                 if (parentProto.callInitHooks) {
 
 350                         parentProto.callInitHooks.call(this);
 
 353                 this._initHooksCalled = true;
 
 355                 for (var i = 0, len = proto._initHooks.length; i < len; i++) {
 
 356                         proto._initHooks[i].call(this);
 
 364 // @function include(properties: Object): this
 
 365 // [Includes a mixin](#class-includes) into the current class.
 
 366 Class.include = function (props) {
 
 367         extend(this.prototype, props);
 
 371 // @function mergeOptions(options: Object): this
 
 372 // [Merges `options`](#class-options) into the defaults of the class.
 
 373 Class.mergeOptions = function (options) {
 
 374         extend(this.prototype.options, options);
 
 378 // @function addInitHook(fn: Function): this
 
 379 // Adds a [constructor hook](#class-constructor-hooks) to the class.
 
 380 Class.addInitHook = function (fn) { // (Function) || (String, args...)
 
 381         var args = Array.prototype.slice.call(arguments, 1);
 
 383         var init = typeof fn === 'function' ? fn : function () {
 
 384                 this[fn].apply(this, args);
 
 387         this.prototype._initHooks = this.prototype._initHooks || [];
 
 388         this.prototype._initHooks.push(init);
 
 392 function checkDeprecatedMixinEvents(includes) {
 
 393         if (!L || !L.Mixin) { return; }
 
 395         includes = isArray(includes) ? includes : [includes];
 
 397         for (var i = 0; i < includes.length; i++) {
 
 398                 if (includes[i] === L.Mixin.Events) {
 
 399                         console.warn('Deprecated include of L.Mixin.Events: ' +
 
 400                                 'this property will be removed in future releases, ' +
 
 401                                 'please inherit from L.Evented instead.', new Error().stack);
 
 411  * 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).
 
 416  * map.on('click', function(e) {
 
 421  * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
 
 424  * function onClick(e) { ... }
 
 426  * map.on('click', onClick);
 
 427  * map.off('click', onClick);
 
 432         /* @method on(type: String, fn: Function, context?: Object): this
 
 433          * 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'`).
 
 436          * @method on(eventMap: Object): this
 
 437          * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
 439         on: function (types, fn, context) {
 
 441                 // types can be a map of types/handlers
 
 442                 if (typeof types === 'object') {
 
 443                         for (var type in types) {
 
 444                                 // we don't process space-separated events here for performance;
 
 445                                 // it's a hot path since Layer uses the on(obj) syntax
 
 446                                 this._on(type, types[type], fn);
 
 450                         // types can be a string of space-separated words
 
 451                         types = splitWords(types);
 
 453                         for (var i = 0, len = types.length; i < len; i++) {
 
 454                                 this._on(types[i], fn, context);
 
 461         /* @method off(type: String, fn?: Function, context?: Object): this
 
 462          * 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.
 
 465          * @method off(eventMap: Object): this
 
 466          * Removes a set of type/listener pairs.
 
 470          * Removes all listeners to all events on the object.
 
 472         off: function (types, fn, context) {
 
 475                         // clear all listeners if called without arguments
 
 478                 } else if (typeof types === 'object') {
 
 479                         for (var type in types) {
 
 480                                 this._off(type, types[type], fn);
 
 484                         types = splitWords(types);
 
 486                         for (var i = 0, len = types.length; i < len; i++) {
 
 487                                 this._off(types[i], fn, context);
 
 494         // attach listener (without syntactic sugar now)
 
 495         _on: function (type, fn, context) {
 
 496                 this._events = this._events || {};
 
 498                 /* get/init listeners for type */
 
 499                 var typeListeners = this._events[type];
 
 500                 if (!typeListeners) {
 
 502                         this._events[type] = typeListeners;
 
 505                 if (context === this) {
 
 506                         // Less memory footprint.
 
 509                 var newListener = {fn: fn, ctx: context},
 
 510                     listeners = typeListeners;
 
 512                 // check if fn already there
 
 513                 for (var i = 0, len = listeners.length; i < len; i++) {
 
 514                         if (listeners[i].fn === fn && listeners[i].ctx === context) {
 
 519                 listeners.push(newListener);
 
 522         _off: function (type, fn, context) {
 
 527                 if (!this._events) { return; }
 
 529                 listeners = this._events[type];
 
 536                         // Set all removed listeners to noop so they are not called if remove happens in fire
 
 537                         for (i = 0, len = listeners.length; i < len; i++) {
 
 538                                 listeners[i].fn = falseFn;
 
 540                         // clear all listeners for a type if function isn't specified
 
 541                         delete this._events[type];
 
 545                 if (context === this) {
 
 551                         // find fn and remove it
 
 552                         for (i = 0, len = listeners.length; i < len; i++) {
 
 553                                 var l = listeners[i];
 
 554                                 if (l.ctx !== context) { continue; }
 
 557                                         // set the removed listener to noop so that's not called if remove happens in fire
 
 560                                         if (this._firingCount) {
 
 561                                                 /* copy array in case events are being fired */
 
 562                                                 this._events[type] = listeners = listeners.slice();
 
 564                                         listeners.splice(i, 1);
 
 572         // @method fire(type: String, data?: Object, propagate?: Boolean): this
 
 573         // Fires an event of the specified type. You can optionally provide an data
 
 574         // object — the first argument of the listener function will contain its
 
 575         // properties. The event can optionally be propagated to event parents.
 
 576         fire: function (type, data, propagate) {
 
 577                 if (!this.listens(type, propagate)) { return this; }
 
 579                 var event = extend({}, data, {type: type, target: this});
 
 582                         var listeners = this._events[type];
 
 585                                 this._firingCount = (this._firingCount + 1) || 1;
 
 586                                 for (var i = 0, len = listeners.length; i < len; i++) {
 
 587                                         var l = listeners[i];
 
 588                                         l.fn.call(l.ctx || this, event);
 
 596                         // propagate the event to parents (set with addEventParent)
 
 597                         this._propagateEvent(event);
 
 603         // @method listens(type: String): Boolean
 
 604         // Returns `true` if a particular event type has any listeners attached to it.
 
 605         listens: function (type, propagate) {
 
 606                 var listeners = this._events && this._events[type];
 
 607                 if (listeners && listeners.length) { return true; }
 
 610                         // also check parents for listeners if event propagates
 
 611                         for (var id in this._eventParents) {
 
 612                                 if (this._eventParents[id].listens(type, propagate)) { return true; }
 
 618         // @method once(…): this
 
 619         // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
 
 620         once: function (types, fn, context) {
 
 622                 if (typeof types === 'object') {
 
 623                         for (var type in types) {
 
 624                                 this.once(type, types[type], fn);
 
 629                 var handler = bind(function () {
 
 631                             .off(types, fn, context)
 
 632                             .off(types, handler, context);
 
 635                 // add a listener that's executed once and removed after that
 
 637                     .on(types, fn, context)
 
 638                     .on(types, handler, context);
 
 641         // @method addEventParent(obj: Evented): this
 
 642         // Adds an event parent - an `Evented` that will receive propagated events
 
 643         addEventParent: function (obj) {
 
 644                 this._eventParents = this._eventParents || {};
 
 645                 this._eventParents[stamp(obj)] = obj;
 
 649         // @method removeEventParent(obj: Evented): this
 
 650         // Removes an event parent, so it will stop receiving propagated events
 
 651         removeEventParent: function (obj) {
 
 652                 if (this._eventParents) {
 
 653                         delete this._eventParents[stamp(obj)];
 
 658         _propagateEvent: function (e) {
 
 659                 for (var id in this._eventParents) {
 
 660                         this._eventParents[id].fire(e.type, extend({layer: e.target}, e), true);
 
 665 // aliases; we should ditch those eventually
 
 667 // @method addEventListener(…): this
 
 668 // Alias to [`on(…)`](#evented-on)
 
 669 Events.addEventListener = Events.on;
 
 671 // @method removeEventListener(…): this
 
 672 // Alias to [`off(…)`](#evented-off)
 
 674 // @method clearAllEventListeners(…): this
 
 675 // Alias to [`off()`](#evented-off)
 
 676 Events.removeEventListener = Events.clearAllEventListeners = Events.off;
 
 678 // @method addOneTimeEventListener(…): this
 
 679 // Alias to [`once(…)`](#evented-once)
 
 680 Events.addOneTimeEventListener = Events.once;
 
 682 // @method fireEvent(…): this
 
 683 // Alias to [`fire(…)`](#evented-fire)
 
 684 Events.fireEvent = Events.fire;
 
 686 // @method hasEventListeners(…): Boolean
 
 687 // Alias to [`listens(…)`](#evented-listens)
 
 688 Events.hasEventListeners = Events.listens;
 
 690 var Evented = Class.extend(Events);
 
 696  * Represents a point with `x` and `y` coordinates in pixels.
 
 701  * var point = L.point(200, 300);
 
 704  * All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent:
 
 707  * map.panBy([200, 300]);
 
 708  * map.panBy(L.point(200, 300));
 
 712 function Point(x, y, round) {
 
 713         // @property x: Number; The `x` coordinate of the point
 
 714         this.x = (round ? Math.round(x) : x);
 
 715         // @property y: Number; The `y` coordinate of the point
 
 716         this.y = (round ? Math.round(y) : y);
 
 721         // @method clone(): Point
 
 722         // Returns a copy of the current point.
 
 724                 return new Point(this.x, this.y);
 
 727         // @method add(otherPoint: Point): Point
 
 728         // Returns the result of addition of the current and the given points.
 
 729         add: function (point) {
 
 730                 // non-destructive, returns a new point
 
 731                 return this.clone()._add(toPoint(point));
 
 734         _add: function (point) {
 
 735                 // destructive, used directly for performance in situations where it's safe to modify existing point
 
 741         // @method subtract(otherPoint: Point): Point
 
 742         // Returns the result of subtraction of the given point from the current.
 
 743         subtract: function (point) {
 
 744                 return this.clone()._subtract(toPoint(point));
 
 747         _subtract: function (point) {
 
 753         // @method divideBy(num: Number): Point
 
 754         // Returns the result of division of the current point by the given number.
 
 755         divideBy: function (num) {
 
 756                 return this.clone()._divideBy(num);
 
 759         _divideBy: function (num) {
 
 765         // @method multiplyBy(num: Number): Point
 
 766         // Returns the result of multiplication of the current point by the given number.
 
 767         multiplyBy: function (num) {
 
 768                 return this.clone()._multiplyBy(num);
 
 771         _multiplyBy: function (num) {
 
 777         // @method scaleBy(scale: Point): Point
 
 778         // Multiply each coordinate of the current point by each coordinate of
 
 779         // `scale`. In linear algebra terms, multiply the point by the
 
 780         // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
 
 781         // defined by `scale`.
 
 782         scaleBy: function (point) {
 
 783                 return new Point(this.x * point.x, this.y * point.y);
 
 786         // @method unscaleBy(scale: Point): Point
 
 787         // Inverse of `scaleBy`. Divide each coordinate of the current point by
 
 788         // each coordinate of `scale`.
 
 789         unscaleBy: function (point) {
 
 790                 return new Point(this.x / point.x, this.y / point.y);
 
 793         // @method round(): Point
 
 794         // Returns a copy of the current point with rounded coordinates.
 
 796                 return this.clone()._round();
 
 799         _round: function () {
 
 800                 this.x = Math.round(this.x);
 
 801                 this.y = Math.round(this.y);
 
 805         // @method floor(): Point
 
 806         // Returns a copy of the current point with floored coordinates (rounded down).
 
 808                 return this.clone()._floor();
 
 811         _floor: function () {
 
 812                 this.x = Math.floor(this.x);
 
 813                 this.y = Math.floor(this.y);
 
 817         // @method ceil(): Point
 
 818         // Returns a copy of the current point with ceiled coordinates (rounded up).
 
 820                 return this.clone()._ceil();
 
 824                 this.x = Math.ceil(this.x);
 
 825                 this.y = Math.ceil(this.y);
 
 829         // @method distanceTo(otherPoint: Point): Number
 
 830         // Returns the cartesian distance between the current and the given points.
 
 831         distanceTo: function (point) {
 
 832                 point = toPoint(point);
 
 834                 var x = point.x - this.x,
 
 835                     y = point.y - this.y;
 
 837                 return Math.sqrt(x * x + y * y);
 
 840         // @method equals(otherPoint: Point): Boolean
 
 841         // Returns `true` if the given point has the same coordinates.
 
 842         equals: function (point) {
 
 843                 point = toPoint(point);
 
 845                 return point.x === this.x &&
 
 849         // @method contains(otherPoint: Point): Boolean
 
 850         // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
 
 851         contains: function (point) {
 
 852                 point = toPoint(point);
 
 854                 return Math.abs(point.x) <= Math.abs(this.x) &&
 
 855                        Math.abs(point.y) <= Math.abs(this.y);
 
 858         // @method toString(): String
 
 859         // Returns a string representation of the point for debugging purposes.
 
 860         toString: function () {
 
 862                         formatNum(this.x) + ', ' +
 
 863                         formatNum(this.y) + ')';
 
 867 // @factory L.point(x: Number, y: Number, round?: Boolean)
 
 868 // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
 
 871 // @factory L.point(coords: Number[])
 
 872 // Expects an array of the form `[x, y]` instead.
 
 875 // @factory L.point(coords: Object)
 
 876 // Expects a plain object of the form `{x: Number, y: Number}` instead.
 
 877 function toPoint(x, y, round) {
 
 878         if (x instanceof Point) {
 
 882                 return new Point(x[0], x[1]);
 
 884         if (x === undefined || x === null) {
 
 887         if (typeof x === 'object' && 'x' in x && 'y' in x) {
 
 888                 return new Point(x.x, x.y);
 
 890         return new Point(x, y, round);
 
 897  * Represents a rectangular area in pixel coordinates.
 
 902  * var p1 = L.point(10, 10),
 
 903  * p2 = L.point(40, 60),
 
 904  * bounds = L.bounds(p1, p2);
 
 907  * 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:
 
 910  * otherBounds.intersects([[10, 10], [40, 60]]);
 
 914 function Bounds(a, b) {
 
 917         var points = b ? [a, b] : a;
 
 919         for (var i = 0, len = points.length; i < len; i++) {
 
 920                 this.extend(points[i]);
 
 925         // @method extend(point: Point): this
 
 926         // Extends the bounds to contain the given point.
 
 927         extend: function (point) { // (Point)
 
 928                 point = toPoint(point);
 
 930                 // @property min: Point
 
 931                 // The top left corner of the rectangle.
 
 932                 // @property max: Point
 
 933                 // The bottom right corner of the rectangle.
 
 934                 if (!this.min && !this.max) {
 
 935                         this.min = point.clone();
 
 936                         this.max = point.clone();
 
 938                         this.min.x = Math.min(point.x, this.min.x);
 
 939                         this.max.x = Math.max(point.x, this.max.x);
 
 940                         this.min.y = Math.min(point.y, this.min.y);
 
 941                         this.max.y = Math.max(point.y, this.max.y);
 
 946         // @method getCenter(round?: Boolean): Point
 
 947         // Returns the center point of the bounds.
 
 948         getCenter: function (round) {
 
 950                         (this.min.x + this.max.x) / 2,
 
 951                         (this.min.y + this.max.y) / 2, round);
 
 954         // @method getBottomLeft(): Point
 
 955         // Returns the bottom-left point of the bounds.
 
 956         getBottomLeft: function () {
 
 957                 return new Point(this.min.x, this.max.y);
 
 960         // @method getTopRight(): Point
 
 961         // Returns the top-right point of the bounds.
 
 962         getTopRight: function () { // -> Point
 
 963                 return new Point(this.max.x, this.min.y);
 
 966         // @method getTopLeft(): Point
 
 967         // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)).
 
 968         getTopLeft: function () {
 
 969                 return this.min; // left, top
 
 972         // @method getBottomRight(): Point
 
 973         // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)).
 
 974         getBottomRight: function () {
 
 975                 return this.max; // right, bottom
 
 978         // @method getSize(): Point
 
 979         // Returns the size of the given bounds
 
 980         getSize: function () {
 
 981                 return this.max.subtract(this.min);
 
 984         // @method contains(otherBounds: Bounds): Boolean
 
 985         // Returns `true` if the rectangle contains the given one.
 
 987         // @method contains(point: Point): Boolean
 
 988         // Returns `true` if the rectangle contains the given point.
 
 989         contains: function (obj) {
 
 992                 if (typeof obj[0] === 'number' || obj instanceof Point) {
 
 998                 if (obj instanceof Bounds) {
 
1005                 return (min.x >= this.min.x) &&
 
1006                        (max.x <= this.max.x) &&
 
1007                        (min.y >= this.min.y) &&
 
1008                        (max.y <= this.max.y);
 
1011         // @method intersects(otherBounds: Bounds): Boolean
 
1012         // Returns `true` if the rectangle intersects the given bounds. Two bounds
 
1013         // intersect if they have at least one point in common.
 
1014         intersects: function (bounds) { // (Bounds) -> Boolean
 
1015                 bounds = toBounds(bounds);
 
1021                     xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
 
1022                     yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
 
1024                 return xIntersects && yIntersects;
 
1027         // @method overlaps(otherBounds: Bounds): Boolean
 
1028         // Returns `true` if the rectangle overlaps the given bounds. Two bounds
 
1029         // overlap if their intersection is an area.
 
1030         overlaps: function (bounds) { // (Bounds) -> Boolean
 
1031                 bounds = toBounds(bounds);
 
1037                     xOverlaps = (max2.x > min.x) && (min2.x < max.x),
 
1038                     yOverlaps = (max2.y > min.y) && (min2.y < max.y);
 
1040                 return xOverlaps && yOverlaps;
 
1043         isValid: function () {
 
1044                 return !!(this.min && this.max);
 
1049 // @factory L.bounds(corner1: Point, corner2: Point)
 
1050 // Creates a Bounds object from two corners coordinate pairs.
 
1052 // @factory L.bounds(points: Point[])
 
1053 // Creates a Bounds object from the given array of points.
 
1054 function toBounds(a, b) {
 
1055         if (!a || a instanceof Bounds) {
 
1058         return new Bounds(a, b);
 
1062  * @class LatLngBounds
 
1063  * @aka L.LatLngBounds
 
1065  * Represents a rectangular geographical area on a map.
 
1070  * var corner1 = L.latLng(40.712, -74.227),
 
1071  * corner2 = L.latLng(40.774, -74.125),
 
1072  * bounds = L.latLngBounds(corner1, corner2);
 
1075  * 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:
 
1079  *      [40.712, -74.227],
 
1084  * 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.
 
1087 function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
 
1088         if (!corner1) { return; }
 
1090         var latlngs = corner2 ? [corner1, corner2] : corner1;
 
1092         for (var i = 0, len = latlngs.length; i < len; i++) {
 
1093                 this.extend(latlngs[i]);
 
1097 LatLngBounds.prototype = {
 
1099         // @method extend(latlng: LatLng): this
 
1100         // Extend the bounds to contain the given point
 
1103         // @method extend(otherBounds: LatLngBounds): this
 
1104         // Extend the bounds to contain the given bounds
 
1105         extend: function (obj) {
 
1106                 var sw = this._southWest,
 
1107                     ne = this._northEast,
 
1110                 if (obj instanceof LatLng) {
 
1114                 } else if (obj instanceof LatLngBounds) {
 
1115                         sw2 = obj._southWest;
 
1116                         ne2 = obj._northEast;
 
1118                         if (!sw2 || !ne2) { return this; }
 
1121                         return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this;
 
1125                         this._southWest = new LatLng(sw2.lat, sw2.lng);
 
1126                         this._northEast = new LatLng(ne2.lat, ne2.lng);
 
1128                         sw.lat = Math.min(sw2.lat, sw.lat);
 
1129                         sw.lng = Math.min(sw2.lng, sw.lng);
 
1130                         ne.lat = Math.max(ne2.lat, ne.lat);
 
1131                         ne.lng = Math.max(ne2.lng, ne.lng);
 
1137         // @method pad(bufferRatio: Number): LatLngBounds
 
1138         // Returns bigger bounds created by extending the current bounds by a given percentage in each direction.
 
1139         pad: function (bufferRatio) {
 
1140                 var sw = this._southWest,
 
1141                     ne = this._northEast,
 
1142                     heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
 
1143                     widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
 
1145                 return new LatLngBounds(
 
1146                         new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
 
1147                         new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
 
1150         // @method getCenter(): LatLng
 
1151         // Returns the center point of the bounds.
 
1152         getCenter: function () {
 
1154                         (this._southWest.lat + this._northEast.lat) / 2,
 
1155                         (this._southWest.lng + this._northEast.lng) / 2);
 
1158         // @method getSouthWest(): LatLng
 
1159         // Returns the south-west point of the bounds.
 
1160         getSouthWest: function () {
 
1161                 return this._southWest;
 
1164         // @method getNorthEast(): LatLng
 
1165         // Returns the north-east point of the bounds.
 
1166         getNorthEast: function () {
 
1167                 return this._northEast;
 
1170         // @method getNorthWest(): LatLng
 
1171         // Returns the north-west point of the bounds.
 
1172         getNorthWest: function () {
 
1173                 return new LatLng(this.getNorth(), this.getWest());
 
1176         // @method getSouthEast(): LatLng
 
1177         // Returns the south-east point of the bounds.
 
1178         getSouthEast: function () {
 
1179                 return new LatLng(this.getSouth(), this.getEast());
 
1182         // @method getWest(): Number
 
1183         // Returns the west longitude of the bounds
 
1184         getWest: function () {
 
1185                 return this._southWest.lng;
 
1188         // @method getSouth(): Number
 
1189         // Returns the south latitude of the bounds
 
1190         getSouth: function () {
 
1191                 return this._southWest.lat;
 
1194         // @method getEast(): Number
 
1195         // Returns the east longitude of the bounds
 
1196         getEast: function () {
 
1197                 return this._northEast.lng;
 
1200         // @method getNorth(): Number
 
1201         // Returns the north latitude of the bounds
 
1202         getNorth: function () {
 
1203                 return this._northEast.lat;
 
1206         // @method contains(otherBounds: LatLngBounds): Boolean
 
1207         // Returns `true` if the rectangle contains the given one.
 
1210         // @method contains (latlng: LatLng): Boolean
 
1211         // Returns `true` if the rectangle contains the given point.
 
1212         contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
 
1213                 if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) {
 
1214                         obj = toLatLng(obj);
 
1216                         obj = toLatLngBounds(obj);
 
1219                 var sw = this._southWest,
 
1220                     ne = this._northEast,
 
1223                 if (obj instanceof LatLngBounds) {
 
1224                         sw2 = obj.getSouthWest();
 
1225                         ne2 = obj.getNorthEast();
 
1230                 return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
 
1231                        (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
 
1234         // @method intersects(otherBounds: LatLngBounds): Boolean
 
1235         // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
 
1236         intersects: function (bounds) {
 
1237                 bounds = toLatLngBounds(bounds);
 
1239                 var sw = this._southWest,
 
1240                     ne = this._northEast,
 
1241                     sw2 = bounds.getSouthWest(),
 
1242                     ne2 = bounds.getNorthEast(),
 
1244                     latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
 
1245                     lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
 
1247                 return latIntersects && lngIntersects;
 
1250         // @method overlaps(otherBounds: Bounds): Boolean
 
1251         // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
 
1252         overlaps: function (bounds) {
 
1253                 bounds = toLatLngBounds(bounds);
 
1255                 var sw = this._southWest,
 
1256                     ne = this._northEast,
 
1257                     sw2 = bounds.getSouthWest(),
 
1258                     ne2 = bounds.getNorthEast(),
 
1260                     latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
 
1261                     lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
 
1263                 return latOverlaps && lngOverlaps;
 
1266         // @method toBBoxString(): String
 
1267         // 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.
 
1268         toBBoxString: function () {
 
1269                 return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
 
1272         // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean
 
1273         // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overriden by setting `maxMargin` to a small number.
 
1274         equals: function (bounds, maxMargin) {
 
1275                 if (!bounds) { return false; }
 
1277                 bounds = toLatLngBounds(bounds);
 
1279                 return this._southWest.equals(bounds.getSouthWest(), maxMargin) &&
 
1280                        this._northEast.equals(bounds.getNorthEast(), maxMargin);
 
1283         // @method isValid(): Boolean
 
1284         // Returns `true` if the bounds are properly initialized.
 
1285         isValid: function () {
 
1286                 return !!(this._southWest && this._northEast);
 
1290 // TODO International date line?
 
1292 // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
 
1293 // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
 
1296 // @factory L.latLngBounds(latlngs: LatLng[])
 
1297 // 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).
 
1298 function toLatLngBounds(a, b) {
 
1299         if (a instanceof LatLngBounds) {
 
1302         return new LatLngBounds(a, b);
 
1308  * Represents a geographical point with a certain latitude and longitude.
 
1313  * var latlng = L.latLng(50.5, 30.5);
 
1316  * 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:
 
1319  * map.panTo([50, 30]);
 
1320  * map.panTo({lon: 30, lat: 50});
 
1321  * map.panTo({lat: 50, lng: 30});
 
1322  * map.panTo(L.latLng(50, 30));
 
1326 function LatLng(lat, lng, alt) {
 
1327         if (isNaN(lat) || isNaN(lng)) {
 
1328                 throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
 
1331         // @property lat: Number
 
1332         // Latitude in degrees
 
1335         // @property lng: Number
 
1336         // Longitude in degrees
 
1339         // @property alt: Number
 
1340         // Altitude in meters (optional)
 
1341         if (alt !== undefined) {
 
1346 LatLng.prototype = {
 
1347         // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
 
1348         // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overriden by setting `maxMargin` to a small number.
 
1349         equals: function (obj, maxMargin) {
 
1350                 if (!obj) { return false; }
 
1352                 obj = toLatLng(obj);
 
1354                 var margin = Math.max(
 
1355                         Math.abs(this.lat - obj.lat),
 
1356                         Math.abs(this.lng - obj.lng));
 
1358                 return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
 
1361         // @method toString(): String
 
1362         // Returns a string representation of the point (for debugging purposes).
 
1363         toString: function (precision) {
 
1365                         formatNum(this.lat, precision) + ', ' +
 
1366                         formatNum(this.lng, precision) + ')';
 
1369         // @method distanceTo(otherLatLng: LatLng): Number
 
1370         // Returns the distance (in meters) to the given `LatLng` calculated using the [Haversine formula](http://en.wikipedia.org/wiki/Haversine_formula).
 
1371         distanceTo: function (other) {
 
1372                 return Earth.distance(this, toLatLng(other));
 
1375         // @method wrap(): LatLng
 
1376         // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
 
1378                 return Earth.wrapLatLng(this);
 
1381         // @method toBounds(sizeInMeters: Number): LatLngBounds
 
1382         // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
 
1383         toBounds: function (sizeInMeters) {
 
1384                 var latAccuracy = 180 * sizeInMeters / 40075017,
 
1385                     lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
 
1387                 return toLatLngBounds(
 
1388                         [this.lat - latAccuracy, this.lng - lngAccuracy],
 
1389                         [this.lat + latAccuracy, this.lng + lngAccuracy]);
 
1392         clone: function () {
 
1393                 return new LatLng(this.lat, this.lng, this.alt);
 
1399 // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
 
1400 // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
 
1403 // @factory L.latLng(coords: Array): LatLng
 
1404 // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
 
1407 // @factory L.latLng(coords: Object): LatLng
 
1408 // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
 
1410 function toLatLng(a, b, c) {
 
1411         if (a instanceof LatLng) {
 
1414         if (isArray(a) && typeof a[0] !== 'object') {
 
1415                 if (a.length === 3) {
 
1416                         return new LatLng(a[0], a[1], a[2]);
 
1418                 if (a.length === 2) {
 
1419                         return new LatLng(a[0], a[1]);
 
1423         if (a === undefined || a === null) {
 
1426         if (typeof a === 'object' && 'lat' in a) {
 
1427                 return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
 
1429         if (b === undefined) {
 
1432         return new LatLng(a, b, c);
 
1438  * Object that defines coordinate reference systems for projecting
 
1439  * geographical points into pixel (screen) coordinates and back (and to
 
1440  * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
 
1441  * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
 
1443  * Leaflet defines the most usual CRSs by default. If you want to use a
 
1444  * CRS not defined by default, take a look at the
 
1445  * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
 
1449         // @method latLngToPoint(latlng: LatLng, zoom: Number): Point
 
1450         // Projects geographical coordinates into pixel coordinates for a given zoom.
 
1451         latLngToPoint: function (latlng, zoom) {
 
1452                 var projectedPoint = this.projection.project(latlng),
 
1453                     scale = this.scale(zoom);
 
1455                 return this.transformation._transform(projectedPoint, scale);
 
1458         // @method pointToLatLng(point: Point, zoom: Number): LatLng
 
1459         // The inverse of `latLngToPoint`. Projects pixel coordinates on a given
 
1460         // zoom into geographical coordinates.
 
1461         pointToLatLng: function (point, zoom) {
 
1462                 var scale = this.scale(zoom),
 
1463                     untransformedPoint = this.transformation.untransform(point, scale);
 
1465                 return this.projection.unproject(untransformedPoint);
 
1468         // @method project(latlng: LatLng): Point
 
1469         // Projects geographical coordinates into coordinates in units accepted for
 
1470         // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
 
1471         project: function (latlng) {
 
1472                 return this.projection.project(latlng);
 
1475         // @method unproject(point: Point): LatLng
 
1476         // Given a projected coordinate returns the corresponding LatLng.
 
1477         // The inverse of `project`.
 
1478         unproject: function (point) {
 
1479                 return this.projection.unproject(point);
 
1482         // @method scale(zoom: Number): Number
 
1483         // Returns the scale used when transforming projected coordinates into
 
1484         // pixel coordinates for a particular zoom. For example, it returns
 
1485         // `256 * 2^zoom` for Mercator-based CRS.
 
1486         scale: function (zoom) {
 
1487                 return 256 * Math.pow(2, zoom);
 
1490         // @method zoom(scale: Number): Number
 
1491         // Inverse of `scale()`, returns the zoom level corresponding to a scale
 
1492         // factor of `scale`.
 
1493         zoom: function (scale) {
 
1494                 return Math.log(scale / 256) / Math.LN2;
 
1497         // @method getProjectedBounds(zoom: Number): Bounds
 
1498         // Returns the projection's bounds scaled and transformed for the provided `zoom`.
 
1499         getProjectedBounds: function (zoom) {
 
1500                 if (this.infinite) { return null; }
 
1502                 var b = this.projection.bounds,
 
1503                     s = this.scale(zoom),
 
1504                     min = this.transformation.transform(b.min, s),
 
1505                     max = this.transformation.transform(b.max, s);
 
1507                 return new Bounds(min, max);
 
1510         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
 
1511         // Returns the distance between two geographical coordinates.
 
1513         // @property code: String
 
1514         // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
 
1516         // @property wrapLng: Number[]
 
1517         // An array of two numbers defining whether the longitude (horizontal) coordinate
 
1518         // axis wraps around a given range and how. Defaults to `[-180, 180]` in most
 
1519         // geographical CRSs. If `undefined`, the longitude axis does not wrap around.
 
1521         // @property wrapLat: Number[]
 
1522         // Like `wrapLng`, but for the latitude (vertical) axis.
 
1524         // wrapLng: [min, max],
 
1525         // wrapLat: [min, max],
 
1527         // @property infinite: Boolean
 
1528         // If true, the coordinate space will be unbounded (infinite in both axes)
 
1531         // @method wrapLatLng(latlng: LatLng): LatLng
 
1532         // Returns a `LatLng` where lat and lng has been wrapped according to the
 
1533         // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
 
1534         wrapLatLng: function (latlng) {
 
1535                 var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
 
1536                     lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
 
1539                 return new LatLng(lat, lng, alt);
 
1542         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
 
1543         // Returns a `LatLngBounds` with the same size as the given one, ensuring
 
1544         // that its center is within the CRS's bounds.
 
1545         // Only accepts actual `L.LatLngBounds` instances, not arrays.
 
1546         wrapLatLngBounds: function (bounds) {
 
1547                 var center = bounds.getCenter(),
 
1548                     newCenter = this.wrapLatLng(center),
 
1549                     latShift = center.lat - newCenter.lat,
 
1550                     lngShift = center.lng - newCenter.lng;
 
1552                 if (latShift === 0 && lngShift === 0) {
 
1556                 var sw = bounds.getSouthWest(),
 
1557                     ne = bounds.getNorthEast(),
 
1558                     newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift),
 
1559                     newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift);
 
1561                 return new LatLngBounds(newSw, newNe);
 
1569  * Serves as the base for CRS that are global such that they cover the earth.
 
1570  * Can only be used as the base for other CRS and cannot be used directly,
 
1571  * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
 
1575 var Earth = extend({}, CRS, {
 
1576         wrapLng: [-180, 180],
 
1578         // Mean Earth Radius, as recommended for use by
 
1579         // the International Union of Geodesy and Geophysics,
 
1580         // see http://rosettacode.org/wiki/Haversine_formula
 
1583         // distance between two geographical points using spherical law of cosines approximation
 
1584         distance: function (latlng1, latlng2) {
 
1585                 var rad = Math.PI / 180,
 
1586                     lat1 = latlng1.lat * rad,
 
1587                     lat2 = latlng2.lat * rad,
 
1588                     a = Math.sin(lat1) * Math.sin(lat2) +
 
1589                         Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlng2.lng - latlng1.lng) * rad);
 
1591                 return this.R * Math.acos(Math.min(a, 1));
 
1596  * @namespace Projection
 
1597  * @projection L.Projection.SphericalMercator
 
1599  * Spherical Mercator projection — the most common projection for online maps,
 
1600  * used by almost all free and commercial tile providers. Assumes that Earth is
 
1601  * a sphere. Used by the `EPSG:3857` CRS.
 
1604 var SphericalMercator = {
 
1607         MAX_LATITUDE: 85.0511287798,
 
1609         project: function (latlng) {
 
1610                 var d = Math.PI / 180,
 
1611                     max = this.MAX_LATITUDE,
 
1612                     lat = Math.max(Math.min(max, latlng.lat), -max),
 
1613                     sin = Math.sin(lat * d);
 
1616                                 this.R * latlng.lng * d,
 
1617                                 this.R * Math.log((1 + sin) / (1 - sin)) / 2);
 
1620         unproject: function (point) {
 
1621                 var d = 180 / Math.PI;
 
1624                         (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
 
1625                         point.x * d / this.R);
 
1628         bounds: (function () {
 
1629                 var d = 6378137 * Math.PI;
 
1630                 return new Bounds([-d, -d], [d, d]);
 
1635  * @class Transformation
 
1636  * @aka L.Transformation
 
1638  * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
 
1639  * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
 
1640  * the reverse. Used by Leaflet in its projections code.
 
1645  * var transformation = L.transformation(2, 5, -1, 10),
 
1646  *      p = L.point(1, 2),
 
1647  *      p2 = transformation.transform(p), //  L.point(7, 8)
 
1648  *      p3 = transformation.untransform(p2); //  L.point(1, 2)
 
1653 // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
 
1654 // Creates a `Transformation` object with the given coefficients.
 
1655 function Transformation(a, b, c, d) {
 
1657                 // use array properties
 
1670 Transformation.prototype = {
 
1671         // @method transform(point: Point, scale?: Number): Point
 
1672         // Returns a transformed point, optionally multiplied by the given scale.
 
1673         // Only accepts actual `L.Point` instances, not arrays.
 
1674         transform: function (point, scale) { // (Point, Number) -> Point
 
1675                 return this._transform(point.clone(), scale);
 
1678         // destructive transform (faster)
 
1679         _transform: function (point, scale) {
 
1681                 point.x = scale * (this._a * point.x + this._b);
 
1682                 point.y = scale * (this._c * point.y + this._d);
 
1686         // @method untransform(point: Point, scale?: Number): Point
 
1687         // Returns the reverse transformation of the given point, optionally divided
 
1688         // by the given scale. Only accepts actual `L.Point` instances, not arrays.
 
1689         untransform: function (point, scale) {
 
1692                         (point.x / scale - this._b) / this._a,
 
1693                         (point.y / scale - this._d) / this._c);
 
1697 // factory L.transformation(a: Number, b: Number, c: Number, d: Number)
 
1699 // @factory L.transformation(a: Number, b: Number, c: Number, d: Number)
 
1700 // Instantiates a Transformation object with the given coefficients.
 
1703 // @factory L.transformation(coefficients: Array): Transformation
 
1704 // Expects an coeficients array of the form
 
1705 // `[a: Number, b: Number, c: Number, d: Number]`.
 
1707 function toTransformation(a, b, c, d) {
 
1708         return new Transformation(a, b, c, d);
 
1713  * @crs L.CRS.EPSG3857
 
1715  * The most common CRS for online maps, used by almost all free and commercial
 
1716  * tile providers. Uses Spherical Mercator projection. Set in by default in
 
1717  * Map's `crs` option.
 
1720 var EPSG3857 = extend({}, Earth, {
 
1722         projection: SphericalMercator,
 
1724         transformation: (function () {
 
1725                 var scale = 0.5 / (Math.PI * SphericalMercator.R);
 
1726                 return toTransformation(scale, 0.5, -scale, 0.5);
 
1730 var EPSG900913 = extend({}, EPSG3857, {
 
1734 // @namespace SVG; @section
 
1735 // There are several static functions which can be called without instantiating L.SVG:
 
1737 // @function create(name: String): SVGElement
 
1738 // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
 
1739 // corresponding to the class name passed. For example, using 'line' will return
 
1740 // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
 
1741 function svgCreate(name) {
 
1742         return document.createElementNS('http://www.w3.org/2000/svg', name);
 
1745 // @function pointsToPath(rings: Point[], closed: Boolean): String
 
1746 // Generates a SVG path string for multiple rings, with each ring turning
 
1747 // into "M..L..L.." instructions
 
1748 function pointsToPath(rings, closed) {
 
1750         i, j, len, len2, points, p;
 
1752         for (i = 0, len = rings.length; i < len; i++) {
 
1755                 for (j = 0, len2 = points.length; j < len2; j++) {
 
1757                         str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
 
1760                 // closes the ring for polygons; "x" is VML syntax
 
1761                 str += closed ? (svg ? 'z' : 'x') : '';
 
1764         // SVG complains about empty path strings
 
1765         return str || 'M0 0';
 
1769  * @namespace Browser
 
1772  * A namespace with static properties for browser/feature detection used by Leaflet internally.
 
1777  * if (L.Browser.ielt9) {
 
1778  *   alert('Upgrade your browser, dude!');
 
1783 var style$1 = document.documentElement.style;
 
1785 // @property ie: Boolean; `true` for all Internet Explorer versions (not Edge).
 
1786 var ie = 'ActiveXObject' in window;
 
1788 // @property ielt9: Boolean; `true` for Internet Explorer versions less than 9.
 
1789 var ielt9 = ie && !document.addEventListener;
 
1791 // @property edge: Boolean; `true` for the Edge web browser.
 
1792 var edge = 'msLaunchUri' in navigator && !('documentMode' in document);
 
1794 // @property webkit: Boolean;
 
1795 // `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
 
1796 var webkit = userAgentContains('webkit');
 
1798 // @property android: Boolean
 
1799 // `true` for any browser running on an Android platform.
 
1800 var android = userAgentContains('android');
 
1802 // @property android23: Boolean; `true` for browsers running on Android 2 or Android 3.
 
1803 var android23 = userAgentContains('android 2') || userAgentContains('android 3');
 
1805 // @property opera: Boolean; `true` for the Opera browser
 
1806 var opera = !!window.opera;
 
1808 // @property chrome: Boolean; `true` for the Chrome browser.
 
1809 var chrome = userAgentContains('chrome');
 
1811 // @property gecko: Boolean; `true` for gecko-based browsers like Firefox.
 
1812 var gecko = userAgentContains('gecko') && !webkit && !opera && !ie;
 
1814 // @property safari: Boolean; `true` for the Safari browser.
 
1815 var safari = !chrome && userAgentContains('safari');
 
1817 var phantom = userAgentContains('phantom');
 
1819 // @property opera12: Boolean
 
1820 // `true` for the Opera browser supporting CSS transforms (version 12 or later).
 
1821 var opera12 = 'OTransition' in style$1;
 
1823 // @property win: Boolean; `true` when the browser is running in a Windows platform
 
1824 var win = navigator.platform.indexOf('Win') === 0;
 
1826 // @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms.
 
1827 var ie3d = ie && ('transition' in style$1);
 
1829 // @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms.
 
1830 var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23;
 
1832 // @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms.
 
1833 var gecko3d = 'MozPerspective' in style$1;
 
1835 // @property any3d: Boolean
 
1836 // `true` for all browsers supporting CSS transforms.
 
1837 var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom;
 
1839 // @property mobile: Boolean; `true` for all browsers running in a mobile device.
 
1840 var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile');
 
1842 // @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device.
 
1843 var mobileWebkit = mobile && webkit;
 
1845 // @property mobileWebkit3d: Boolean
 
1846 // `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
 
1847 var mobileWebkit3d = mobile && webkit3d;
 
1849 // @property msPointer: Boolean
 
1850 // `true` for browsers implementing the Microsoft touch events model (notably IE10).
 
1851 var msPointer = !window.PointerEvent && window.MSPointerEvent;
 
1853 // @property pointer: Boolean
 
1854 // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
 
1855 var pointer = !!(window.PointerEvent || msPointer);
 
1857 // @property touch: Boolean
 
1858 // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
 
1859 // This does not necessarily mean that the browser is running in a computer with
 
1860 // a touchscreen, it only means that the browser is capable of understanding
 
1862 var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
 
1863                 (window.DocumentTouch && document instanceof window.DocumentTouch));
 
1865 // @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device.
 
1866 var mobileOpera = mobile && opera;
 
1868 // @property mobileGecko: Boolean
 
1869 // `true` for gecko-based browsers running in a mobile device.
 
1870 var mobileGecko = mobile && gecko;
 
1872 // @property retina: Boolean
 
1873 // `true` for browsers on a high-resolution "retina" screen.
 
1874 var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1;
 
1877 // @property canvas: Boolean
 
1878 // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
 
1879 var canvas = (function () {
 
1880         return !!document.createElement('canvas').getContext;
 
1883 // @property svg: Boolean
 
1884 // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
 
1885 var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect);
 
1887 // @property vml: Boolean
 
1888 // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
 
1889 var vml = !svg && (function () {
 
1891                 var div = document.createElement('div');
 
1892                 div.innerHTML = '<v:shape adj="1"/>';
 
1894                 var shape = div.firstChild;
 
1895                 shape.style.behavior = 'url(#default#VML)';
 
1897                 return shape && (typeof shape.adj === 'object');
 
1905 function userAgentContains(str) {
 
1906         return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
 
1910 var Browser = (Object.freeze || Object)({
 
1916         android23: android23,
 
1929         mobileWebkit: mobileWebkit,
 
1930         mobileWebkit3d: mobileWebkit3d,
 
1931         msPointer: msPointer,
 
1934         mobileOpera: mobileOpera,
 
1935         mobileGecko: mobileGecko,
 
1943  * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
 
1947 var POINTER_DOWN =   msPointer ? 'MSPointerDown'   : 'pointerdown';
 
1948 var POINTER_MOVE =   msPointer ? 'MSPointerMove'   : 'pointermove';
 
1949 var POINTER_UP =     msPointer ? 'MSPointerUp'     : 'pointerup';
 
1950 var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel';
 
1951 var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION'];
 
1953 var _pointerDocListener = false;
 
1955 // DomEvent.DoubleTap needs to know about this
 
1956 var _pointersCount = 0;
 
1958 // Provides a touch events wrapper for (ms)pointer events.
 
1959 // ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
 
1961 function addPointerListener(obj, type, handler, id) {
 
1962         if (type === 'touchstart') {
 
1963                 _addPointerStart(obj, handler, id);
 
1965         } else if (type === 'touchmove') {
 
1966                 _addPointerMove(obj, handler, id);
 
1968         } else if (type === 'touchend') {
 
1969                 _addPointerEnd(obj, handler, id);
 
1975 function removePointerListener(obj, type, id) {
 
1976         var handler = obj['_leaflet_' + type + id];
 
1978         if (type === 'touchstart') {
 
1979                 obj.removeEventListener(POINTER_DOWN, handler, false);
 
1981         } else if (type === 'touchmove') {
 
1982                 obj.removeEventListener(POINTER_MOVE, handler, false);
 
1984         } else if (type === 'touchend') {
 
1985                 obj.removeEventListener(POINTER_UP, handler, false);
 
1986                 obj.removeEventListener(POINTER_CANCEL, handler, false);
 
1992 function _addPointerStart(obj, handler, id) {
 
1993         var onDown = bind(function (e) {
 
1994                 if (e.pointerType !== 'mouse' && e.pointerType !== e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
 
1995                         // In IE11, some touch events needs to fire for form controls, or
 
1996                         // the controls will stop working. We keep a whitelist of tag names that
 
1997                         // need these events. For other target tags, we prevent default on the event.
 
1998                         if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
 
2005                 _handlePointer(e, handler);
 
2008         obj['_leaflet_touchstart' + id] = onDown;
 
2009         obj.addEventListener(POINTER_DOWN, onDown, false);
 
2011         // need to keep track of what pointers and how many are active to provide e.touches emulation
 
2012         if (!_pointerDocListener) {
 
2013                 // we listen documentElement as any drags that end by moving the touch off the screen get fired there
 
2014                 document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true);
 
2015                 document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true);
 
2016                 document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true);
 
2017                 document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
 
2019                 _pointerDocListener = true;
 
2023 function _globalPointerDown(e) {
 
2024         _pointers[e.pointerId] = e;
 
2028 function _globalPointerMove(e) {
 
2029         if (_pointers[e.pointerId]) {
 
2030                 _pointers[e.pointerId] = e;
 
2034 function _globalPointerUp(e) {
 
2035         delete _pointers[e.pointerId];
 
2039 function _handlePointer(e, handler) {
 
2041         for (var i in _pointers) {
 
2042                 e.touches.push(_pointers[i]);
 
2044         e.changedTouches = [e];
 
2049 function _addPointerMove(obj, handler, id) {
 
2050         var onMove = function (e) {
 
2051                 // don't fire touch moves when mouse isn't down
 
2052                 if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
 
2054                 _handlePointer(e, handler);
 
2057         obj['_leaflet_touchmove' + id] = onMove;
 
2058         obj.addEventListener(POINTER_MOVE, onMove, false);
 
2061 function _addPointerEnd(obj, handler, id) {
 
2062         var onUp = function (e) {
 
2063                 _handlePointer(e, handler);
 
2066         obj['_leaflet_touchend' + id] = onUp;
 
2067         obj.addEventListener(POINTER_UP, onUp, false);
 
2068         obj.addEventListener(POINTER_CANCEL, onUp, false);
 
2072  * Extends the event handling code with double tap support for mobile browsers.
 
2075 var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart';
 
2076 var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend';
 
2077 var _pre = '_leaflet_';
 
2079 // inspired by Zepto touch code by Thomas Fuchs
 
2080 function addDoubleTapListener(obj, handler, id) {
 
2085         function onTouchStart(e) {
 
2089                         if ((!edge) || e.pointerType === 'mouse') { return; }
 
2090                         count = _pointersCount;
 
2092                         count = e.touches.length;
 
2095                 if (count > 1) { return; }
 
2097                 var now = Date.now(),
 
2098                     delta = now - (last || now);
 
2100                 touch$$1 = e.touches ? e.touches[0] : e;
 
2101                 doubleTap = (delta > 0 && delta <= delay);
 
2105         function onTouchEnd(e) {
 
2106                 if (doubleTap && !touch$$1.cancelBubble) {
 
2108                                 if ((!edge) || e.pointerType === 'mouse') { return; }
 
2109                                 // work around .type being readonly with MSPointer* events
 
2113                                 for (i in touch$$1) {
 
2115                                         newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop;
 
2117                                 touch$$1 = newTouch;
 
2119                         touch$$1.type = 'dblclick';
 
2125         obj[_pre + _touchstart + id] = onTouchStart;
 
2126         obj[_pre + _touchend + id] = onTouchEnd;
 
2127         obj[_pre + 'dblclick' + id] = handler;
 
2129         obj.addEventListener(_touchstart, onTouchStart, false);
 
2130         obj.addEventListener(_touchend, onTouchEnd, false);
 
2132         // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse),
 
2133         // the browser doesn't fire touchend/pointerup events but does fire
 
2134         // native dblclicks. See #4127.
 
2135         // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180.
 
2136         obj.addEventListener('dblclick', handler, false);
 
2141 function removeDoubleTapListener(obj, id) {
 
2142         var touchstart = obj[_pre + _touchstart + id],
 
2143             touchend = obj[_pre + _touchend + id],
 
2144             dblclick = obj[_pre + 'dblclick' + id];
 
2146         obj.removeEventListener(_touchstart, touchstart, false);
 
2147         obj.removeEventListener(_touchend, touchend, false);
 
2149                 obj.removeEventListener('dblclick', dblclick, false);
 
2156  * @namespace DomEvent
 
2157  * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
 
2160 // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
 
2162 // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
 
2163 // Adds a listener function (`fn`) to a particular DOM event type of the
 
2164 // element `el`. You can optionally specify the context of the listener
 
2165 // (object the `this` keyword will point to). You can also pass several
 
2166 // space-separated types (e.g. `'click dblclick'`).
 
2169 // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
 
2170 // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
2171 function on(obj, types, fn, context) {
 
2173         if (typeof types === 'object') {
 
2174                 for (var type in types) {
 
2175                         addOne(obj, type, types[type], fn);
 
2178                 types = splitWords(types);
 
2180                 for (var i = 0, len = types.length; i < len; i++) {
 
2181                         addOne(obj, types[i], fn, context);
 
2188 var eventsKey = '_leaflet_events';
 
2190 // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
 
2191 // Removes a previously added listener function. If no function is specified,
 
2192 // it will remove all the listeners of that particular DOM event from the element.
 
2193 // Note that if you passed a custom context to on, you must pass the same
 
2194 // context to `off` in order to remove the listener.
 
2197 // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
 
2198 // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
2201 // @function off(el: HTMLElement): this
 
2202 // Removes all known event listeners
 
2203 function off(obj, types, fn, context) {
 
2205         if (typeof types === 'object') {
 
2206                 for (var type in types) {
 
2207                         removeOne(obj, type, types[type], fn);
 
2210                 types = splitWords(types);
 
2212                 for (var i = 0, len = types.length; i < len; i++) {
 
2213                         removeOne(obj, types[i], fn, context);
 
2216                 for (var j in obj[eventsKey]) {
 
2217                         removeOne(obj, j, obj[eventsKey][j]);
 
2219                 delete obj[eventsKey];
 
2225 function addOne(obj, type, fn, context) {
 
2226         var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
 
2228         if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
 
2230         var handler = function (e) {
 
2231                 return fn.call(context || obj, e || window.event);
 
2234         var originalHandler = handler;
 
2236         if (pointer && type.indexOf('touch') === 0) {
 
2237                 // Needs DomEvent.Pointer.js
 
2238                 addPointerListener(obj, type, handler, id);
 
2240         } else if (touch && (type === 'dblclick') && addDoubleTapListener &&
 
2241                    !(pointer && chrome)) {
 
2242                 // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
 
2244                 addDoubleTapListener(obj, handler, id);
 
2246         } else if ('addEventListener' in obj) {
 
2248                 if (type === 'mousewheel') {
 
2249                         obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
 
2251                 } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
 
2252                         handler = function (e) {
 
2253                                 e = e || window.event;
 
2254                                 if (isExternalTarget(obj, e)) {
 
2258                         obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
 
2261                         if (type === 'click' && android) {
 
2262                                 handler = function (e) {
 
2263                                         filterClick(e, originalHandler);
 
2266                         obj.addEventListener(type, handler, false);
 
2269         } else if ('attachEvent' in obj) {
 
2270                 obj.attachEvent('on' + type, handler);
 
2273         obj[eventsKey] = obj[eventsKey] || {};
 
2274         obj[eventsKey][id] = handler;
 
2277 function removeOne(obj, type, fn, context) {
 
2279         var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''),
 
2280             handler = obj[eventsKey] && obj[eventsKey][id];
 
2282         if (!handler) { return this; }
 
2284         if (pointer && type.indexOf('touch') === 0) {
 
2285                 removePointerListener(obj, type, id);
 
2287         } else if (touch && (type === 'dblclick') && removeDoubleTapListener) {
 
2288                 removeDoubleTapListener(obj, id);
 
2290         } else if ('removeEventListener' in obj) {
 
2292                 if (type === 'mousewheel') {
 
2293                         obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
 
2296                         obj.removeEventListener(
 
2297                                 type === 'mouseenter' ? 'mouseover' :
 
2298                                 type === 'mouseleave' ? 'mouseout' : type, handler, false);
 
2301         } else if ('detachEvent' in obj) {
 
2302                 obj.detachEvent('on' + type, handler);
 
2305         obj[eventsKey][id] = null;
 
2308 // @function stopPropagation(ev: DOMEvent): this
 
2309 // Stop the given event from propagation to parent elements. Used inside the listener functions:
 
2311 // L.DomEvent.on(div, 'click', function (ev) {
 
2312 //      L.DomEvent.stopPropagation(ev);
 
2315 function stopPropagation(e) {
 
2317         if (e.stopPropagation) {
 
2318                 e.stopPropagation();
 
2319         } else if (e.originalEvent) {  // In case of Leaflet event.
 
2320                 e.originalEvent._stopped = true;
 
2322                 e.cancelBubble = true;
 
2329 // @function disableScrollPropagation(el: HTMLElement): this
 
2330 // Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
 
2331 function disableScrollPropagation(el) {
 
2332         addOne(el, 'mousewheel', stopPropagation);
 
2336 // @function disableClickPropagation(el: HTMLElement): this
 
2337 // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
 
2338 // `'mousedown'` and `'touchstart'` events (plus browser variants).
 
2339 function disableClickPropagation(el) {
 
2340         on(el, 'mousedown touchstart dblclick', stopPropagation);
 
2341         addOne(el, 'click', fakeStop);
 
2345 // @function preventDefault(ev: DOMEvent): this
 
2346 // Prevents the default action of the DOM Event `ev` from happening (such as
 
2347 // following a link in the href of the a element, or doing a POST request
 
2348 // with page reload when a `<form>` is submitted).
 
2349 // Use it inside listener functions.
 
2350 function preventDefault(e) {
 
2351         if (e.preventDefault) {
 
2354                 e.returnValue = false;
 
2359 // @function stop(ev): this
 
2360 // Does `stopPropagation` and `preventDefault` at the same time.
 
2367 // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
 
2368 // Gets normalized mouse position from a DOM event relative to the
 
2369 // `container` or to the whole page if not specified.
 
2370 function getMousePosition(e, container) {
 
2372                 return new Point(e.clientX, e.clientY);
 
2375         var rect = container.getBoundingClientRect();
 
2378                 e.clientX - rect.left - container.clientLeft,
 
2379                 e.clientY - rect.top - container.clientTop);
 
2382 // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
 
2383 // and Firefox scrolls device pixels, not CSS pixels
 
2385         (win && chrome) ? 2 * window.devicePixelRatio :
 
2386         gecko ? window.devicePixelRatio : 1;
 
2388 // @function getWheelDelta(ev: DOMEvent): Number
 
2389 // Gets normalized wheel delta from a mousewheel DOM event, in vertical
 
2390 // pixels scrolled (negative if scrolling down).
 
2391 // Events from pointing devices without precise scrolling are mapped to
 
2392 // a best guess of 60 pixels.
 
2393 function getWheelDelta(e) {
 
2394         return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
 
2395                (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
 
2396                (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
 
2397                (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
 
2398                (e.deltaX || e.deltaZ) ? 0 :     // Skip horizontal/depth wheel events
 
2399                e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
 
2400                (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
 
2401                e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
 
2405 var skipEvents = {};
 
2407 function fakeStop(e) {
 
2408         // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e)
 
2409         skipEvents[e.type] = true;
 
2412 function skipped(e) {
 
2413         var events = skipEvents[e.type];
 
2414         // reset when checking, as it's only used in map container and propagates outside of the map
 
2415         skipEvents[e.type] = false;
 
2419 // check if element really left/entered the event target (for mouseenter/mouseleave)
 
2420 function isExternalTarget(el, e) {
 
2422         var related = e.relatedTarget;
 
2424         if (!related) { return true; }
 
2427                 while (related && (related !== el)) {
 
2428                         related = related.parentNode;
 
2433         return (related !== el);
 
2438 // this is a horrible workaround for a bug in Android where a single touch triggers two click events
 
2439 function filterClick(e, handler) {
 
2440         var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
 
2441             elapsed = lastClick && (timeStamp - lastClick);
 
2443         // are they closer together than 500ms yet more than 100ms?
 
2444         // Android typically triggers them ~300ms apart while multiple listeners
 
2445         // on the same event should be triggered far faster;
 
2446         // or check if click is simulated on the element, and if it is, reject any non-simulated events
 
2448         if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
 
2452         lastClick = timeStamp;
 
2460 var DomEvent = (Object.freeze || Object)({
 
2463         stopPropagation: stopPropagation,
 
2464         disableScrollPropagation: disableScrollPropagation,
 
2465         disableClickPropagation: disableClickPropagation,
 
2466         preventDefault: preventDefault,
 
2468         getMousePosition: getMousePosition,
 
2469         getWheelDelta: getWheelDelta,
 
2472         isExternalTarget: isExternalTarget,
 
2478  * @namespace DomUtil
 
2480  * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
 
2481  * tree, used by Leaflet internally.
 
2483  * Most functions expecting or returning a `HTMLElement` also work for
 
2484  * SVG elements. The only difference is that classes refer to CSS classes
 
2485  * in HTML and SVG classes in SVG.
 
2489 // @property TRANSFORM: String
 
2490 // Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
 
2491 var TRANSFORM = testProp(
 
2492         ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
 
2494 // webkitTransition comes first because some browser versions that drop vendor prefix don't do
 
2495 // the same for the transitionend event, in particular the Android 4.1 stock browser
 
2497 // @property TRANSITION: String
 
2498 // Vendor-prefixed transition style name.
 
2499 var TRANSITION = testProp(
 
2500         ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
 
2502 // @property TRANSITION_END: String
 
2503 // Vendor-prefixed transitionend event name.
 
2504 var TRANSITION_END =
 
2505         TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
 
2508 // @function get(id: String|HTMLElement): HTMLElement
 
2509 // Returns an element given its DOM id, or returns the element itself
 
2510 // if it was passed directly.
 
2512         return typeof id === 'string' ? document.getElementById(id) : id;
 
2515 // @function getStyle(el: HTMLElement, styleAttrib: String): String
 
2516 // Returns the value for a certain style attribute on an element,
 
2517 // including computed values or values set through CSS.
 
2518 function getStyle(el, style) {
 
2519         var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
 
2521         if ((!value || value === 'auto') && document.defaultView) {
 
2522                 var css = document.defaultView.getComputedStyle(el, null);
 
2523                 value = css ? css[style] : null;
 
2525         return value === 'auto' ? null : value;
 
2528 // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
 
2529 // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
 
2530 function create$1(tagName, className, container) {
 
2531         var el = document.createElement(tagName);
 
2532         el.className = className || '';
 
2535                 container.appendChild(el);
 
2540 // @function remove(el: HTMLElement)
 
2541 // Removes `el` from its parent element
 
2542 function remove(el) {
 
2543         var parent = el.parentNode;
 
2545                 parent.removeChild(el);
 
2549 // @function empty(el: HTMLElement)
 
2550 // Removes all of `el`'s children elements from `el`
 
2551 function empty(el) {
 
2552         while (el.firstChild) {
 
2553                 el.removeChild(el.firstChild);
 
2557 // @function toFront(el: HTMLElement)
 
2558 // Makes `el` the last child of its parent, so it renders in front of the other children.
 
2559 function toFront(el) {
 
2560         var parent = el.parentNode;
 
2561         if (parent.lastChild !== el) {
 
2562                 parent.appendChild(el);
 
2566 // @function toBack(el: HTMLElement)
 
2567 // Makes `el` the first child of its parent, so it renders behind the other children.
 
2568 function toBack(el) {
 
2569         var parent = el.parentNode;
 
2570         if (parent.firstChild !== el) {
 
2571                 parent.insertBefore(el, parent.firstChild);
 
2575 // @function hasClass(el: HTMLElement, name: String): Boolean
 
2576 // Returns `true` if the element's class attribute contains `name`.
 
2577 function hasClass(el, name) {
 
2578         if (el.classList !== undefined) {
 
2579                 return el.classList.contains(name);
 
2581         var className = getClass(el);
 
2582         return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
 
2585 // @function addClass(el: HTMLElement, name: String)
 
2586 // Adds `name` to the element's class attribute.
 
2587 function addClass(el, name) {
 
2588         if (el.classList !== undefined) {
 
2589                 var classes = splitWords(name);
 
2590                 for (var i = 0, len = classes.length; i < len; i++) {
 
2591                         el.classList.add(classes[i]);
 
2593         } else if (!hasClass(el, name)) {
 
2594                 var className = getClass(el);
 
2595                 setClass(el, (className ? className + ' ' : '') + name);
 
2599 // @function removeClass(el: HTMLElement, name: String)
 
2600 // Removes `name` from the element's class attribute.
 
2601 function removeClass(el, name) {
 
2602         if (el.classList !== undefined) {
 
2603                 el.classList.remove(name);
 
2605                 setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
 
2609 // @function setClass(el: HTMLElement, name: String)
 
2610 // Sets the element's class.
 
2611 function setClass(el, name) {
 
2612         if (el.className.baseVal === undefined) {
 
2613                 el.className = name;
 
2615                 // in case of SVG element
 
2616                 el.className.baseVal = name;
 
2620 // @function getClass(el: HTMLElement): String
 
2621 // Returns the element's class.
 
2622 function getClass(el) {
 
2623         return el.className.baseVal === undefined ? el.className : el.className.baseVal;
 
2626 // @function setOpacity(el: HTMLElement, opacity: Number)
 
2627 // Set the opacity of an element (including old IE support).
 
2628 // `opacity` must be a number from `0` to `1`.
 
2629 function setOpacity(el, value) {
 
2630         if ('opacity' in el.style) {
 
2631                 el.style.opacity = value;
 
2632         } else if ('filter' in el.style) {
 
2633                 _setOpacityIE(el, value);
 
2637 function _setOpacityIE(el, value) {
 
2639             filterName = 'DXImageTransform.Microsoft.Alpha';
 
2641         // filters collection throws an error if we try to retrieve a filter that doesn't exist
 
2643                 filter = el.filters.item(filterName);
 
2645                 // don't set opacity to 1 if we haven't already set an opacity,
 
2646                 // it isn't needed and breaks transparent pngs.
 
2647                 if (value === 1) { return; }
 
2650         value = Math.round(value * 100);
 
2653                 filter.Enabled = (value !== 100);
 
2654                 filter.Opacity = value;
 
2656                 el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
 
2660 // @function testProp(props: String[]): String|false
 
2661 // Goes through the array of style names and returns the first name
 
2662 // that is a valid style name for an element. If no such name is found,
 
2663 // it returns false. Useful for vendor-prefixed styles like `transform`.
 
2664 function testProp(props) {
 
2665         var style = document.documentElement.style;
 
2667         for (var i = 0; i < props.length; i++) {
 
2668                 if (props[i] in style) {
 
2675 // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
 
2676 // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
 
2677 // and optionally scaled by `scale`. Does not have an effect if the
 
2678 // browser doesn't support 3D CSS transforms.
 
2679 function setTransform(el, offset, scale) {
 
2680         var pos = offset || new Point(0, 0);
 
2682         el.style[TRANSFORM] =
 
2684                         'translate(' + pos.x + 'px,' + pos.y + 'px)' :
 
2685                         'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
 
2686                 (scale ? ' scale(' + scale + ')' : '');
 
2689 // @function setPosition(el: HTMLElement, position: Point)
 
2690 // Sets the position of `el` to coordinates specified by `position`,
 
2691 // using CSS translate or top/left positioning depending on the browser
 
2692 // (used by Leaflet internally to position its layers).
 
2693 function setPosition(el, point) {
 
2696         el._leaflet_pos = point;
 
2700                 setTransform(el, point);
 
2702                 el.style.left = point.x + 'px';
 
2703                 el.style.top = point.y + 'px';
 
2707 // @function getPosition(el: HTMLElement): Point
 
2708 // Returns the coordinates of an element previously positioned with setPosition.
 
2709 function getPosition(el) {
 
2710         // this method is only used for elements previously positioned using setPosition,
 
2711         // so it's safe to cache the position for performance
 
2713         return el._leaflet_pos || new Point(0, 0);
 
2716 // @function disableTextSelection()
 
2717 // Prevents the user from generating `selectstart` DOM events, usually generated
 
2718 // when the user drags the mouse through a page with text. Used internally
 
2719 // by Leaflet to override the behaviour of any click-and-drag interaction on
 
2720 // the map. Affects drag interactions on the whole document.
 
2722 // @function enableTextSelection()
 
2723 // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
 
2724 var disableTextSelection;
 
2725 var enableTextSelection;
 
2727 if ('onselectstart' in document) {
 
2728         disableTextSelection = function () {
 
2729                 on(window, 'selectstart', preventDefault);
 
2731         enableTextSelection = function () {
 
2732                 off(window, 'selectstart', preventDefault);
 
2735         var userSelectProperty = testProp(
 
2736                 ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
 
2738         disableTextSelection = function () {
 
2739                 if (userSelectProperty) {
 
2740                         var style = document.documentElement.style;
 
2741                         _userSelect = style[userSelectProperty];
 
2742                         style[userSelectProperty] = 'none';
 
2745         enableTextSelection = function () {
 
2746                 if (userSelectProperty) {
 
2747                         document.documentElement.style[userSelectProperty] = _userSelect;
 
2748                         _userSelect = undefined;
 
2753 // @function disableImageDrag()
 
2754 // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
 
2755 // for `dragstart` DOM events, usually generated when the user drags an image.
 
2756 function disableImageDrag() {
 
2757         on(window, 'dragstart', preventDefault);
 
2760 // @function enableImageDrag()
 
2761 // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
 
2762 function enableImageDrag() {
 
2763         off(window, 'dragstart', preventDefault);
 
2766 var _outlineElement;
 
2768 // @function preventOutline(el: HTMLElement)
 
2769 // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
 
2770 // of the element `el` invisible. Used internally by Leaflet to prevent
 
2771 // focusable elements from displaying an outline when the user performs a
 
2772 // drag interaction on them.
 
2773 function preventOutline(element) {
 
2774         while (element.tabIndex === -1) {
 
2775                 element = element.parentNode;
 
2777         if (!element.style) { return; }
 
2779         _outlineElement = element;
 
2780         _outlineStyle = element.style.outline;
 
2781         element.style.outline = 'none';
 
2782         on(window, 'keydown', restoreOutline);
 
2785 // @function restoreOutline()
 
2786 // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
 
2787 function restoreOutline() {
 
2788         if (!_outlineElement) { return; }
 
2789         _outlineElement.style.outline = _outlineStyle;
 
2790         _outlineElement = undefined;
 
2791         _outlineStyle = undefined;
 
2792         off(window, 'keydown', restoreOutline);
 
2796 var DomUtil = (Object.freeze || Object)({
 
2797         TRANSFORM: TRANSFORM,
 
2798         TRANSITION: TRANSITION,
 
2799         TRANSITION_END: TRANSITION_END,
 
2809         removeClass: removeClass,
 
2812         setOpacity: setOpacity,
 
2814         setTransform: setTransform,
 
2815         setPosition: setPosition,
 
2816         getPosition: getPosition,
 
2817         disableTextSelection: disableTextSelection,
 
2818         enableTextSelection: enableTextSelection,
 
2819         disableImageDrag: disableImageDrag,
 
2820         enableImageDrag: enableImageDrag,
 
2821         preventOutline: preventOutline,
 
2822         restoreOutline: restoreOutline
 
2826  * @class PosAnimation
 
2827  * @aka L.PosAnimation
 
2829  * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
 
2833  * var fx = new L.PosAnimation();
 
2834  * fx.run(el, [300, 500], 0.5);
 
2837  * @constructor L.PosAnimation()
 
2838  * Creates a `PosAnimation` object.
 
2842 var PosAnimation = Evented.extend({
 
2844         // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
 
2845         // Run an animation of a given element to a new position, optionally setting
 
2846         // duration in seconds (`0.25` by default) and easing linearity factor (3rd
 
2847         // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
 
2848         // `0.5` by default).
 
2849         run: function (el, newPos, duration, easeLinearity) {
 
2853                 this._inProgress = true;
 
2854                 this._duration = duration || 0.25;
 
2855                 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
 
2857                 this._startPos = getPosition(el);
 
2858                 this._offset = newPos.subtract(this._startPos);
 
2859                 this._startTime = +new Date();
 
2861                 // @event start: Event
 
2862                 // Fired when the animation starts
 
2869         // Stops the animation (if currently running).
 
2871                 if (!this._inProgress) { return; }
 
2877         _animate: function () {
 
2879                 this._animId = requestAnimFrame(this._animate, this);
 
2883         _step: function (round) {
 
2884                 var elapsed = (+new Date()) - this._startTime,
 
2885                     duration = this._duration * 1000;
 
2887                 if (elapsed < duration) {
 
2888                         this._runFrame(this._easeOut(elapsed / duration), round);
 
2895         _runFrame: function (progress, round) {
 
2896                 var pos = this._startPos.add(this._offset.multiplyBy(progress));
 
2900                 setPosition(this._el, pos);
 
2902                 // @event step: Event
 
2903                 // Fired continuously during the animation.
 
2907         _complete: function () {
 
2908                 cancelAnimFrame(this._animId);
 
2910                 this._inProgress = false;
 
2911                 // @event end: Event
 
2912                 // Fired when the animation ends.
 
2916         _easeOut: function (t) {
 
2917                 return 1 - Math.pow(1 - t, this._easeOutPower);
 
2926  * The central class of the API — it is used to create a map on a page and manipulate it.
 
2931  * // initialize the map on the "map" div with a given center and zoom
 
2932  * var map = L.map('map', {
 
2933  *      center: [51.505, -0.09],
 
2940 var Map = Evented.extend({
 
2943                 // @section Map State Options
 
2944                 // @option crs: CRS = L.CRS.EPSG3857
 
2945                 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
 
2946                 // sure what it means.
 
2949                 // @option center: LatLng = undefined
 
2950                 // Initial geographic center of the map
 
2953                 // @option zoom: Number = undefined
 
2954                 // Initial map zoom level
 
2957                 // @option minZoom: Number = *
 
2958                 // Minimum zoom level of the map.
 
2959                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
 
2960                 // the lowest of their `minZoom` options will be used instead.
 
2963                 // @option maxZoom: Number = *
 
2964                 // Maximum zoom level of the map.
 
2965                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
 
2966                 // the highest of their `maxZoom` options will be used instead.
 
2969                 // @option layers: Layer[] = []
 
2970                 // Array of layers that will be added to the map initially
 
2973                 // @option maxBounds: LatLngBounds = null
 
2974                 // When this option is set, the map restricts the view to the given
 
2975                 // geographical bounds, bouncing the user back if the user tries to pan
 
2976                 // outside the view. To set the restriction dynamically, use
 
2977                 // [`setMaxBounds`](#map-setmaxbounds) method.
 
2978                 maxBounds: undefined,
 
2980                 // @option renderer: Renderer = *
 
2981                 // The default method for drawing vector layers on the map. `L.SVG`
 
2982                 // or `L.Canvas` by default depending on browser support.
 
2983                 renderer: undefined,
 
2986                 // @section Animation Options
 
2987                 // @option zoomAnimation: Boolean = true
 
2988                 // Whether the map zoom animation is enabled. By default it's enabled
 
2989                 // in all browsers that support CSS3 Transitions except Android.
 
2990                 zoomAnimation: true,
 
2992                 // @option zoomAnimationThreshold: Number = 4
 
2993                 // Won't animate zoom if the zoom difference exceeds this value.
 
2994                 zoomAnimationThreshold: 4,
 
2996                 // @option fadeAnimation: Boolean = true
 
2997                 // Whether the tile fade animation is enabled. By default it's enabled
 
2998                 // in all browsers that support CSS3 Transitions except Android.
 
2999                 fadeAnimation: true,
 
3001                 // @option markerZoomAnimation: Boolean = true
 
3002                 // Whether markers animate their zoom with the zoom animation, if disabled
 
3003                 // they will disappear for the length of the animation. By default it's
 
3004                 // enabled in all browsers that support CSS3 Transitions except Android.
 
3005                 markerZoomAnimation: true,
 
3007                 // @option transform3DLimit: Number = 2^23
 
3008                 // Defines the maximum size of a CSS translation transform. The default
 
3009                 // value should not be changed unless a web browser positions layers in
 
3010                 // the wrong place after doing a large `panBy`.
 
3011                 transform3DLimit: 8388608, // Precision limit of a 32-bit float
 
3013                 // @section Interaction Options
 
3014                 // @option zoomSnap: Number = 1
 
3015                 // Forces the map's zoom level to always be a multiple of this, particularly
 
3016                 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
 
3017                 // By default, the zoom level snaps to the nearest integer; lower values
 
3018                 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
 
3019                 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
 
3022                 // @option zoomDelta: Number = 1
 
3023                 // Controls how much the map's zoom level will change after a
 
3024                 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
 
3025                 // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
 
3026                 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
 
3029                 // @option trackResize: Boolean = true
 
3030                 // Whether the map automatically handles browser window resize to update itself.
 
3034         initialize: function (id, options) { // (HTMLElement or String, Object)
 
3035                 options = setOptions(this, options);
 
3037                 this._initContainer(id);
 
3040                 // hack for https://github.com/Leaflet/Leaflet/issues/1980
 
3041                 this._onResize = bind(this._onResize, this);
 
3045                 if (options.maxBounds) {
 
3046                         this.setMaxBounds(options.maxBounds);
 
3049                 if (options.zoom !== undefined) {
 
3050                         this._zoom = this._limitZoom(options.zoom);
 
3053                 if (options.center && options.zoom !== undefined) {
 
3054                         this.setView(toLatLng(options.center), options.zoom, {reset: true});
 
3057                 this._handlers = [];
 
3059                 this._zoomBoundLayers = {};
 
3060                 this._sizeChanged = true;
 
3062                 this.callInitHooks();
 
3064                 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
 
3065                 this._zoomAnimated = TRANSITION && any3d && !mobileOpera &&
 
3066                                 this.options.zoomAnimation;
 
3068                 // zoom transitions run with the same duration for all layers, so if one of transitionend events
 
3069                 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
 
3070                 if (this._zoomAnimated) {
 
3071                         this._createAnimProxy();
 
3072                         on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
 
3075                 this._addLayers(this.options.layers);
 
3079         // @section Methods for modifying map state
 
3081         // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
 
3082         // Sets the view of the map (geographical center and zoom) with the given
 
3083         // animation options.
 
3084         setView: function (center, zoom, options) {
 
3086                 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
 
3087                 center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
 
3088                 options = options || {};
 
3092                 if (this._loaded && !options.reset && options !== true) {
 
3094                         if (options.animate !== undefined) {
 
3095                                 options.zoom = extend({animate: options.animate}, options.zoom);
 
3096                                 options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
 
3099                         // try animating pan or zoom
 
3100                         var moved = (this._zoom !== zoom) ?
 
3101                                 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
 
3102                                 this._tryAnimatedPan(center, options.pan);
 
3105                                 // prevent resize handler call, the view will refresh after animation anyway
 
3106                                 clearTimeout(this._sizeTimer);
 
3111                 // animation didn't start, just reset the map view
 
3112                 this._resetView(center, zoom);
 
3117         // @method setZoom(zoom: Number, options?: Zoom/pan options): this
 
3118         // Sets the zoom of the map.
 
3119         setZoom: function (zoom, options) {
 
3120                 if (!this._loaded) {
 
3124                 return this.setView(this.getCenter(), zoom, {zoom: options});
 
3127         // @method zoomIn(delta?: Number, options?: Zoom options): this
 
3128         // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
 
3129         zoomIn: function (delta, options) {
 
3130                 delta = delta || (any3d ? this.options.zoomDelta : 1);
 
3131                 return this.setZoom(this._zoom + delta, options);
 
3134         // @method zoomOut(delta?: Number, options?: Zoom options): this
 
3135         // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
 
3136         zoomOut: function (delta, options) {
 
3137                 delta = delta || (any3d ? this.options.zoomDelta : 1);
 
3138                 return this.setZoom(this._zoom - delta, options);
 
3141         // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
 
3142         // Zooms the map while keeping a specified geographical point on the map
 
3143         // stationary (e.g. used internally for scroll zoom and double-click zoom).
 
3145         // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
 
3146         // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
 
3147         setZoomAround: function (latlng, zoom, options) {
 
3148                 var scale = this.getZoomScale(zoom),
 
3149                     viewHalf = this.getSize().divideBy(2),
 
3150                     containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
 
3152                     centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
 
3153                     newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
 
3155                 return this.setView(newCenter, zoom, {zoom: options});
 
3158         _getBoundsCenterZoom: function (bounds, options) {
 
3160                 options = options || {};
 
3161                 bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
 
3163                 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
 
3164                     paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
 
3166                     zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
 
3168                 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
 
3170                 if (zoom === Infinity) {
 
3172                                 center: bounds.getCenter(),
 
3177                 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
 
3179                     swPoint = this.project(bounds.getSouthWest(), zoom),
 
3180                     nePoint = this.project(bounds.getNorthEast(), zoom),
 
3181                     center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
 
3189         // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
 
3190         // Sets a map view that contains the given geographical bounds with the
 
3191         // maximum zoom level possible.
 
3192         fitBounds: function (bounds, options) {
 
3194                 bounds = toLatLngBounds(bounds);
 
3196                 if (!bounds.isValid()) {
 
3197                         throw new Error('Bounds are not valid.');
 
3200                 var target = this._getBoundsCenterZoom(bounds, options);
 
3201                 return this.setView(target.center, target.zoom, options);
 
3204         // @method fitWorld(options?: fitBounds options): this
 
3205         // Sets a map view that mostly contains the whole world with the maximum
 
3206         // zoom level possible.
 
3207         fitWorld: function (options) {
 
3208                 return this.fitBounds([[-90, -180], [90, 180]], options);
 
3211         // @method panTo(latlng: LatLng, options?: Pan options): this
 
3212         // Pans the map to a given center.
 
3213         panTo: function (center, options) { // (LatLng)
 
3214                 return this.setView(center, this._zoom, {pan: options});
 
3217         // @method panBy(offset: Point, options?: Pan options): this
 
3218         // Pans the map by a given number of pixels (animated).
 
3219         panBy: function (offset, options) {
 
3220                 offset = toPoint(offset).round();
 
3221                 options = options || {};
 
3223                 if (!offset.x && !offset.y) {
 
3224                         return this.fire('moveend');
 
3226                 // If we pan too far, Chrome gets issues with tiles
 
3227                 // and makes them disappear or appear in the wrong place (slightly offset) #2602
 
3228                 if (options.animate !== true && !this.getSize().contains(offset)) {
 
3229                         this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
 
3233                 if (!this._panAnim) {
 
3234                         this._panAnim = new PosAnimation();
 
3237                                 'step': this._onPanTransitionStep,
 
3238                                 'end': this._onPanTransitionEnd
 
3242                 // don't fire movestart if animating inertia
 
3243                 if (!options.noMoveStart) {
 
3244                         this.fire('movestart');
 
3247                 // animate pan unless animate: false specified
 
3248                 if (options.animate !== false) {
 
3249                         addClass(this._mapPane, 'leaflet-pan-anim');
 
3251                         var newPos = this._getMapPanePos().subtract(offset).round();
 
3252                         this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
 
3254                         this._rawPanBy(offset);
 
3255                         this.fire('move').fire('moveend');
 
3261         // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
 
3262         // Sets the view of the map (geographical center and zoom) performing a smooth
 
3263         // pan-zoom animation.
 
3264         flyTo: function (targetCenter, targetZoom, options) {
 
3266                 options = options || {};
 
3267                 if (options.animate === false || !any3d) {
 
3268                         return this.setView(targetCenter, targetZoom, options);
 
3273                 var from = this.project(this.getCenter()),
 
3274                     to = this.project(targetCenter),
 
3275                     size = this.getSize(),
 
3276                     startZoom = this._zoom;
 
3278                 targetCenter = toLatLng(targetCenter);
 
3279                 targetZoom = targetZoom === undefined ? startZoom : targetZoom;
 
3281                 var w0 = Math.max(size.x, size.y),
 
3282                     w1 = w0 * this.getZoomScale(startZoom, targetZoom),
 
3283                     u1 = (to.distanceTo(from)) || 1,
 
3288                         var s1 = i ? -1 : 1,
 
3290                             t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
 
3291                             b1 = 2 * s2 * rho2 * u1,
 
3293                             sq = Math.sqrt(b * b + 1) - b;
 
3295                             // workaround for floating point precision bug when sq = 0, log = -Infinite,
 
3296                             // thus triggering an infinite loop in flyTo
 
3297                             var log = sq < 0.000000001 ? -18 : Math.log(sq);
 
3302                 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
 
3303                 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
 
3304                 function tanh(n) { return sinh(n) / cosh(n); }
 
3308                 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
 
3309                 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
 
3311                 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
 
3313                 var start = Date.now(),
 
3314                     S = (r(1) - r0) / rho,
 
3315                     duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
 
3318                         var t = (Date.now() - start) / duration,
 
3322                                 this._flyToFrame = requestAnimFrame(frame, this);
 
3325                                         this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
 
3326                                         this.getScaleZoom(w0 / w(s), startZoom),
 
3331                                         ._move(targetCenter, targetZoom)
 
3336                 this._moveStart(true);
 
3342         // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
 
3343         // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
 
3344         // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
 
3345         flyToBounds: function (bounds, options) {
 
3346                 var target = this._getBoundsCenterZoom(bounds, options);
 
3347                 return this.flyTo(target.center, target.zoom, options);
 
3350         // @method setMaxBounds(bounds: Bounds): this
 
3351         // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
 
3352         setMaxBounds: function (bounds) {
 
3353                 bounds = toLatLngBounds(bounds);
 
3355                 if (!bounds.isValid()) {
 
3356                         this.options.maxBounds = null;
 
3357                         return this.off('moveend', this._panInsideMaxBounds);
 
3358                 } else if (this.options.maxBounds) {
 
3359                         this.off('moveend', this._panInsideMaxBounds);
 
3362                 this.options.maxBounds = bounds;
 
3365                         this._panInsideMaxBounds();
 
3368                 return this.on('moveend', this._panInsideMaxBounds);
 
3371         // @method setMinZoom(zoom: Number): this
 
3372         // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
 
3373         setMinZoom: function (zoom) {
 
3374                 this.options.minZoom = zoom;
 
3376                 if (this._loaded && this.getZoom() < this.options.minZoom) {
 
3377                         return this.setZoom(zoom);
 
3383         // @method setMaxZoom(zoom: Number): this
 
3384         // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
 
3385         setMaxZoom: function (zoom) {
 
3386                 this.options.maxZoom = zoom;
 
3388                 if (this._loaded && (this.getZoom() > this.options.maxZoom)) {
 
3389                         return this.setZoom(zoom);
 
3395         // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
 
3396         // 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.
 
3397         panInsideBounds: function (bounds, options) {
 
3398                 this._enforcingBounds = true;
 
3399                 var center = this.getCenter(),
 
3400                     newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
 
3402                 if (!center.equals(newCenter)) {
 
3403                         this.panTo(newCenter, options);
 
3406                 this._enforcingBounds = false;
 
3410         // @method invalidateSize(options: Zoom/Pan options): this
 
3411         // Checks if the map container size changed and updates the map if so —
 
3412         // call it after you've changed the map size dynamically, also animating
 
3413         // pan by default. If `options.pan` is `false`, panning will not occur.
 
3414         // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
 
3415         // that it doesn't happen often even if the method is called many
 
3419         // @method invalidateSize(animate: Boolean): this
 
3420         // Checks if the map container size changed and updates the map if so —
 
3421         // call it after you've changed the map size dynamically, also animating
 
3423         invalidateSize: function (options) {
 
3424                 if (!this._loaded) { return this; }
 
3429                 }, options === true ? {animate: true} : options);
 
3431                 var oldSize = this.getSize();
 
3432                 this._sizeChanged = true;
 
3433                 this._lastCenter = null;
 
3435                 var newSize = this.getSize(),
 
3436                     oldCenter = oldSize.divideBy(2).round(),
 
3437                     newCenter = newSize.divideBy(2).round(),
 
3438                     offset = oldCenter.subtract(newCenter);
 
3440                 if (!offset.x && !offset.y) { return this; }
 
3442                 if (options.animate && options.pan) {
 
3447                                 this._rawPanBy(offset);
 
3452                         if (options.debounceMoveend) {
 
3453                                 clearTimeout(this._sizeTimer);
 
3454                                 this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
 
3456                                 this.fire('moveend');
 
3460                 // @section Map state change events
 
3461                 // @event resize: ResizeEvent
 
3462                 // Fired when the map is resized.
 
3463                 return this.fire('resize', {
 
3469         // @section Methods for modifying map state
 
3470         // @method stop(): this
 
3471         // Stops the currently running `panTo` or `flyTo` animation, if any.
 
3473                 this.setZoom(this._limitZoom(this._zoom));
 
3474                 if (!this.options.zoomSnap) {
 
3475                         this.fire('viewreset');
 
3477                 return this._stop();
 
3480         // @section Geolocation methods
 
3481         // @method locate(options?: Locate options): this
 
3482         // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
 
3483         // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
 
3484         // and optionally sets the map view to the user's location with respect to
 
3485         // detection accuracy (or to the world view if geolocation failed).
 
3486         // Note that, if your page doesn't use HTTPS, this method will fail in
 
3487         // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
 
3488         // See `Locate options` for more details.
 
3489         locate: function (options) {
 
3491                 options = this._locateOptions = extend({
 
3495                         // maxZoom: <Number>
 
3497                         // enableHighAccuracy: false
 
3500                 if (!('geolocation' in navigator)) {
 
3501                         this._handleGeolocationError({
 
3503                                 message: 'Geolocation not supported.'
 
3508                 var onResponse = bind(this._handleGeolocationResponse, this),
 
3509                     onError = bind(this._handleGeolocationError, this);
 
3511                 if (options.watch) {
 
3512                         this._locationWatchId =
 
3513                                 navigator.geolocation.watchPosition(onResponse, onError, options);
 
3515                         navigator.geolocation.getCurrentPosition(onResponse, onError, options);
 
3520         // @method stopLocate(): this
 
3521         // Stops watching location previously initiated by `map.locate({watch: true})`
 
3522         // and aborts resetting the map view if map.locate was called with
 
3523         // `{setView: true}`.
 
3524         stopLocate: function () {
 
3525                 if (navigator.geolocation && navigator.geolocation.clearWatch) {
 
3526                         navigator.geolocation.clearWatch(this._locationWatchId);
 
3528                 if (this._locateOptions) {
 
3529                         this._locateOptions.setView = false;
 
3534         _handleGeolocationError: function (error) {
 
3536                     message = error.message ||
 
3537                             (c === 1 ? 'permission denied' :
 
3538                             (c === 2 ? 'position unavailable' : 'timeout'));
 
3540                 if (this._locateOptions.setView && !this._loaded) {
 
3544                 // @section Location events
 
3545                 // @event locationerror: ErrorEvent
 
3546                 // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
 
3547                 this.fire('locationerror', {
 
3549                         message: 'Geolocation error: ' + message + '.'
 
3553         _handleGeolocationResponse: function (pos) {
 
3554                 var lat = pos.coords.latitude,
 
3555                     lng = pos.coords.longitude,
 
3556                     latlng = new LatLng(lat, lng),
 
3557                     bounds = latlng.toBounds(pos.coords.accuracy),
 
3558                     options = this._locateOptions;
 
3560                 if (options.setView) {
 
3561                         var zoom = this.getBoundsZoom(bounds);
 
3562                         this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
 
3568                         timestamp: pos.timestamp
 
3571                 for (var i in pos.coords) {
 
3572                         if (typeof pos.coords[i] === 'number') {
 
3573                                 data[i] = pos.coords[i];
 
3577                 // @event locationfound: LocationEvent
 
3578                 // Fired when geolocation (using the [`locate`](#map-locate) method)
 
3579                 // went successfully.
 
3580                 this.fire('locationfound', data);
 
3583         // TODO handler.addTo
 
3584         // TODO Appropiate docs section?
 
3585         // @section Other Methods
 
3586         // @method addHandler(name: String, HandlerClass: Function): this
 
3587         // Adds a new `Handler` to the map, given its name and constructor function.
 
3588         addHandler: function (name, HandlerClass) {
 
3589                 if (!HandlerClass) { return this; }
 
3591                 var handler = this[name] = new HandlerClass(this);
 
3593                 this._handlers.push(handler);
 
3595                 if (this.options[name]) {
 
3602         // @method remove(): this
 
3603         // Destroys the map and clears all related event listeners.
 
3604         remove: function () {
 
3606                 this._initEvents(true);
 
3608                 if (this._containerId !== this._container._leaflet_id) {
 
3609                         throw new Error('Map container is being reused by another instance');
 
3613                         // throws error in IE6-8
 
3614                         delete this._container._leaflet_id;
 
3615                         delete this._containerId;
 
3618                         this._container._leaflet_id = undefined;
 
3620                         this._containerId = undefined;
 
3623                 remove(this._mapPane);
 
3625                 if (this._clearControlPos) {
 
3626                         this._clearControlPos();
 
3629                 this._clearHandlers();
 
3632                         // @section Map state change events
 
3633                         // @event unload: Event
 
3634                         // Fired when the map is destroyed with [remove](#map-remove) method.
 
3635                         this.fire('unload');
 
3639                 for (i in this._layers) {
 
3640                         this._layers[i].remove();
 
3642                 for (i in this._panes) {
 
3643                         remove(this._panes[i]);
 
3648                 delete this._mapPane;
 
3649                 delete this._renderer;
 
3654         // @section Other Methods
 
3655         // @method createPane(name: String, container?: HTMLElement): HTMLElement
 
3656         // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
 
3657         // then returns it. The pane is created as a child of `container`, or
 
3658         // as a child of the main map pane if not set.
 
3659         createPane: function (name, container) {
 
3660                 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
 
3661                     pane = create$1('div', className, container || this._mapPane);
 
3664                         this._panes[name] = pane;
 
3669         // @section Methods for Getting Map State
 
3671         // @method getCenter(): LatLng
 
3672         // Returns the geographical center of the map view
 
3673         getCenter: function () {
 
3674                 this._checkIfLoaded();
 
3676                 if (this._lastCenter && !this._moved()) {
 
3677                         return this._lastCenter;
 
3679                 return this.layerPointToLatLng(this._getCenterLayerPoint());
 
3682         // @method getZoom(): Number
 
3683         // Returns the current zoom level of the map view
 
3684         getZoom: function () {
 
3688         // @method getBounds(): LatLngBounds
 
3689         // Returns the geographical bounds visible in the current map view
 
3690         getBounds: function () {
 
3691                 var bounds = this.getPixelBounds(),
 
3692                     sw = this.unproject(bounds.getBottomLeft()),
 
3693                     ne = this.unproject(bounds.getTopRight());
 
3695                 return new LatLngBounds(sw, ne);
 
3698         // @method getMinZoom(): Number
 
3699         // 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.
 
3700         getMinZoom: function () {
 
3701                 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
 
3704         // @method getMaxZoom(): Number
 
3705         // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
 
3706         getMaxZoom: function () {
 
3707                 return this.options.maxZoom === undefined ?
 
3708                         (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
 
3709                         this.options.maxZoom;
 
3712         // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean): Number
 
3713         // Returns the maximum zoom level on which the given bounds fit to the map
 
3714         // view in its entirety. If `inside` (optional) is set to `true`, the method
 
3715         // instead returns the minimum zoom level on which the map view fits into
 
3716         // the given bounds in its entirety.
 
3717         getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
 
3718                 bounds = toLatLngBounds(bounds);
 
3719                 padding = toPoint(padding || [0, 0]);
 
3721                 var zoom = this.getZoom() || 0,
 
3722                     min = this.getMinZoom(),
 
3723                     max = this.getMaxZoom(),
 
3724                     nw = bounds.getNorthWest(),
 
3725                     se = bounds.getSouthEast(),
 
3726                     size = this.getSize().subtract(padding),
 
3727                     boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
 
3728                     snap = any3d ? this.options.zoomSnap : 1,
 
3729                     scalex = size.x / boundsSize.x,
 
3730                     scaley = size.y / boundsSize.y,
 
3731                     scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
 
3733                 zoom = this.getScaleZoom(scale, zoom);
 
3736                         zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
 
3737                         zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
 
3740                 return Math.max(min, Math.min(max, zoom));
 
3743         // @method getSize(): Point
 
3744         // Returns the current size of the map container (in pixels).
 
3745         getSize: function () {
 
3746                 if (!this._size || this._sizeChanged) {
 
3747                         this._size = new Point(
 
3748                                 this._container.clientWidth || 0,
 
3749                                 this._container.clientHeight || 0);
 
3751                         this._sizeChanged = false;
 
3753                 return this._size.clone();
 
3756         // @method getPixelBounds(): Bounds
 
3757         // Returns the bounds of the current map view in projected pixel
 
3758         // coordinates (sometimes useful in layer and overlay implementations).
 
3759         getPixelBounds: function (center, zoom) {
 
3760                 var topLeftPoint = this._getTopLeftPoint(center, zoom);
 
3761                 return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
 
3764         // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
 
3765         // the map pane? "left point of the map layer" can be confusing, specially
 
3766         // since there can be negative offsets.
 
3767         // @method getPixelOrigin(): Point
 
3768         // Returns the projected pixel coordinates of the top left point of
 
3769         // the map layer (useful in custom layer and overlay implementations).
 
3770         getPixelOrigin: function () {
 
3771                 this._checkIfLoaded();
 
3772                 return this._pixelOrigin;
 
3775         // @method getPixelWorldBounds(zoom?: Number): Bounds
 
3776         // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
 
3777         // If `zoom` is omitted, the map's current zoom level is used.
 
3778         getPixelWorldBounds: function (zoom) {
 
3779                 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
 
3782         // @section Other Methods
 
3784         // @method getPane(pane: String|HTMLElement): HTMLElement
 
3785         // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
 
3786         getPane: function (pane) {
 
3787                 return typeof pane === 'string' ? this._panes[pane] : pane;
 
3790         // @method getPanes(): Object
 
3791         // Returns a plain object containing the names of all [panes](#map-pane) as keys and
 
3792         // the panes as values.
 
3793         getPanes: function () {
 
3797         // @method getContainer: HTMLElement
 
3798         // Returns the HTML element that contains the map.
 
3799         getContainer: function () {
 
3800                 return this._container;
 
3804         // @section Conversion Methods
 
3806         // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
 
3807         // Returns the scale factor to be applied to a map transition from zoom level
 
3808         // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
 
3809         getZoomScale: function (toZoom, fromZoom) {
 
3810                 // TODO replace with universal implementation after refactoring projections
 
3811                 var crs = this.options.crs;
 
3812                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
 
3813                 return crs.scale(toZoom) / crs.scale(fromZoom);
 
3816         // @method getScaleZoom(scale: Number, fromZoom: Number): Number
 
3817         // Returns the zoom level that the map would end up at, if it is at `fromZoom`
 
3818         // level and everything is scaled by a factor of `scale`. Inverse of
 
3819         // [`getZoomScale`](#map-getZoomScale).
 
3820         getScaleZoom: function (scale, fromZoom) {
 
3821                 var crs = this.options.crs;
 
3822                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
 
3823                 var zoom = crs.zoom(scale * crs.scale(fromZoom));
 
3824                 return isNaN(zoom) ? Infinity : zoom;
 
3827         // @method project(latlng: LatLng, zoom: Number): Point
 
3828         // Projects a geographical coordinate `LatLng` according to the projection
 
3829         // of the map's CRS, then scales it according to `zoom` and the CRS's
 
3830         // `Transformation`. The result is pixel coordinate relative to
 
3832         project: function (latlng, zoom) {
 
3833                 zoom = zoom === undefined ? this._zoom : zoom;
 
3834                 return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
 
3837         // @method unproject(point: Point, zoom: Number): LatLng
 
3838         // Inverse of [`project`](#map-project).
 
3839         unproject: function (point, zoom) {
 
3840                 zoom = zoom === undefined ? this._zoom : zoom;
 
3841                 return this.options.crs.pointToLatLng(toPoint(point), zoom);
 
3844         // @method layerPointToLatLng(point: Point): LatLng
 
3845         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
 
3846         // returns the corresponding geographical coordinate (for the current zoom level).
 
3847         layerPointToLatLng: function (point) {
 
3848                 var projectedPoint = toPoint(point).add(this.getPixelOrigin());
 
3849                 return this.unproject(projectedPoint);
 
3852         // @method latLngToLayerPoint(latlng: LatLng): Point
 
3853         // Given a geographical coordinate, returns the corresponding pixel coordinate
 
3854         // relative to the [origin pixel](#map-getpixelorigin).
 
3855         latLngToLayerPoint: function (latlng) {
 
3856                 var projectedPoint = this.project(toLatLng(latlng))._round();
 
3857                 return projectedPoint._subtract(this.getPixelOrigin());
 
3860         // @method wrapLatLng(latlng: LatLng): LatLng
 
3861         // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
 
3862         // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
 
3864         // By default this means longitude is wrapped around the dateline so its
 
3865         // value is between -180 and +180 degrees.
 
3866         wrapLatLng: function (latlng) {
 
3867                 return this.options.crs.wrapLatLng(toLatLng(latlng));
 
3870         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
 
3871         // Returns a `LatLngBounds` with the same size as the given one, ensuring that
 
3872         // its center is within the CRS's bounds.
 
3873         // By default this means the center longitude is wrapped around the dateline so its
 
3874         // value is between -180 and +180 degrees, and the majority of the bounds
 
3875         // overlaps the CRS's bounds.
 
3876         wrapLatLngBounds: function (latlng) {
 
3877                 return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
 
3880         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
 
3881         // Returns the distance between two geographical coordinates according to
 
3882         // the map's CRS. By default this measures distance in meters.
 
3883         distance: function (latlng1, latlng2) {
 
3884                 return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
 
3887         // @method containerPointToLayerPoint(point: Point): Point
 
3888         // Given a pixel coordinate relative to the map container, returns the corresponding
 
3889         // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
 
3890         containerPointToLayerPoint: function (point) { // (Point)
 
3891                 return toPoint(point).subtract(this._getMapPanePos());
 
3894         // @method layerPointToContainerPoint(point: Point): Point
 
3895         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
 
3896         // returns the corresponding pixel coordinate relative to the map container.
 
3897         layerPointToContainerPoint: function (point) { // (Point)
 
3898                 return toPoint(point).add(this._getMapPanePos());
 
3901         // @method containerPointToLatLng(point: Point): LatLng
 
3902         // Given a pixel coordinate relative to the map container, returns
 
3903         // the corresponding geographical coordinate (for the current zoom level).
 
3904         containerPointToLatLng: function (point) {
 
3905                 var layerPoint = this.containerPointToLayerPoint(toPoint(point));
 
3906                 return this.layerPointToLatLng(layerPoint);
 
3909         // @method latLngToContainerPoint(latlng: LatLng): Point
 
3910         // Given a geographical coordinate, returns the corresponding pixel coordinate
 
3911         // relative to the map container.
 
3912         latLngToContainerPoint: function (latlng) {
 
3913                 return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
 
3916         // @method mouseEventToContainerPoint(ev: MouseEvent): Point
 
3917         // Given a MouseEvent object, returns the pixel coordinate relative to the
 
3918         // map container where the event took place.
 
3919         mouseEventToContainerPoint: function (e) {
 
3920                 return getMousePosition(e, this._container);
 
3923         // @method mouseEventToLayerPoint(ev: MouseEvent): Point
 
3924         // Given a MouseEvent object, returns the pixel coordinate relative to
 
3925         // the [origin pixel](#map-getpixelorigin) where the event took place.
 
3926         mouseEventToLayerPoint: function (e) {
 
3927                 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
 
3930         // @method mouseEventToLatLng(ev: MouseEvent): LatLng
 
3931         // Given a MouseEvent object, returns geographical coordinate where the
 
3932         // event took place.
 
3933         mouseEventToLatLng: function (e) { // (MouseEvent)
 
3934                 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
 
3938         // map initialization methods
 
3940         _initContainer: function (id) {
 
3941                 var container = this._container = get(id);
 
3944                         throw new Error('Map container not found.');
 
3945                 } else if (container._leaflet_id) {
 
3946                         throw new Error('Map container is already initialized.');
 
3949                 on(container, 'scroll', this._onScroll, this);
 
3950                 this._containerId = stamp(container);
 
3953         _initLayout: function () {
 
3954                 var container = this._container;
 
3956                 this._fadeAnimated = this.options.fadeAnimation && any3d;
 
3958                 addClass(container, 'leaflet-container' +
 
3959                         (touch ? ' leaflet-touch' : '') +
 
3960                         (retina ? ' leaflet-retina' : '') +
 
3961                         (ielt9 ? ' leaflet-oldie' : '') +
 
3962                         (safari ? ' leaflet-safari' : '') +
 
3963                         (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
 
3965                 var position = getStyle(container, 'position');
 
3967                 if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
 
3968                         container.style.position = 'relative';
 
3973                 if (this._initControlPos) {
 
3974                         this._initControlPos();
 
3978         _initPanes: function () {
 
3979                 var panes = this._panes = {};
 
3980                 this._paneRenderers = {};
 
3984                 // Panes are DOM elements used to control the ordering of layers on the map. You
 
3985                 // can access panes with [`map.getPane`](#map-getpane) or
 
3986                 // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
 
3987                 // [`map.createPane`](#map-createpane) method.
 
3989                 // Every map has the following default panes that differ only in zIndex.
 
3991                 // @pane mapPane: HTMLElement = 'auto'
 
3992                 // Pane that contains all other map panes
 
3994                 this._mapPane = this.createPane('mapPane', this._container);
 
3995                 setPosition(this._mapPane, new Point(0, 0));
 
3997                 // @pane tilePane: HTMLElement = 200
 
3998                 // Pane for `GridLayer`s and `TileLayer`s
 
3999                 this.createPane('tilePane');
 
4000                 // @pane overlayPane: HTMLElement = 400
 
4001                 // Pane for vector overlays (`Path`s), like `Polyline`s and `Polygon`s
 
4002                 this.createPane('shadowPane');
 
4003                 // @pane shadowPane: HTMLElement = 500
 
4004                 // Pane for overlay shadows (e.g. `Marker` shadows)
 
4005                 this.createPane('overlayPane');
 
4006                 // @pane markerPane: HTMLElement = 600
 
4007                 // Pane for `Icon`s of `Marker`s
 
4008                 this.createPane('markerPane');
 
4009                 // @pane tooltipPane: HTMLElement = 650
 
4010                 // Pane for tooltip.
 
4011                 this.createPane('tooltipPane');
 
4012                 // @pane popupPane: HTMLElement = 700
 
4013                 // Pane for `Popup`s.
 
4014                 this.createPane('popupPane');
 
4016                 if (!this.options.markerZoomAnimation) {
 
4017                         addClass(panes.markerPane, 'leaflet-zoom-hide');
 
4018                         addClass(panes.shadowPane, 'leaflet-zoom-hide');
 
4023         // private methods that modify map state
 
4025         // @section Map state change events
 
4026         _resetView: function (center, zoom) {
 
4027                 setPosition(this._mapPane, new Point(0, 0));
 
4029                 var loading = !this._loaded;
 
4030                 this._loaded = true;
 
4031                 zoom = this._limitZoom(zoom);
 
4033                 this.fire('viewprereset');
 
4035                 var zoomChanged = this._zoom !== zoom;
 
4037                         ._moveStart(zoomChanged)
 
4038                         ._move(center, zoom)
 
4039                         ._moveEnd(zoomChanged);
 
4041                 // @event viewreset: Event
 
4042                 // Fired when the map needs to redraw its content (this usually happens
 
4043                 // on map zoom or load). Very useful for creating custom overlays.
 
4044                 this.fire('viewreset');
 
4046                 // @event load: Event
 
4047                 // Fired when the map is initialized (when its center and zoom are set
 
4048                 // for the first time).
 
4054         _moveStart: function (zoomChanged) {
 
4055                 // @event zoomstart: Event
 
4056                 // Fired when the map zoom is about to change (e.g. before zoom animation).
 
4057                 // @event movestart: Event
 
4058                 // Fired when the view of the map starts changing (e.g. user starts dragging the map).
 
4060                         this.fire('zoomstart');
 
4062                 return this.fire('movestart');
 
4065         _move: function (center, zoom, data) {
 
4066                 if (zoom === undefined) {
 
4069                 var zoomChanged = this._zoom !== zoom;
 
4072                 this._lastCenter = center;
 
4073                 this._pixelOrigin = this._getNewPixelOrigin(center);
 
4075                 // @event zoom: Event
 
4076                 // Fired repeatedly during any change in zoom level, including zoom
 
4077                 // and fly animations.
 
4078                 if (zoomChanged || (data && data.pinch)) {      // Always fire 'zoom' if pinching because #3530
 
4079                         this.fire('zoom', data);
 
4082                 // @event move: Event
 
4083                 // Fired repeatedly during any movement of the map, including pan and
 
4085                 return this.fire('move', data);
 
4088         _moveEnd: function (zoomChanged) {
 
4089                 // @event zoomend: Event
 
4090                 // Fired when the map has changed, after any animations.
 
4092                         this.fire('zoomend');
 
4095                 // @event moveend: Event
 
4096                 // Fired when the center of the map stops changing (e.g. user stopped
 
4097                 // dragging the map).
 
4098                 return this.fire('moveend');
 
4101         _stop: function () {
 
4102                 cancelAnimFrame(this._flyToFrame);
 
4103                 if (this._panAnim) {
 
4104                         this._panAnim.stop();
 
4109         _rawPanBy: function (offset) {
 
4110                 setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
 
4113         _getZoomSpan: function () {
 
4114                 return this.getMaxZoom() - this.getMinZoom();
 
4117         _panInsideMaxBounds: function () {
 
4118                 if (!this._enforcingBounds) {
 
4119                         this.panInsideBounds(this.options.maxBounds);
 
4123         _checkIfLoaded: function () {
 
4124                 if (!this._loaded) {
 
4125                         throw new Error('Set map center and zoom first.');
 
4129         // DOM event handling
 
4131         // @section Interaction events
 
4132         _initEvents: function (remove$$1) {
 
4134                 this._targets[stamp(this._container)] = this;
 
4136                 var onOff = remove$$1 ? off : on;
 
4138                 // @event click: MouseEvent
 
4139                 // Fired when the user clicks (or taps) the map.
 
4140                 // @event dblclick: MouseEvent
 
4141                 // Fired when the user double-clicks (or double-taps) the map.
 
4142                 // @event mousedown: MouseEvent
 
4143                 // Fired when the user pushes the mouse button on the map.
 
4144                 // @event mouseup: MouseEvent
 
4145                 // Fired when the user releases the mouse button on the map.
 
4146                 // @event mouseover: MouseEvent
 
4147                 // Fired when the mouse enters the map.
 
4148                 // @event mouseout: MouseEvent
 
4149                 // Fired when the mouse leaves the map.
 
4150                 // @event mousemove: MouseEvent
 
4151                 // Fired while the mouse moves over the map.
 
4152                 // @event contextmenu: MouseEvent
 
4153                 // Fired when the user pushes the right mouse button on the map, prevents
 
4154                 // default browser context menu from showing if there are listeners on
 
4155                 // this event. Also fired on mobile when the user holds a single touch
 
4156                 // for a second (also called long press).
 
4157                 // @event keypress: KeyboardEvent
 
4158                 // Fired when the user presses a key from the keyboard while the map is focused.
 
4159                 onOff(this._container, 'click dblclick mousedown mouseup ' +
 
4160                         'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
 
4162                 if (this.options.trackResize) {
 
4163                         onOff(window, 'resize', this._onResize, this);
 
4166                 if (any3d && this.options.transform3DLimit) {
 
4167                         (remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
 
4171         _onResize: function () {
 
4172                 cancelAnimFrame(this._resizeRequest);
 
4173                 this._resizeRequest = requestAnimFrame(
 
4174                         function () { this.invalidateSize({debounceMoveend: true}); }, this);
 
4177         _onScroll: function () {
 
4178                 this._container.scrollTop  = 0;
 
4179                 this._container.scrollLeft = 0;
 
4182         _onMoveEnd: function () {
 
4183                 var pos = this._getMapPanePos();
 
4184                 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
 
4185                         // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
 
4186                         // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
 
4187                         this._resetView(this.getCenter(), this.getZoom());
 
4191         _findEventTargets: function (e, type) {
 
4194                     isHover = type === 'mouseout' || type === 'mouseover',
 
4195                     src = e.target || e.srcElement,
 
4199                         target = this._targets[stamp(src)];
 
4200                         if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
 
4201                                 // Prevent firing click after you just dragged an object.
 
4205                         if (target && target.listens(type, true)) {
 
4206                                 if (isHover && !isExternalTarget(src, e)) { break; }
 
4207                                 targets.push(target);
 
4208                                 if (isHover) { break; }
 
4210                         if (src === this._container) { break; }
 
4211                         src = src.parentNode;
 
4213                 if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) {
 
4219         _handleDOMEvent: function (e) {
 
4220                 if (!this._loaded || skipped(e)) { return; }
 
4224                 if (type === 'mousedown' || type === 'keypress') {
 
4225                         // prevents outline when clicking on keyboard-focusable element
 
4226                         preventOutline(e.target || e.srcElement);
 
4229                 this._fireDOMEvent(e, type);
 
4232         _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
 
4234         _fireDOMEvent: function (e, type, targets) {
 
4236                 if (e.type === 'click') {
 
4237                         // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
 
4238                         // @event preclick: MouseEvent
 
4239                         // Fired before mouse click on the map (sometimes useful when you
 
4240                         // want something to happen on click before any existing click
 
4241                         // handlers start running).
 
4242                         var synth = extend({}, e);
 
4243                         synth.type = 'preclick';
 
4244                         this._fireDOMEvent(synth, synth.type, targets);
 
4247                 if (e._stopped) { return; }
 
4249                 // Find the layer the event is propagating from and its parents.
 
4250                 targets = (targets || []).concat(this._findEventTargets(e, type));
 
4252                 if (!targets.length) { return; }
 
4254                 var target = targets[0];
 
4255                 if (type === 'contextmenu' && target.listens(type, true)) {
 
4263                 if (e.type !== 'keypress') {
 
4264                         var isMarker = (target.options && 'icon' in target.options);
 
4265                         data.containerPoint = isMarker ?
 
4266                                         this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
 
4267                         data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
 
4268                         data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
 
4271                 for (var i = 0; i < targets.length; i++) {
 
4272                         targets[i].fire(type, data, true);
 
4273                         if (data.originalEvent._stopped ||
 
4274                                 (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
 
4278         _draggableMoved: function (obj) {
 
4279                 obj = obj.dragging && obj.dragging.enabled() ? obj : this;
 
4280                 return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
 
4283         _clearHandlers: function () {
 
4284                 for (var i = 0, len = this._handlers.length; i < len; i++) {
 
4285                         this._handlers[i].disable();
 
4289         // @section Other Methods
 
4291         // @method whenReady(fn: Function, context?: Object): this
 
4292         // Runs the given function `fn` when the map gets initialized with
 
4293         // a view (center and zoom) and at least one layer, or immediately
 
4294         // if it's already initialized, optionally passing a function context.
 
4295         whenReady: function (callback, context) {
 
4297                         callback.call(context || this, {target: this});
 
4299                         this.on('load', callback, context);
 
4305         // private methods for getting map state
 
4307         _getMapPanePos: function () {
 
4308                 return getPosition(this._mapPane) || new Point(0, 0);
 
4311         _moved: function () {
 
4312                 var pos = this._getMapPanePos();
 
4313                 return pos && !pos.equals([0, 0]);
 
4316         _getTopLeftPoint: function (center, zoom) {
 
4317                 var pixelOrigin = center && zoom !== undefined ?
 
4318                         this._getNewPixelOrigin(center, zoom) :
 
4319                         this.getPixelOrigin();
 
4320                 return pixelOrigin.subtract(this._getMapPanePos());
 
4323         _getNewPixelOrigin: function (center, zoom) {
 
4324                 var viewHalf = this.getSize()._divideBy(2);
 
4325                 return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
 
4328         _latLngToNewLayerPoint: function (latlng, zoom, center) {
 
4329                 var topLeft = this._getNewPixelOrigin(center, zoom);
 
4330                 return this.project(latlng, zoom)._subtract(topLeft);
 
4333         _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
 
4334                 var topLeft = this._getNewPixelOrigin(center, zoom);
 
4336                         this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
 
4337                         this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
 
4338                         this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
 
4339                         this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
 
4343         // layer point of the current center
 
4344         _getCenterLayerPoint: function () {
 
4345                 return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
 
4348         // offset of the specified place to the current center in pixels
 
4349         _getCenterOffset: function (latlng) {
 
4350                 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
 
4353         // adjust center for view to get inside bounds
 
4354         _limitCenter: function (center, zoom, bounds) {
 
4356                 if (!bounds) { return center; }
 
4358                 var centerPoint = this.project(center, zoom),
 
4359                     viewHalf = this.getSize().divideBy(2),
 
4360                     viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
 
4361                     offset = this._getBoundsOffset(viewBounds, bounds, zoom);
 
4363                 // If offset is less than a pixel, ignore.
 
4364                 // This prevents unstable projections from getting into
 
4365                 // an infinite loop of tiny offsets.
 
4366                 if (offset.round().equals([0, 0])) {
 
4370                 return this.unproject(centerPoint.add(offset), zoom);
 
4373         // adjust offset for view to get inside bounds
 
4374         _limitOffset: function (offset, bounds) {
 
4375                 if (!bounds) { return offset; }
 
4377                 var viewBounds = this.getPixelBounds(),
 
4378                     newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
 
4380                 return offset.add(this._getBoundsOffset(newBounds, bounds));
 
4383         // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
 
4384         _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
 
4385                 var projectedMaxBounds = toBounds(
 
4386                         this.project(maxBounds.getNorthEast(), zoom),
 
4387                         this.project(maxBounds.getSouthWest(), zoom)
 
4389                     minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
 
4390                     maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
 
4392                     dx = this._rebound(minOffset.x, -maxOffset.x),
 
4393                     dy = this._rebound(minOffset.y, -maxOffset.y);
 
4395                 return new Point(dx, dy);
 
4398         _rebound: function (left, right) {
 
4399                 return left + right > 0 ?
 
4400                         Math.round(left - right) / 2 :
 
4401                         Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
 
4404         _limitZoom: function (zoom) {
 
4405                 var min = this.getMinZoom(),
 
4406                     max = this.getMaxZoom(),
 
4407                     snap = any3d ? this.options.zoomSnap : 1;
 
4409                         zoom = Math.round(zoom / snap) * snap;
 
4411                 return Math.max(min, Math.min(max, zoom));
 
4414         _onPanTransitionStep: function () {
 
4418         _onPanTransitionEnd: function () {
 
4419                 removeClass(this._mapPane, 'leaflet-pan-anim');
 
4420                 this.fire('moveend');
 
4423         _tryAnimatedPan: function (center, options) {
 
4424                 // difference between the new and current centers in pixels
 
4425                 var offset = this._getCenterOffset(center)._floor();
 
4427                 // don't animate too far unless animate: true specified in options
 
4428                 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
 
4430                 this.panBy(offset, options);
 
4435         _createAnimProxy: function () {
 
4437                 var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
 
4438                 this._panes.mapPane.appendChild(proxy);
 
4440                 this.on('zoomanim', function (e) {
 
4441                         var prop = TRANSFORM,
 
4442                             transform = this._proxy.style[prop];
 
4444                         setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
 
4446                         // workaround for case when transform is the same and so transitionend event is not fired
 
4447                         if (transform === this._proxy.style[prop] && this._animatingZoom) {
 
4448                                 this._onZoomTransitionEnd();
 
4452                 this.on('load moveend', function () {
 
4453                         var c = this.getCenter(),
 
4455                         setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
 
4458                 this._on('unload', this._destroyAnimProxy, this);
 
4461         _destroyAnimProxy: function () {
 
4462                 remove(this._proxy);
 
4466         _catchTransitionEnd: function (e) {
 
4467                 if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
 
4468                         this._onZoomTransitionEnd();
 
4472         _nothingToAnimate: function () {
 
4473                 return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
 
4476         _tryAnimatedZoom: function (center, zoom, options) {
 
4478                 if (this._animatingZoom) { return true; }
 
4480                 options = options || {};
 
4482                 // don't animate if disabled, not supported or zoom difference is too large
 
4483                 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
 
4484                         Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
 
4486                 // offset is the pixel coords of the zoom origin relative to the current center
 
4487                 var scale = this.getZoomScale(zoom),
 
4488                     offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
 
4490                 // don't animate if the zoom origin isn't within one screen from the current center, unless forced
 
4491                 if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
 
4493                 requestAnimFrame(function () {
 
4496                             ._animateZoom(center, zoom, true);
 
4502         _animateZoom: function (center, zoom, startAnim, noUpdate) {
 
4504                         this._animatingZoom = true;
 
4506                         // remember what center/zoom to set after animation
 
4507                         this._animateToCenter = center;
 
4508                         this._animateToZoom = zoom;
 
4510                         addClass(this._mapPane, 'leaflet-zoom-anim');
 
4513                 // @event zoomanim: ZoomAnimEvent
 
4514                 // Fired on every frame of a zoom animation
 
4515                 this.fire('zoomanim', {
 
4521                 // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
 
4522                 setTimeout(bind(this._onZoomTransitionEnd, this), 250);
 
4525         _onZoomTransitionEnd: function () {
 
4526                 if (!this._animatingZoom) { return; }
 
4528                 removeClass(this._mapPane, 'leaflet-zoom-anim');
 
4530                 this._animatingZoom = false;
 
4532                 this._move(this._animateToCenter, this._animateToZoom);
 
4534                 // This anim frame should prevent an obscure iOS webkit tile loading race condition.
 
4535                 requestAnimFrame(function () {
 
4536                         this._moveEnd(true);
 
4543 // @factory L.map(id: String, options?: Map options)
 
4544 // Instantiates a map object given the DOM ID of a `<div>` element
 
4545 // and optionally an object literal with `Map options`.
 
4548 // @factory L.map(el: HTMLElement, options?: Map options)
 
4549 // Instantiates a map object given an instance of a `<div>` HTML element
 
4550 // and optionally an object literal with `Map options`.
 
4551 function createMap(id, options) {
 
4552         return new Map(id, options);
 
4560  * L.Control is a base class for implementing map controls. Handles positioning.
 
4561  * All other controls extend from this class.
 
4564 var Control = Class.extend({
 
4566         // @aka Control options
 
4568                 // @option position: String = 'topright'
 
4569                 // The position of the control (one of the map corners). Possible values are `'topleft'`,
 
4570                 // `'topright'`, `'bottomleft'` or `'bottomright'`
 
4571                 position: 'topright'
 
4574         initialize: function (options) {
 
4575                 setOptions(this, options);
 
4579          * Classes extending L.Control will inherit the following methods:
 
4581          * @method getPosition: string
 
4582          * Returns the position of the control.
 
4584         getPosition: function () {
 
4585                 return this.options.position;
 
4588         // @method setPosition(position: string): this
 
4589         // Sets the position of the control.
 
4590         setPosition: function (position) {
 
4591                 var map = this._map;
 
4594                         map.removeControl(this);
 
4597                 this.options.position = position;
 
4600                         map.addControl(this);
 
4606         // @method getContainer: HTMLElement
 
4607         // Returns the HTMLElement that contains the control.
 
4608         getContainer: function () {
 
4609                 return this._container;
 
4612         // @method addTo(map: Map): this
 
4613         // Adds the control to the given map.
 
4614         addTo: function (map) {
 
4618                 var container = this._container = this.onAdd(map),
 
4619                     pos = this.getPosition(),
 
4620                     corner = map._controlCorners[pos];
 
4622                 addClass(container, 'leaflet-control');
 
4624                 if (pos.indexOf('bottom') !== -1) {
 
4625                         corner.insertBefore(container, corner.firstChild);
 
4627                         corner.appendChild(container);
 
4633         // @method remove: this
 
4634         // Removes the control from the map it is currently active on.
 
4635         remove: function () {
 
4640                 remove(this._container);
 
4642                 if (this.onRemove) {
 
4643                         this.onRemove(this._map);
 
4651         _refocusOnMap: function (e) {
 
4652                 // if map exists and event is not a keyboard event
 
4653                 if (this._map && e && e.screenX > 0 && e.screenY > 0) {
 
4654                         this._map.getContainer().focus();
 
4659 var control = function (options) {
 
4660         return new Control(options);
 
4663 /* @section Extension methods
 
4666  * Every control should extend from `L.Control` and (re-)implement the following methods.
 
4668  * @method onAdd(map: Map): HTMLElement
 
4669  * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
 
4671  * @method onRemove(map: Map)
 
4672  * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
 
4676  * @section Methods for Layers and Controls
 
4679         // @method addControl(control: Control): this
 
4680         // Adds the given control to the map
 
4681         addControl: function (control) {
 
4682                 control.addTo(this);
 
4686         // @method removeControl(control: Control): this
 
4687         // Removes the given control from the map
 
4688         removeControl: function (control) {
 
4693         _initControlPos: function () {
 
4694                 var corners = this._controlCorners = {},
 
4696                     container = this._controlContainer =
 
4697                             create$1('div', l + 'control-container', this._container);
 
4699                 function createCorner(vSide, hSide) {
 
4700                         var className = l + vSide + ' ' + l + hSide;
 
4702                         corners[vSide + hSide] = create$1('div', className, container);
 
4705                 createCorner('top', 'left');
 
4706                 createCorner('top', 'right');
 
4707                 createCorner('bottom', 'left');
 
4708                 createCorner('bottom', 'right');
 
4711         _clearControlPos: function () {
 
4712                 for (var i in this._controlCorners) {
 
4713                         remove(this._controlCorners[i]);
 
4715                 remove(this._controlContainer);
 
4716                 delete this._controlCorners;
 
4717                 delete this._controlContainer;
 
4722  * @class Control.Layers
 
4723  * @aka L.Control.Layers
 
4726  * 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`.
 
4731  * var baseLayers = {
 
4733  *      "OpenStreetMap": osm
 
4738  *      "Roads": roadsLayer
 
4741  * L.control.layers(baseLayers, overlays).addTo(map);
 
4744  * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
 
4748  *     "<someName1>": layer1,
 
4749  *     "<someName2>": layer2
 
4753  * The layer names can contain HTML, which allows you to add additional styling to the items:
 
4756  * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
 
4760 var Layers = Control.extend({
 
4762         // @aka Control.Layers options
 
4764                 // @option collapsed: Boolean = true
 
4765                 // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
 
4767                 position: 'topright',
 
4769                 // @option autoZIndex: Boolean = true
 
4770                 // 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.
 
4773                 // @option hideSingleBase: Boolean = false
 
4774                 // If `true`, the base layers in the control will be hidden when there is only one.
 
4775                 hideSingleBase: false,
 
4777                 // @option sortLayers: Boolean = false
 
4778                 // Whether to sort the layers. When `false`, layers will keep the order
 
4779                 // in which they were added to the control.
 
4782                 // @option sortFunction: Function = *
 
4783                 // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
 
4784                 // that will be used for sorting the layers, when `sortLayers` is `true`.
 
4785                 // The function receives both the `L.Layer` instances and their names, as in
 
4786                 // `sortFunction(layerA, layerB, nameA, nameB)`.
 
4787                 // By default, it sorts layers alphabetically by their name.
 
4788                 sortFunction: function (layerA, layerB, nameA, nameB) {
 
4789                         return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
 
4793         initialize: function (baseLayers, overlays, options) {
 
4794                 setOptions(this, options);
 
4796                 this._layerControlInputs = [];
 
4798                 this._lastZIndex = 0;
 
4799                 this._handlingClick = false;
 
4801                 for (var i in baseLayers) {
 
4802                         this._addLayer(baseLayers[i], i);
 
4805                 for (i in overlays) {
 
4806                         this._addLayer(overlays[i], i, true);
 
4810         onAdd: function (map) {
 
4815                 map.on('zoomend', this._checkDisabledLayers, this);
 
4817                 for (var i = 0; i < this._layers.length; i++) {
 
4818                         this._layers[i].layer.on('add remove', this._onLayerChange, this);
 
4821                 return this._container;
 
4824         addTo: function (map) {
 
4825                 Control.prototype.addTo.call(this, map);
 
4826                 // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
 
4827                 return this._expandIfNotCollapsed();
 
4830         onRemove: function () {
 
4831                 this._map.off('zoomend', this._checkDisabledLayers, this);
 
4833                 for (var i = 0; i < this._layers.length; i++) {
 
4834                         this._layers[i].layer.off('add remove', this._onLayerChange, this);
 
4838         // @method addBaseLayer(layer: Layer, name: String): this
 
4839         // Adds a base layer (radio button entry) with the given name to the control.
 
4840         addBaseLayer: function (layer, name) {
 
4841                 this._addLayer(layer, name);
 
4842                 return (this._map) ? this._update() : this;
 
4845         // @method addOverlay(layer: Layer, name: String): this
 
4846         // Adds an overlay (checkbox entry) with the given name to the control.
 
4847         addOverlay: function (layer, name) {
 
4848                 this._addLayer(layer, name, true);
 
4849                 return (this._map) ? this._update() : this;
 
4852         // @method removeLayer(layer: Layer): this
 
4853         // Remove the given layer from the control.
 
4854         removeLayer: function (layer) {
 
4855                 layer.off('add remove', this._onLayerChange, this);
 
4857                 var obj = this._getLayer(stamp(layer));
 
4859                         this._layers.splice(this._layers.indexOf(obj), 1);
 
4861                 return (this._map) ? this._update() : this;
 
4864         // @method expand(): this
 
4865         // Expand the control container if collapsed.
 
4866         expand: function () {
 
4867                 addClass(this._container, 'leaflet-control-layers-expanded');
 
4868                 this._form.style.height = null;
 
4869                 var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
 
4870                 if (acceptableHeight < this._form.clientHeight) {
 
4871                         addClass(this._form, 'leaflet-control-layers-scrollbar');
 
4872                         this._form.style.height = acceptableHeight + 'px';
 
4874                         removeClass(this._form, 'leaflet-control-layers-scrollbar');
 
4876                 this._checkDisabledLayers();
 
4880         // @method collapse(): this
 
4881         // Collapse the control container if expanded.
 
4882         collapse: function () {
 
4883                 removeClass(this._container, 'leaflet-control-layers-expanded');
 
4887         _initLayout: function () {
 
4888                 var className = 'leaflet-control-layers',
 
4889                     container = this._container = create$1('div', className),
 
4890                     collapsed = this.options.collapsed;
 
4892                 // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
 
4893                 container.setAttribute('aria-haspopup', true);
 
4895                 disableClickPropagation(container);
 
4896                 disableScrollPropagation(container);
 
4898                 var form = this._form = create$1('form', className + '-list');
 
4901                         this._map.on('click', this.collapse, this);
 
4905                                         mouseenter: this.expand,
 
4906                                         mouseleave: this.collapse
 
4911                 var link = this._layersLink = create$1('a', className + '-toggle', container);
 
4913                 link.title = 'Layers';
 
4916                         on(link, 'click', stop);
 
4917                         on(link, 'click', this.expand, this);
 
4919                         on(link, 'focus', this.expand, this);
 
4926                 this._baseLayersList = create$1('div', className + '-base', form);
 
4927                 this._separator = create$1('div', className + '-separator', form);
 
4928                 this._overlaysList = create$1('div', className + '-overlays', form);
 
4930                 container.appendChild(form);
 
4933         _getLayer: function (id) {
 
4934                 for (var i = 0; i < this._layers.length; i++) {
 
4936                         if (this._layers[i] && stamp(this._layers[i].layer) === id) {
 
4937                                 return this._layers[i];
 
4942         _addLayer: function (layer, name, overlay) {
 
4944                         layer.on('add remove', this._onLayerChange, this);
 
4953                 if (this.options.sortLayers) {
 
4954                         this._layers.sort(bind(function (a, b) {
 
4955                                 return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
 
4959                 if (this.options.autoZIndex && layer.setZIndex) {
 
4961                         layer.setZIndex(this._lastZIndex);
 
4964                 this._expandIfNotCollapsed();
 
4967         _update: function () {
 
4968                 if (!this._container) { return this; }
 
4970                 empty(this._baseLayersList);
 
4971                 empty(this._overlaysList);
 
4973                 this._layerControlInputs = [];
 
4974                 var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
 
4976                 for (i = 0; i < this._layers.length; i++) {
 
4977                         obj = this._layers[i];
 
4979                         overlaysPresent = overlaysPresent || obj.overlay;
 
4980                         baseLayersPresent = baseLayersPresent || !obj.overlay;
 
4981                         baseLayersCount += !obj.overlay ? 1 : 0;
 
4984                 // Hide base layers section if there's only one layer.
 
4985                 if (this.options.hideSingleBase) {
 
4986                         baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
 
4987                         this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
 
4990                 this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
 
4995         _onLayerChange: function (e) {
 
4996                 if (!this._handlingClick) {
 
5000                 var obj = this._getLayer(stamp(e.target));
 
5003                 // @section Layer events
 
5004                 // @event baselayerchange: LayersControlEvent
 
5005                 // Fired when the base layer is changed through the [layer control](#control-layers).
 
5006                 // @event overlayadd: LayersControlEvent
 
5007                 // Fired when an overlay is selected through the [layer control](#control-layers).
 
5008                 // @event overlayremove: LayersControlEvent
 
5009                 // Fired when an overlay is deselected through the [layer control](#control-layers).
 
5010                 // @namespace Control.Layers
 
5011                 var type = obj.overlay ?
 
5012                         (e.type === 'add' ? 'overlayadd' : 'overlayremove') :
 
5013                         (e.type === 'add' ? 'baselayerchange' : null);
 
5016                         this._map.fire(type, obj);
 
5020         // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
 
5021         _createRadioElement: function (name, checked) {
 
5023                 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
 
5024                                 name + '"' + (checked ? ' checked="checked"' : '') + '/>';
 
5026                 var radioFragment = document.createElement('div');
 
5027                 radioFragment.innerHTML = radioHtml;
 
5029                 return radioFragment.firstChild;
 
5032         _addItem: function (obj) {
 
5033                 var label = document.createElement('label'),
 
5034                     checked = this._map.hasLayer(obj.layer),
 
5038                         input = document.createElement('input');
 
5039                         input.type = 'checkbox';
 
5040                         input.className = 'leaflet-control-layers-selector';
 
5041                         input.defaultChecked = checked;
 
5043                         input = this._createRadioElement('leaflet-base-layers', checked);
 
5046                 this._layerControlInputs.push(input);
 
5047                 input.layerId = stamp(obj.layer);
 
5049                 on(input, 'click', this._onInputClick, this);
 
5051                 var name = document.createElement('span');
 
5052                 name.innerHTML = ' ' + obj.name;
 
5054                 // Helps from preventing layer control flicker when checkboxes are disabled
 
5055                 // https://github.com/Leaflet/Leaflet/issues/2771
 
5056                 var holder = document.createElement('div');
 
5058                 label.appendChild(holder);
 
5059                 holder.appendChild(input);
 
5060                 holder.appendChild(name);
 
5062                 var container = obj.overlay ? this._overlaysList : this._baseLayersList;
 
5063                 container.appendChild(label);
 
5065                 this._checkDisabledLayers();
 
5069         _onInputClick: function () {
 
5070                 var inputs = this._layerControlInputs,
 
5072                 var addedLayers = [],
 
5075                 this._handlingClick = true;
 
5077                 for (var i = inputs.length - 1; i >= 0; i--) {
 
5079                         layer = this._getLayer(input.layerId).layer;
 
5081                         if (input.checked) {
 
5082                                 addedLayers.push(layer);
 
5083                         } else if (!input.checked) {
 
5084                                 removedLayers.push(layer);
 
5088                 // Bugfix issue 2318: Should remove all old layers before readding new ones
 
5089                 for (i = 0; i < removedLayers.length; i++) {
 
5090                         if (this._map.hasLayer(removedLayers[i])) {
 
5091                                 this._map.removeLayer(removedLayers[i]);
 
5094                 for (i = 0; i < addedLayers.length; i++) {
 
5095                         if (!this._map.hasLayer(addedLayers[i])) {
 
5096                                 this._map.addLayer(addedLayers[i]);
 
5100                 this._handlingClick = false;
 
5102                 this._refocusOnMap();
 
5105         _checkDisabledLayers: function () {
 
5106                 var inputs = this._layerControlInputs,
 
5109                     zoom = this._map.getZoom();
 
5111                 for (var i = inputs.length - 1; i >= 0; i--) {
 
5113                         layer = this._getLayer(input.layerId).layer;
 
5114                         input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
 
5115                                          (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
 
5120         _expandIfNotCollapsed: function () {
 
5121                 if (this._map && !this.options.collapsed) {
 
5127         _expand: function () {
 
5128                 // Backward compatibility, remove me in 1.1.
 
5129                 return this.expand();
 
5132         _collapse: function () {
 
5133                 // Backward compatibility, remove me in 1.1.
 
5134                 return this.collapse();
 
5140 // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
 
5141 // 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.
 
5142 var layers = function (baseLayers, overlays, options) {
 
5143         return new Layers(baseLayers, overlays, options);
 
5147  * @class Control.Zoom
 
5148  * @aka L.Control.Zoom
 
5151  * 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`.
 
5154 var Zoom = Control.extend({
 
5156         // @aka Control.Zoom options
 
5158                 position: 'topleft',
 
5160                 // @option zoomInText: String = '+'
 
5161                 // The text set on the 'zoom in' button.
 
5164                 // @option zoomInTitle: String = 'Zoom in'
 
5165                 // The title set on the 'zoom in' button.
 
5166                 zoomInTitle: 'Zoom in',
 
5168                 // @option zoomOutText: String = '−'
 
5169                 // The text set on the 'zoom out' button.
 
5170                 zoomOutText: '−',
 
5172                 // @option zoomOutTitle: String = 'Zoom out'
 
5173                 // The title set on the 'zoom out' button.
 
5174                 zoomOutTitle: 'Zoom out'
 
5177         onAdd: function (map) {
 
5178                 var zoomName = 'leaflet-control-zoom',
 
5179                     container = create$1('div', zoomName + ' leaflet-bar'),
 
5180                     options = this.options;
 
5182                 this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
 
5183                         zoomName + '-in',  container, this._zoomIn);
 
5184                 this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
 
5185                         zoomName + '-out', container, this._zoomOut);
 
5187                 this._updateDisabled();
 
5188                 map.on('zoomend zoomlevelschange', this._updateDisabled, this);
 
5193         onRemove: function (map) {
 
5194                 map.off('zoomend zoomlevelschange', this._updateDisabled, this);
 
5197         disable: function () {
 
5198                 this._disabled = true;
 
5199                 this._updateDisabled();
 
5203         enable: function () {
 
5204                 this._disabled = false;
 
5205                 this._updateDisabled();
 
5209         _zoomIn: function (e) {
 
5210                 if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
 
5211                         this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
 
5215         _zoomOut: function (e) {
 
5216                 if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
 
5217                         this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
 
5221         _createButton: function (html, title, className, container, fn) {
 
5222                 var link = create$1('a', className, container);
 
5223                 link.innerHTML = html;
 
5228                  * Will force screen readers like VoiceOver to read this as "Zoom in - button"
 
5230                 link.setAttribute('role', 'button');
 
5231                 link.setAttribute('aria-label', title);
 
5233                 disableClickPropagation(link);
 
5234                 on(link, 'click', stop);
 
5235                 on(link, 'click', fn, this);
 
5236                 on(link, 'click', this._refocusOnMap, this);
 
5241         _updateDisabled: function () {
 
5242                 var map = this._map,
 
5243                     className = 'leaflet-disabled';
 
5245                 removeClass(this._zoomInButton, className);
 
5246                 removeClass(this._zoomOutButton, className);
 
5248                 if (this._disabled || map._zoom === map.getMinZoom()) {
 
5249                         addClass(this._zoomOutButton, className);
 
5251                 if (this._disabled || map._zoom === map.getMaxZoom()) {
 
5252                         addClass(this._zoomInButton, className);
 
5258 // @section Control options
 
5259 // @option zoomControl: Boolean = true
 
5260 // Whether a [zoom control](#control-zoom) is added to the map by default.
 
5265 Map.addInitHook(function () {
 
5266         if (this.options.zoomControl) {
 
5267                 this.zoomControl = new Zoom();
 
5268                 this.addControl(this.zoomControl);
 
5272 // @namespace Control.Zoom
 
5273 // @factory L.control.zoom(options: Control.Zoom options)
 
5274 // Creates a zoom control
 
5275 var zoom = function (options) {
 
5276         return new Zoom(options);
 
5280  * @class Control.Scale
 
5281  * @aka L.Control.Scale
 
5284  * 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`.
 
5289  * L.control.scale().addTo(map);
 
5293 var Scale = Control.extend({
 
5295         // @aka Control.Scale options
 
5297                 position: 'bottomleft',
 
5299                 // @option maxWidth: Number = 100
 
5300                 // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
 
5303                 // @option metric: Boolean = True
 
5304                 // Whether to show the metric scale line (m/km).
 
5307                 // @option imperial: Boolean = True
 
5308                 // Whether to show the imperial scale line (mi/ft).
 
5311                 // @option updateWhenIdle: Boolean = false
 
5312                 // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
 
5315         onAdd: function (map) {
 
5316                 var className = 'leaflet-control-scale',
 
5317                     container = create$1('div', className),
 
5318                     options = this.options;
 
5320                 this._addScales(options, className + '-line', container);
 
5322                 map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
5323                 map.whenReady(this._update, this);
 
5328         onRemove: function (map) {
 
5329                 map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
5332         _addScales: function (options, className, container) {
 
5333                 if (options.metric) {
 
5334                         this._mScale = create$1('div', className, container);
 
5336                 if (options.imperial) {
 
5337                         this._iScale = create$1('div', className, container);
 
5341         _update: function () {
 
5342                 var map = this._map,
 
5343                     y = map.getSize().y / 2;
 
5345                 var maxMeters = map.distance(
 
5346                                 map.containerPointToLatLng([0, y]),
 
5347                                 map.containerPointToLatLng([this.options.maxWidth, y]));
 
5349                 this._updateScales(maxMeters);
 
5352         _updateScales: function (maxMeters) {
 
5353                 if (this.options.metric && maxMeters) {
 
5354                         this._updateMetric(maxMeters);
 
5356                 if (this.options.imperial && maxMeters) {
 
5357                         this._updateImperial(maxMeters);
 
5361         _updateMetric: function (maxMeters) {
 
5362                 var meters = this._getRoundNum(maxMeters),
 
5363                     label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
 
5365                 this._updateScale(this._mScale, label, meters / maxMeters);
 
5368         _updateImperial: function (maxMeters) {
 
5369                 var maxFeet = maxMeters * 3.2808399,
 
5370                     maxMiles, miles, feet;
 
5372                 if (maxFeet > 5280) {
 
5373                         maxMiles = maxFeet / 5280;
 
5374                         miles = this._getRoundNum(maxMiles);
 
5375                         this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
 
5378                         feet = this._getRoundNum(maxFeet);
 
5379                         this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
 
5383         _updateScale: function (scale, text, ratio) {
 
5384                 scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
 
5385                 scale.innerHTML = text;
 
5388         _getRoundNum: function (num) {
 
5389                 var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
 
5402 // @factory L.control.scale(options?: Control.Scale options)
 
5403 // Creates an scale control with the given options.
 
5404 var scale = function (options) {
 
5405         return new Scale(options);
 
5409  * @class Control.Attribution
 
5410  * @aka L.Control.Attribution
 
5413  * 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.
 
5416 var Attribution = Control.extend({
 
5418         // @aka Control.Attribution options
 
5420                 position: 'bottomright',
 
5422                 // @option prefix: String = 'Leaflet'
 
5423                 // The HTML text shown before the attributions. Pass `false` to disable.
 
5424                 prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
 
5427         initialize: function (options) {
 
5428                 setOptions(this, options);
 
5430                 this._attributions = {};
 
5433         onAdd: function (map) {
 
5434                 map.attributionControl = this;
 
5435                 this._container = create$1('div', 'leaflet-control-attribution');
 
5436                 disableClickPropagation(this._container);
 
5438                 // TODO ugly, refactor
 
5439                 for (var i in map._layers) {
 
5440                         if (map._layers[i].getAttribution) {
 
5441                                 this.addAttribution(map._layers[i].getAttribution());
 
5447                 return this._container;
 
5450         // @method setPrefix(prefix: String): this
 
5451         // Sets the text before the attributions.
 
5452         setPrefix: function (prefix) {
 
5453                 this.options.prefix = prefix;
 
5458         // @method addAttribution(text: String): this
 
5459         // Adds an attribution text (e.g. `'Vector data © Mapbox'`).
 
5460         addAttribution: function (text) {
 
5461                 if (!text) { return this; }
 
5463                 if (!this._attributions[text]) {
 
5464                         this._attributions[text] = 0;
 
5466                 this._attributions[text]++;
 
5473         // @method removeAttribution(text: String): this
 
5474         // Removes an attribution text.
 
5475         removeAttribution: function (text) {
 
5476                 if (!text) { return this; }
 
5478                 if (this._attributions[text]) {
 
5479                         this._attributions[text]--;
 
5486         _update: function () {
 
5487                 if (!this._map) { return; }
 
5491                 for (var i in this._attributions) {
 
5492                         if (this._attributions[i]) {
 
5497                 var prefixAndAttribs = [];
 
5499                 if (this.options.prefix) {
 
5500                         prefixAndAttribs.push(this.options.prefix);
 
5502                 if (attribs.length) {
 
5503                         prefixAndAttribs.push(attribs.join(', '));
 
5506                 this._container.innerHTML = prefixAndAttribs.join(' | ');
 
5511 // @section Control options
 
5512 // @option attributionControl: Boolean = true
 
5513 // Whether a [attribution control](#control-attribution) is added to the map by default.
 
5515         attributionControl: true
 
5518 Map.addInitHook(function () {
 
5519         if (this.options.attributionControl) {
 
5520                 new Attribution().addTo(this);
 
5524 // @namespace Control.Attribution
 
5525 // @factory L.control.attribution(options: Control.Attribution options)
 
5526 // Creates an attribution control.
 
5527 var attribution = function (options) {
 
5528         return new Attribution(options);
 
5531 Control.Layers = Layers;
 
5532 Control.Zoom = Zoom;
 
5533 Control.Scale = Scale;
 
5534 Control.Attribution = Attribution;
 
5536 control.layers = layers;
 
5537 control.zoom = zoom;
 
5538 control.scale = scale;
 
5539 control.attribution = attribution;
 
5542         L.Handler is a base class for handler classes that are used internally to inject
 
5543         interaction features like dragging to classes like Map and Marker.
 
5548 // Abstract class for map interaction handlers
 
5550 var Handler = Class.extend({
 
5551         initialize: function (map) {
 
5555         // @method enable(): this
 
5556         // Enables the handler
 
5557         enable: function () {
 
5558                 if (this._enabled) { return this; }
 
5560                 this._enabled = true;
 
5565         // @method disable(): this
 
5566         // Disables the handler
 
5567         disable: function () {
 
5568                 if (!this._enabled) { return this; }
 
5570                 this._enabled = false;
 
5575         // @method enabled(): Boolean
 
5576         // Returns `true` if the handler is enabled
 
5577         enabled: function () {
 
5578                 return !!this._enabled;
 
5581         // @section Extension methods
 
5582         // Classes inheriting from `Handler` must implement the two following methods:
 
5583         // @method addHooks()
 
5584         // Called when the handler is enabled, should add event hooks.
 
5585         // @method removeHooks()
 
5586         // Called when the handler is disabled, should remove the event hooks added previously.
 
5589 var Mixin = {Events: Events};
 
5596  * A class for making DOM elements draggable (including touch support).
 
5597  * Used internally for map and marker dragging. Only works for elements
 
5598  * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
 
5602  * var draggable = new L.Draggable(elementToDrag);
 
5603  * draggable.enable();
 
5607 var START = touch ? 'touchstart mousedown' : 'mousedown';
 
5609         mousedown: 'mouseup',
 
5610         touchstart: 'touchend',
 
5611         pointerdown: 'touchend',
 
5612         MSPointerDown: 'touchend'
 
5615         mousedown: 'mousemove',
 
5616         touchstart: 'touchmove',
 
5617         pointerdown: 'touchmove',
 
5618         MSPointerDown: 'touchmove'
 
5622 var Draggable = Evented.extend({
 
5626                 // @aka Draggable options
 
5627                 // @option clickTolerance: Number = 3
 
5628                 // The max number of pixels a user can shift the mouse pointer during a click
 
5629                 // for it to be considered a valid click (as opposed to a mouse drag).
 
5633         // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
 
5634         // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
 
5635         initialize: function (element, dragStartTarget, preventOutline$$1, options) {
 
5636                 setOptions(this, options);
 
5638                 this._element = element;
 
5639                 this._dragStartTarget = dragStartTarget || element;
 
5640                 this._preventOutline = preventOutline$$1;
 
5644         // Enables the dragging ability
 
5645         enable: function () {
 
5646                 if (this._enabled) { return; }
 
5648                 on(this._dragStartTarget, START, this._onDown, this);
 
5650                 this._enabled = true;
 
5653         // @method disable()
 
5654         // Disables the dragging ability
 
5655         disable: function () {
 
5656                 if (!this._enabled) { return; }
 
5658                 // If we're currently dragging this draggable,
 
5659                 // disabling it counts as first ending the drag.
 
5660                 if (Draggable._dragging === this) {
 
5664                 off(this._dragStartTarget, START, this._onDown, this);
 
5666                 this._enabled = false;
 
5667                 this._moved = false;
 
5670         _onDown: function (e) {
 
5671                 // Ignore simulated events, since we handle both touch and
 
5672                 // mouse explicitly; otherwise we risk getting duplicates of
 
5673                 // touch events, see #4315.
 
5674                 // Also ignore the event if disabled; this happens in IE11
 
5675                 // under some circumstances, see #3666.
 
5676                 if (e._simulated || !this._enabled) { return; }
 
5678                 this._moved = false;
 
5680                 if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
 
5682                 if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
 
5683                 Draggable._dragging = this;  // Prevent dragging multiple objects at once.
 
5685                 if (this._preventOutline) {
 
5686                         preventOutline(this._element);
 
5690                 disableTextSelection();
 
5692                 if (this._moving) { return; }
 
5694                 // @event down: Event
 
5695                 // Fired when a drag is about to start.
 
5698                 var first = e.touches ? e.touches[0] : e;
 
5700                 this._startPoint = new Point(first.clientX, first.clientY);
 
5702                 on(document, MOVE[e.type], this._onMove, this);
 
5703                 on(document, END[e.type], this._onUp, this);
 
5706         _onMove: function (e) {
 
5707                 // Ignore simulated events, since we handle both touch and
 
5708                 // mouse explicitly; otherwise we risk getting duplicates of
 
5709                 // touch events, see #4315.
 
5710                 // Also ignore the event if disabled; this happens in IE11
 
5711                 // under some circumstances, see #3666.
 
5712                 if (e._simulated || !this._enabled) { return; }
 
5714                 if (e.touches && e.touches.length > 1) {
 
5719                 var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
 
5720                     newPoint = new Point(first.clientX, first.clientY),
 
5721                     offset = newPoint.subtract(this._startPoint);
 
5723                 if (!offset.x && !offset.y) { return; }
 
5724                 if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
 
5729                         // @event dragstart: Event
 
5730                         // Fired when a drag starts
 
5731                         this.fire('dragstart');
 
5734                         this._startPos = getPosition(this._element).subtract(offset);
 
5736                         addClass(document.body, 'leaflet-dragging');
 
5738                         this._lastTarget = e.target || e.srcElement;
 
5739                         // IE and Edge do not give the <use> element, so fetch it
 
5741                         if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
 
5742                                 this._lastTarget = this._lastTarget.correspondingUseElement;
 
5744                         addClass(this._lastTarget, 'leaflet-drag-target');
 
5747                 this._newPos = this._startPos.add(offset);
 
5748                 this._moving = true;
 
5750                 cancelAnimFrame(this._animRequest);
 
5751                 this._lastEvent = e;
 
5752                 this._animRequest = requestAnimFrame(this._updatePosition, this, true);
 
5755         _updatePosition: function () {
 
5756                 var e = {originalEvent: this._lastEvent};
 
5758                 // @event predrag: Event
 
5759                 // Fired continuously during dragging *before* each corresponding
 
5760                 // update of the element's position.
 
5761                 this.fire('predrag', e);
 
5762                 setPosition(this._element, this._newPos);
 
5764                 // @event drag: Event
 
5765                 // Fired continuously during dragging.
 
5766                 this.fire('drag', e);
 
5769         _onUp: function (e) {
 
5770                 // Ignore simulated events, since we handle both touch and
 
5771                 // mouse explicitly; otherwise we risk getting duplicates of
 
5772                 // touch events, see #4315.
 
5773                 // Also ignore the event if disabled; this happens in IE11
 
5774                 // under some circumstances, see #3666.
 
5775                 if (e._simulated || !this._enabled) { return; }
 
5779         finishDrag: function () {
 
5780                 removeClass(document.body, 'leaflet-dragging');
 
5782                 if (this._lastTarget) {
 
5783                         removeClass(this._lastTarget, 'leaflet-drag-target');
 
5784                         this._lastTarget = null;
 
5787                 for (var i in MOVE) {
 
5788                         off(document, MOVE[i], this._onMove, this);
 
5789                         off(document, END[i], this._onUp, this);
 
5793                 enableTextSelection();
 
5795                 if (this._moved && this._moving) {
 
5796                         // ensure drag is not fired after dragend
 
5797                         cancelAnimFrame(this._animRequest);
 
5799                         // @event dragend: DragEndEvent
 
5800                         // Fired when the drag ends.
 
5801                         this.fire('dragend', {
 
5802                                 distance: this._newPos.distanceTo(this._startPos)
 
5806                 this._moving = false;
 
5807                 Draggable._dragging = false;
 
5813  * @namespace LineUtil
 
5815  * Various utility functions for polyine points processing, used by Leaflet internally to make polylines lightning-fast.
 
5818 // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
 
5819 // Improves rendering performance dramatically by lessening the number of points to draw.
 
5821 // @function simplify(points: Point[], tolerance: Number): Point[]
 
5822 // Dramatically reduces the number of points in a polyline while retaining
 
5823 // its shape and returns a new array of simplified points, using the
 
5824 // [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
 
5825 // Used for a huge performance boost when processing/displaying Leaflet polylines for
 
5826 // each zoom level and also reducing visual noise. tolerance affects the amount of
 
5827 // simplification (lesser value means higher quality but slower and with more points).
 
5828 // Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
 
5829 function simplify(points, tolerance) {
 
5830         if (!tolerance || !points.length) {
 
5831                 return points.slice();
 
5834         var sqTolerance = tolerance * tolerance;
 
5836             // stage 1: vertex reduction
 
5837             points = _reducePoints(points, sqTolerance);
 
5839             // stage 2: Douglas-Peucker simplification
 
5840             points = _simplifyDP(points, sqTolerance);
 
5845 // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
 
5846 // Returns the distance between point `p` and segment `p1` to `p2`.
 
5847 function pointToSegmentDistance(p, p1, p2) {
 
5848         return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
 
5851 // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
 
5852 // Returns the closest point from a point `p` on a segment `p1` to `p2`.
 
5853 function closestPointOnSegment(p, p1, p2) {
 
5854         return _sqClosestPointOnSegment(p, p1, p2);
 
5857 // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
 
5858 function _simplifyDP(points, sqTolerance) {
 
5860         var len = points.length,
 
5861             ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
 
5862             markers = new ArrayConstructor(len);
 
5864             markers[0] = markers[len - 1] = 1;
 
5866         _simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
 
5871         for (i = 0; i < len; i++) {
 
5873                         newPoints.push(points[i]);
 
5880 function _simplifyDPStep(points, markers, sqTolerance, first, last) {
 
5885         for (i = first + 1; i <= last - 1; i++) {
 
5886                 sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
 
5888                 if (sqDist > maxSqDist) {
 
5894         if (maxSqDist > sqTolerance) {
 
5897                 _simplifyDPStep(points, markers, sqTolerance, first, index);
 
5898                 _simplifyDPStep(points, markers, sqTolerance, index, last);
 
5902 // reduce points that are too close to each other to a single point
 
5903 function _reducePoints(points, sqTolerance) {
 
5904         var reducedPoints = [points[0]];
 
5906         for (var i = 1, prev = 0, len = points.length; i < len; i++) {
 
5907                 if (_sqDist(points[i], points[prev]) > sqTolerance) {
 
5908                         reducedPoints.push(points[i]);
 
5912         if (prev < len - 1) {
 
5913                 reducedPoints.push(points[len - 1]);
 
5915         return reducedPoints;
 
5920 // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
 
5921 // Clips the segment a to b by rectangular bounds with the
 
5922 // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
 
5923 // (modifying the segment points directly!). Used by Leaflet to only show polyline
 
5924 // points that are on the screen or near, increasing performance.
 
5925 function clipSegment(a, b, bounds, useLastCode, round) {
 
5926         var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
 
5927             codeB = _getBitCode(b, bounds),
 
5929             codeOut, p, newCode;
 
5931             // save 2nd code to avoid calculating it on the next segment
 
5935                 // if a,b is inside the clip window (trivial accept)
 
5936                 if (!(codeA | codeB)) {
 
5940                 // if a,b is outside the clip window (trivial reject)
 
5941                 if (codeA & codeB) {
 
5946                 codeOut = codeA || codeB;
 
5947                 p = _getEdgeIntersection(a, b, codeOut, bounds, round);
 
5948                 newCode = _getBitCode(p, bounds);
 
5950                 if (codeOut === codeA) {
 
5960 function _getEdgeIntersection(a, b, code, bounds, round) {
 
5967         if (code & 8) { // top
 
5968                 x = a.x + dx * (max.y - a.y) / dy;
 
5971         } else if (code & 4) { // bottom
 
5972                 x = a.x + dx * (min.y - a.y) / dy;
 
5975         } else if (code & 2) { // right
 
5977                 y = a.y + dy * (max.x - a.x) / dx;
 
5979         } else if (code & 1) { // left
 
5981                 y = a.y + dy * (min.x - a.x) / dx;
 
5984         return new Point(x, y, round);
 
5987 function _getBitCode(p, bounds) {
 
5990         if (p.x < bounds.min.x) { // left
 
5992         } else if (p.x > bounds.max.x) { // right
 
5996         if (p.y < bounds.min.y) { // bottom
 
5998         } else if (p.y > bounds.max.y) { // top
 
6005 // square distance (to avoid unnecessary Math.sqrt calls)
 
6006 function _sqDist(p1, p2) {
 
6007         var dx = p2.x - p1.x,
 
6009         return dx * dx + dy * dy;
 
6012 // return closest point on segment or distance to that point
 
6013 function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
 
6018             dot = dx * dx + dy * dy,
 
6022                 t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
 
6036         return sqDist ? dx * dx + dy * dy : new Point(x, y);
 
6040 // @function isFlat(latlngs: LatLng[]): Boolean
 
6041 // Returns true if `latlngs` is a flat array, false is nested.
 
6042 function isFlat(latlngs) {
 
6043         return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
 
6046 function _flat(latlngs) {
 
6047         console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
 
6048         return isFlat(latlngs);
 
6052 var LineUtil = (Object.freeze || Object)({
 
6054         pointToSegmentDistance: pointToSegmentDistance,
 
6055         closestPointOnSegment: closestPointOnSegment,
 
6056         clipSegment: clipSegment,
 
6057         _getEdgeIntersection: _getEdgeIntersection,
 
6058         _getBitCode: _getBitCode,
 
6059         _sqClosestPointOnSegment: _sqClosestPointOnSegment,
 
6065  * @namespace PolyUtil
 
6066  * Various utility functions for polygon geometries.
 
6069 /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
 
6070  * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgeman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)).
 
6071  * Used by Leaflet to only show polygon points that are on the screen or near, increasing
 
6072  * performance. Note that polygon points needs different algorithm for clipping
 
6073  * than polyline, so there's a seperate method for it.
 
6075 function clipPolygon(points, bounds, round) {
 
6077             edges = [1, 4, 2, 8],
 
6082         for (i = 0, len = points.length; i < len; i++) {
 
6083                 points[i]._code = _getBitCode(points[i], bounds);
 
6086         // for each edge (left, bottom, right, top)
 
6087         for (k = 0; k < 4; k++) {
 
6091                 for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
 
6095                         // if a is inside the clip window
 
6096                         if (!(a._code & edge)) {
 
6097                                 // if b is outside the clip window (a->b goes out of screen)
 
6098                                 if (b._code & edge) {
 
6099                                         p = _getEdgeIntersection(b, a, edge, bounds, round);
 
6100                                         p._code = _getBitCode(p, bounds);
 
6101                                         clippedPoints.push(p);
 
6103                                 clippedPoints.push(a);
 
6105                         // else if b is inside the clip window (a->b enters the screen)
 
6106                         } else if (!(b._code & edge)) {
 
6107                                 p = _getEdgeIntersection(b, a, edge, bounds, round);
 
6108                                 p._code = _getBitCode(p, bounds);
 
6109                                 clippedPoints.push(p);
 
6112                 points = clippedPoints;
 
6119 var PolyUtil = (Object.freeze || Object)({
 
6120         clipPolygon: clipPolygon
 
6124  * @namespace Projection
 
6126  * Leaflet comes with a set of already defined Projections out of the box:
 
6128  * @projection L.Projection.LonLat
 
6130  * Equirectangular, or Plate Carree projection — the most simple projection,
 
6131  * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
 
6132  * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
 
6133  * `EPSG:4326` and `Simple` CRS.
 
6137         project: function (latlng) {
 
6138                 return new Point(latlng.lng, latlng.lat);
 
6141         unproject: function (point) {
 
6142                 return new LatLng(point.y, point.x);
 
6145         bounds: new Bounds([-180, -90], [180, 90])
 
6149  * @namespace Projection
 
6150  * @projection L.Projection.Mercator
 
6152  * 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.
 
6157         R_MINOR: 6356752.314245179,
 
6159         bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
 
6161         project: function (latlng) {
 
6162                 var d = Math.PI / 180,
 
6165                     tmp = this.R_MINOR / r,
 
6166                     e = Math.sqrt(1 - tmp * tmp),
 
6167                     con = e * Math.sin(y);
 
6169                 var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
 
6170                 y = -r * Math.log(Math.max(ts, 1E-10));
 
6172                 return new Point(latlng.lng * d * r, y);
 
6175         unproject: function (point) {
 
6176                 var d = 180 / Math.PI,
 
6178                     tmp = this.R_MINOR / r,
 
6179                     e = Math.sqrt(1 - tmp * tmp),
 
6180                     ts = Math.exp(-point.y / r),
 
6181                     phi = Math.PI / 2 - 2 * Math.atan(ts);
 
6183                 for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
 
6184                         con = e * Math.sin(phi);
 
6185                         con = Math.pow((1 - con) / (1 + con), e / 2);
 
6186                         dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
 
6190                 return new LatLng(phi * d, point.x * d / r);
 
6197  * An object with methods for projecting geographical coordinates of the world onto
 
6198  * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection).
 
6200  * @property bounds: Bounds
 
6201  * The bounds (specified in CRS units) where the projection is valid
 
6203  * @method project(latlng: LatLng): Point
 
6204  * Projects geographical coordinates into a 2D point.
 
6205  * Only accepts actual `L.LatLng` instances, not arrays.
 
6207  * @method unproject(point: Point): LatLng
 
6208  * The inverse of `project`. Projects a 2D point into a geographical location.
 
6209  * Only accepts actual `L.Point` instances, not arrays.
 
6216 var index = (Object.freeze || Object)({
 
6219         SphericalMercator: SphericalMercator
 
6224  * @crs L.CRS.EPSG3395
 
6226  * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
 
6228 var EPSG3395 = extend({}, Earth, {
 
6230         projection: Mercator,
 
6232         transformation: (function () {
 
6233                 var scale = 0.5 / (Math.PI * Mercator.R);
 
6234                 return toTransformation(scale, 0.5, -scale, 0.5);
 
6240  * @crs L.CRS.EPSG4326
 
6242  * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
 
6244  * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
 
6245  * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
 
6246  * with this CRS, ensure that there are two 256x256 pixel tiles covering the
 
6247  * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
 
6248  * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
 
6251 var EPSG4326 = extend({}, Earth, {
 
6254         transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
 
6261  * A simple CRS that maps longitude and latitude into `x` and `y` directly.
 
6262  * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
 
6263  * axis should still be inverted (going from bottom to top). `distance()` returns
 
6264  * simple euclidean distance.
 
6267 var Simple = extend({}, CRS, {
 
6269         transformation: toTransformation(1, 0, -1, 0),
 
6271         scale: function (zoom) {
 
6272                 return Math.pow(2, zoom);
 
6275         zoom: function (scale) {
 
6276                 return Math.log(scale) / Math.LN2;
 
6279         distance: function (latlng1, latlng2) {
 
6280                 var dx = latlng2.lng - latlng1.lng,
 
6281                     dy = latlng2.lat - latlng1.lat;
 
6283                 return Math.sqrt(dx * dx + dy * dy);
 
6290 CRS.EPSG3395 = EPSG3395;
 
6291 CRS.EPSG3857 = EPSG3857;
 
6292 CRS.EPSG900913 = EPSG900913;
 
6293 CRS.EPSG4326 = EPSG4326;
 
6294 CRS.Simple = Simple;
 
6302  * A set of methods from the Layer base class that all Leaflet layers use.
 
6303  * Inherits all methods, options and events from `L.Evented`.
 
6308  * var layer = L.Marker(latlng).addTo(map);
 
6314  * Fired after the layer is added to a map
 
6316  * @event remove: Event
 
6317  * Fired after the layer is removed from a map
 
6321 var Layer = Evented.extend({
 
6323         // Classes extending `L.Layer` will inherit the following options:
 
6325                 // @option pane: String = 'overlayPane'
 
6326                 // 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.
 
6327                 pane: 'overlayPane',
 
6329                 // @option attribution: String = null
 
6330                 // String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox".
 
6333                 bubblingMouseEvents: true
 
6337          * Classes extending `L.Layer` will inherit the following methods:
 
6339          * @method addTo(map: Map|LayerGroup): this
 
6340          * Adds the layer to the given map or layer group.
 
6342         addTo: function (map) {
 
6347         // @method remove: this
 
6348         // Removes the layer from the map it is currently active on.
 
6349         remove: function () {
 
6350                 return this.removeFrom(this._map || this._mapToAdd);
 
6353         // @method removeFrom(map: Map): this
 
6354         // Removes the layer from the given map
 
6355         removeFrom: function (obj) {
 
6357                         obj.removeLayer(this);
 
6362         // @method getPane(name? : String): HTMLElement
 
6363         // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
 
6364         getPane: function (name) {
 
6365                 return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
 
6368         addInteractiveTarget: function (targetEl) {
 
6369                 this._map._targets[stamp(targetEl)] = this;
 
6373         removeInteractiveTarget: function (targetEl) {
 
6374                 delete this._map._targets[stamp(targetEl)];
 
6378         // @method getAttribution: String
 
6379         // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
 
6380         getAttribution: function () {
 
6381                 return this.options.attribution;
 
6384         _layerAdd: function (e) {
 
6387                 // check in case layer gets added and then removed before the map is ready
 
6388                 if (!map.hasLayer(this)) { return; }
 
6391                 this._zoomAnimated = map._zoomAnimated;
 
6393                 if (this.getEvents) {
 
6394                         var events = this.getEvents();
 
6395                         map.on(events, this);
 
6396                         this.once('remove', function () {
 
6397                                 map.off(events, this);
 
6403                 if (this.getAttribution && map.attributionControl) {
 
6404                         map.attributionControl.addAttribution(this.getAttribution());
 
6408                 map.fire('layeradd', {layer: this});
 
6412 /* @section Extension methods
 
6415  * Every layer should extend from `L.Layer` and (re-)implement the following methods.
 
6417  * @method onAdd(map: Map): this
 
6418  * 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).
 
6420  * @method onRemove(map: Map): this
 
6421  * 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).
 
6423  * @method getEvents(): Object
 
6424  * 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.
 
6426  * @method getAttribution(): String
 
6427  * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
 
6429  * @method beforeAdd(map: Map): this
 
6430  * 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.
 
6435  * @section Layer events
 
6437  * @event layeradd: LayerEvent
 
6438  * Fired when a new layer is added to the map.
 
6440  * @event layerremove: LayerEvent
 
6441  * Fired when some layer is removed from the map
 
6443  * @section Methods for Layers and Controls
 
6446         // @method addLayer(layer: Layer): this
 
6447         // Adds the given layer to the map
 
6448         addLayer: function (layer) {
 
6449                 if (!layer._layerAdd) {
 
6450                         throw new Error('The provided object is not a Layer.');
 
6453                 var id = stamp(layer);
 
6454                 if (this._layers[id]) { return this; }
 
6455                 this._layers[id] = layer;
 
6457                 layer._mapToAdd = this;
 
6459                 if (layer.beforeAdd) {
 
6460                         layer.beforeAdd(this);
 
6463                 this.whenReady(layer._layerAdd, layer);
 
6468         // @method removeLayer(layer: Layer): this
 
6469         // Removes the given layer from the map.
 
6470         removeLayer: function (layer) {
 
6471                 var id = stamp(layer);
 
6473                 if (!this._layers[id]) { return this; }
 
6476                         layer.onRemove(this);
 
6479                 if (layer.getAttribution && this.attributionControl) {
 
6480                         this.attributionControl.removeAttribution(layer.getAttribution());
 
6483                 delete this._layers[id];
 
6486                         this.fire('layerremove', {layer: layer});
 
6487                         layer.fire('remove');
 
6490                 layer._map = layer._mapToAdd = null;
 
6495         // @method hasLayer(layer: Layer): Boolean
 
6496         // Returns `true` if the given layer is currently added to the map
 
6497         hasLayer: function (layer) {
 
6498                 return !!layer && (stamp(layer) in this._layers);
 
6501         /* @method eachLayer(fn: Function, context?: Object): this
 
6502          * Iterates over the layers of the map, optionally specifying context of the iterator function.
 
6504          * map.eachLayer(function(layer){
 
6505          *     layer.bindPopup('Hello');
 
6509         eachLayer: function (method, context) {
 
6510                 for (var i in this._layers) {
 
6511                         method.call(context, this._layers[i]);
 
6516         _addLayers: function (layers) {
 
6517                 layers = layers ? (isArray(layers) ? layers : [layers]) : [];
 
6519                 for (var i = 0, len = layers.length; i < len; i++) {
 
6520                         this.addLayer(layers[i]);
 
6524         _addZoomLimit: function (layer) {
 
6525                 if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
 
6526                         this._zoomBoundLayers[stamp(layer)] = layer;
 
6527                         this._updateZoomLevels();
 
6531         _removeZoomLimit: function (layer) {
 
6532                 var id = stamp(layer);
 
6534                 if (this._zoomBoundLayers[id]) {
 
6535                         delete this._zoomBoundLayers[id];
 
6536                         this._updateZoomLevels();
 
6540         _updateZoomLevels: function () {
 
6541                 var minZoom = Infinity,
 
6542                     maxZoom = -Infinity,
 
6543                     oldZoomSpan = this._getZoomSpan();
 
6545                 for (var i in this._zoomBoundLayers) {
 
6546                         var options = this._zoomBoundLayers[i].options;
 
6548                         minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
 
6549                         maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
 
6552                 this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
 
6553                 this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
 
6555                 // @section Map state change events
 
6556                 // @event zoomlevelschange: Event
 
6557                 // Fired when the number of zoomlevels on the map is changed due
 
6558                 // to adding or removing a layer.
 
6559                 if (oldZoomSpan !== this._getZoomSpan()) {
 
6560                         this.fire('zoomlevelschange');
 
6563                 if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
 
6564                         this.setZoom(this._layersMaxZoom);
 
6566                 if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
 
6567                         this.setZoom(this._layersMinZoom);
 
6577  * Used to group several layers and handle them as one. If you add it to the map,
 
6578  * any layers added or removed from the group will be added/removed on the map as
 
6579  * well. Extends `Layer`.
 
6584  * L.layerGroup([marker1, marker2])
 
6585  *      .addLayer(polyline)
 
6590 var LayerGroup = Layer.extend({
 
6592         initialize: function (layers) {
 
6598                         for (i = 0, len = layers.length; i < len; i++) {
 
6599                                 this.addLayer(layers[i]);
 
6604         // @method addLayer(layer: Layer): this
 
6605         // Adds the given layer to the group.
 
6606         addLayer: function (layer) {
 
6607                 var id = this.getLayerId(layer);
 
6609                 this._layers[id] = layer;
 
6612                         this._map.addLayer(layer);
 
6618         // @method removeLayer(layer: Layer): this
 
6619         // Removes the given layer from the group.
 
6621         // @method removeLayer(id: Number): this
 
6622         // Removes the layer with the given internal ID from the group.
 
6623         removeLayer: function (layer) {
 
6624                 var id = layer in this._layers ? layer : this.getLayerId(layer);
 
6626                 if (this._map && this._layers[id]) {
 
6627                         this._map.removeLayer(this._layers[id]);
 
6630                 delete this._layers[id];
 
6635         // @method hasLayer(layer: Layer): Boolean
 
6636         // Returns `true` if the given layer is currently added to the group.
 
6638         // @method hasLayer(id: Number): Boolean
 
6639         // Returns `true` if the given internal ID is currently added to the group.
 
6640         hasLayer: function (layer) {
 
6641                 return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
 
6644         // @method clearLayers(): this
 
6645         // Removes all the layers from the group.
 
6646         clearLayers: function () {
 
6647                 for (var i in this._layers) {
 
6648                         this.removeLayer(this._layers[i]);
 
6653         // @method invoke(methodName: String, …): this
 
6654         // Calls `methodName` on every layer contained in this group, passing any
 
6655         // additional parameters. Has no effect if the layers contained do not
 
6656         // implement `methodName`.
 
6657         invoke: function (methodName) {
 
6658                 var args = Array.prototype.slice.call(arguments, 1),
 
6661                 for (i in this._layers) {
 
6662                         layer = this._layers[i];
 
6664                         if (layer[methodName]) {
 
6665                                 layer[methodName].apply(layer, args);
 
6672         onAdd: function (map) {
 
6673                 for (var i in this._layers) {
 
6674                         map.addLayer(this._layers[i]);
 
6678         onRemove: function (map) {
 
6679                 for (var i in this._layers) {
 
6680                         map.removeLayer(this._layers[i]);
 
6684         // @method eachLayer(fn: Function, context?: Object): this
 
6685         // Iterates over the layers of the group, optionally specifying context of the iterator function.
 
6687         // group.eachLayer(function (layer) {
 
6688         //      layer.bindPopup('Hello');
 
6691         eachLayer: function (method, context) {
 
6692                 for (var i in this._layers) {
 
6693                         method.call(context, this._layers[i]);
 
6698         // @method getLayer(id: Number): Layer
 
6699         // Returns the layer with the given internal ID.
 
6700         getLayer: function (id) {
 
6701                 return this._layers[id];
 
6704         // @method getLayers(): Layer[]
 
6705         // Returns an array of all the layers added to the group.
 
6706         getLayers: function () {
 
6709                 for (var i in this._layers) {
 
6710                         layers.push(this._layers[i]);
 
6715         // @method setZIndex(zIndex: Number): this
 
6716         // Calls `setZIndex` on every layer contained in this group, passing the z-index.
 
6717         setZIndex: function (zIndex) {
 
6718                 return this.invoke('setZIndex', zIndex);
 
6721         // @method getLayerId(layer: Layer): Number
 
6722         // Returns the internal ID for a layer
 
6723         getLayerId: function (layer) {
 
6724                 return stamp(layer);
 
6729 // @factory L.layerGroup(layers?: Layer[])
 
6730 // Create a layer group, optionally given an initial set of layers.
 
6731 var layerGroup = function (layers) {
 
6732         return new LayerGroup(layers);
 
6736  * @class FeatureGroup
 
6737  * @aka L.FeatureGroup
 
6738  * @inherits LayerGroup
 
6740  * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
 
6741  *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
 
6742  *  * Events are propagated to the `FeatureGroup`, so if the group has an event
 
6743  * handler, it will handle events from any of the layers. This includes mouse events
 
6744  * and custom events.
 
6745  *  * Has `layeradd` and `layerremove` events
 
6750  * L.featureGroup([marker1, marker2, polyline])
 
6751  *      .bindPopup('Hello world!')
 
6752  *      .on('click', function() { alert('Clicked on a member of the group!'); })
 
6757 var FeatureGroup = LayerGroup.extend({
 
6759         addLayer: function (layer) {
 
6760                 if (this.hasLayer(layer)) {
 
6764                 layer.addEventParent(this);
 
6766                 LayerGroup.prototype.addLayer.call(this, layer);
 
6768                 // @event layeradd: LayerEvent
 
6769                 // Fired when a layer is added to this `FeatureGroup`
 
6770                 return this.fire('layeradd', {layer: layer});
 
6773         removeLayer: function (layer) {
 
6774                 if (!this.hasLayer(layer)) {
 
6777                 if (layer in this._layers) {
 
6778                         layer = this._layers[layer];
 
6781                 layer.removeEventParent(this);
 
6783                 LayerGroup.prototype.removeLayer.call(this, layer);
 
6785                 // @event layerremove: LayerEvent
 
6786                 // Fired when a layer is removed from this `FeatureGroup`
 
6787                 return this.fire('layerremove', {layer: layer});
 
6790         // @method setStyle(style: Path options): this
 
6791         // Sets the given path options to each layer of the group that has a `setStyle` method.
 
6792         setStyle: function (style) {
 
6793                 return this.invoke('setStyle', style);
 
6796         // @method bringToFront(): this
 
6797         // Brings the layer group to the top of all other layers
 
6798         bringToFront: function () {
 
6799                 return this.invoke('bringToFront');
 
6802         // @method bringToBack(): this
 
6803         // Brings the layer group to the top of all other layers
 
6804         bringToBack: function () {
 
6805                 return this.invoke('bringToBack');
 
6808         // @method getBounds(): LatLngBounds
 
6809         // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
 
6810         getBounds: function () {
 
6811                 var bounds = new LatLngBounds();
 
6813                 for (var id in this._layers) {
 
6814                         var layer = this._layers[id];
 
6815                         bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
 
6821 // @factory L.featureGroup(layers: Layer[])
 
6822 // Create a feature group, optionally given an initial set of layers.
 
6823 var featureGroup = function (layers) {
 
6824         return new FeatureGroup(layers);
 
6831  * Represents an icon to provide when creating a marker.
 
6836  * var myIcon = L.icon({
 
6837  *     iconUrl: 'my-icon.png',
 
6838  *     iconRetinaUrl: 'my-icon@2x.png',
 
6839  *     iconSize: [38, 95],
 
6840  *     iconAnchor: [22, 94],
 
6841  *     popupAnchor: [-3, -76],
 
6842  *     shadowUrl: 'my-icon-shadow.png',
 
6843  *     shadowRetinaUrl: 'my-icon-shadow@2x.png',
 
6844  *     shadowSize: [68, 95],
 
6845  *     shadowAnchor: [22, 94]
 
6848  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
 
6851  * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
 
6855 var Icon = Class.extend({
 
6860          * @option iconUrl: String = null
 
6861          * **(required)** The URL to the icon image (absolute or relative to your script path).
 
6863          * @option iconRetinaUrl: String = null
 
6864          * The URL to a retina sized version of the icon image (absolute or relative to your
 
6865          * script path). Used for Retina screen devices.
 
6867          * @option iconSize: Point = null
 
6868          * Size of the icon image in pixels.
 
6870          * @option iconAnchor: Point = null
 
6871          * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
 
6872          * will be aligned so that this point is at the marker's geographical location. Centered
 
6873          * by default if size is specified, also can be set in CSS with negative margins.
 
6875          * @option popupAnchor: Point = null
 
6876          * The coordinates of the point from which popups will "open", relative to the icon anchor.
 
6878          * @option shadowUrl: String = null
 
6879          * The URL to the icon shadow image. If not specified, no shadow image will be created.
 
6881          * @option shadowRetinaUrl: String = null
 
6883          * @option shadowSize: Point = null
 
6884          * Size of the shadow image in pixels.
 
6886          * @option shadowAnchor: Point = null
 
6887          * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
 
6888          * as iconAnchor if not specified).
 
6890          * @option className: String = ''
 
6891          * A custom class name to assign to both icon and shadow images. Empty by default.
 
6894         initialize: function (options) {
 
6895                 setOptions(this, options);
 
6898         // @method createIcon(oldIcon?: HTMLElement): HTMLElement
 
6899         // Called internally when the icon has to be shown, returns a `<img>` HTML element
 
6900         // styled according to the options.
 
6901         createIcon: function (oldIcon) {
 
6902                 return this._createIcon('icon', oldIcon);
 
6905         // @method createShadow(oldIcon?: HTMLElement): HTMLElement
 
6906         // As `createIcon`, but for the shadow beneath it.
 
6907         createShadow: function (oldIcon) {
 
6908                 return this._createIcon('shadow', oldIcon);
 
6911         _createIcon: function (name, oldIcon) {
 
6912                 var src = this._getIconUrl(name);
 
6915                         if (name === 'icon') {
 
6916                                 throw new Error('iconUrl not set in Icon options (see the docs).');
 
6921                 var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
 
6922                 this._setIconStyles(img, name);
 
6927         _setIconStyles: function (img, name) {
 
6928                 var options = this.options;
 
6929                 var sizeOption = options[name + 'Size'];
 
6931                 if (typeof sizeOption === 'number') {
 
6932                         sizeOption = [sizeOption, sizeOption];
 
6935                 var size = toPoint(sizeOption),
 
6936                     anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
 
6937                             size && size.divideBy(2, true));
 
6939                 img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
 
6942                         img.style.marginLeft = (-anchor.x) + 'px';
 
6943                         img.style.marginTop  = (-anchor.y) + 'px';
 
6947                         img.style.width  = size.x + 'px';
 
6948                         img.style.height = size.y + 'px';
 
6952         _createImg: function (src, el) {
 
6953                 el = el || document.createElement('img');
 
6958         _getIconUrl: function (name) {
 
6959                 return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
 
6964 // @factory L.icon(options: Icon options)
 
6965 // Creates an icon instance with the given options.
 
6966 function icon(options) {
 
6967         return new Icon(options);
 
6971  * @miniclass Icon.Default (Icon)
 
6972  * @aka L.Icon.Default
 
6975  * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
 
6976  * no icon is specified. Points to the blue marker image distributed with Leaflet
 
6979  * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
 
6980  * (which is a set of `Icon options`).
 
6982  * If you want to _completely_ replace the default icon, override the
 
6983  * `L.Marker.prototype.options.icon` with your own icon instead.
 
6986 var IconDefault = Icon.extend({
 
6989                 iconUrl:       'marker-icon.png',
 
6990                 iconRetinaUrl: 'marker-icon-2x.png',
 
6991                 shadowUrl:     'marker-shadow.png',
 
6993                 iconAnchor:  [12, 41],
 
6994                 popupAnchor: [1, -34],
 
6995                 tooltipAnchor: [16, -28],
 
6996                 shadowSize:  [41, 41]
 
6999         _getIconUrl: function (name) {
 
7000                 if (!IconDefault.imagePath) {   // Deprecated, backwards-compatibility only
 
7001                         IconDefault.imagePath = this._detectIconPath();
 
7004                 // @option imagePath: String
 
7005                 // `Icon.Default` will try to auto-detect the absolute location of the
 
7006                 // blue icon images. If you are placing these images in a non-standard
 
7007                 // way, set this option to point to the right absolute path.
 
7008                 return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
 
7011         _detectIconPath: function () {
 
7012                 var el = create$1('div',  'leaflet-default-icon-path', document.body);
 
7013                 var path = getStyle(el, 'background-image') ||
 
7014                            getStyle(el, 'backgroundImage');     // IE8
 
7016                 document.body.removeChild(el);
 
7018                 if (path === null || path.indexOf('url') !== 0) {
 
7021                         path = path.replace(/^url\([\"\']?/, '').replace(/marker-icon\.png[\"\']?\)$/, '');
 
7029  * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
 
7033 /* @namespace Marker
 
7034  * @section Interaction handlers
 
7036  * 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:
 
7039  * marker.dragging.disable();
 
7042  * @property dragging: Handler
 
7043  * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
 
7046 var MarkerDrag = Handler.extend({
 
7047         initialize: function (marker) {
 
7048                 this._marker = marker;
 
7051         addHooks: function () {
 
7052                 var icon = this._marker._icon;
 
7054                 if (!this._draggable) {
 
7055                         this._draggable = new Draggable(icon, icon, true);
 
7058                 this._draggable.on({
 
7059                         dragstart: this._onDragStart,
 
7061                         dragend: this._onDragEnd
 
7064                 addClass(icon, 'leaflet-marker-draggable');
 
7067         removeHooks: function () {
 
7068                 this._draggable.off({
 
7069                         dragstart: this._onDragStart,
 
7071                         dragend: this._onDragEnd
 
7074                 if (this._marker._icon) {
 
7075                         removeClass(this._marker._icon, 'leaflet-marker-draggable');
 
7079         moved: function () {
 
7080                 return this._draggable && this._draggable._moved;
 
7083         _onDragStart: function () {
 
7084                 // @section Dragging events
 
7085                 // @event dragstart: Event
 
7086                 // Fired when the user starts dragging the marker.
 
7088                 // @event movestart: Event
 
7089                 // Fired when the marker starts moving (because of dragging).
 
7091                 this._oldLatLng = this._marker.getLatLng();
 
7098         _onDrag: function (e) {
 
7099                 var marker = this._marker,
 
7100                     shadow = marker._shadow,
 
7101                 iconPos = getPosition(marker._icon),
 
7102                     latlng = marker._map.layerPointToLatLng(iconPos);
 
7104                 // update shadow position
 
7106                         setPosition(shadow, iconPos);
 
7109                 marker._latlng = latlng;
 
7111                 e.oldLatLng = this._oldLatLng;
 
7113                 // @event drag: Event
 
7114                 // Fired repeatedly while the user drags the marker.
 
7120         _onDragEnd: function (e) {
 
7121                 // @event dragend: DragEndEvent
 
7122                 // Fired when the user stops dragging the marker.
 
7124                 // @event moveend: Event
 
7125                 // Fired when the marker stops moving (because of dragging).
 
7126                 delete this._oldLatLng;
 
7129                     .fire('dragend', e);
 
7135  * @inherits Interactive layer
 
7137  * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
 
7142  * L.marker([50.5, 30.5]).addTo(map);
 
7146 var Marker = Layer.extend({
 
7149         // @aka Marker options
 
7151                 // @option icon: Icon = *
 
7152                 // Icon instance to use for rendering the marker.
 
7153                 // See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
 
7154                 // If not specified, a common instance of `L.Icon.Default` is used.
 
7155                 icon: new IconDefault(),
 
7157                 // Option inherited from "Interactive layer" abstract class
 
7160                 // @option draggable: Boolean = false
 
7161                 // Whether the marker is draggable with mouse/touch or not.
 
7164                 // @option keyboard: Boolean = true
 
7165                 // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
 
7168                 // @option title: String = ''
 
7169                 // Text for the browser tooltip that appear on marker hover (no tooltip by default).
 
7172                 // @option alt: String = ''
 
7173                 // Text for the `alt` attribute of the icon image (useful for accessibility).
 
7176                 // @option zIndexOffset: Number = 0
 
7177                 // 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).
 
7180                 // @option opacity: Number = 1.0
 
7181                 // The opacity of the marker.
 
7184                 // @option riseOnHover: Boolean = false
 
7185                 // If `true`, the marker will get on top of others when you hover the mouse over it.
 
7188                 // @option riseOffset: Number = 250
 
7189                 // The z-index offset used for the `riseOnHover` feature.
 
7192                 // @option pane: String = 'markerPane'
 
7193                 // `Map pane` where the markers icon will be added.
 
7196                 // @option bubblingMouseEvents: Boolean = false
 
7197                 // When `true`, a mouse event on this marker will trigger the same event on the map
 
7198                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
 
7199                 bubblingMouseEvents: false
 
7204          * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
 
7207         initialize: function (latlng, options) {
 
7208                 setOptions(this, options);
 
7209                 this._latlng = toLatLng(latlng);
 
7212         onAdd: function (map) {
 
7213                 this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
 
7215                 if (this._zoomAnimated) {
 
7216                         map.on('zoomanim', this._animateZoom, this);
 
7223         onRemove: function (map) {
 
7224                 if (this.dragging && this.dragging.enabled()) {
 
7225                         this.options.draggable = true;
 
7226                         this.dragging.removeHooks();
 
7228                 delete this.dragging;
 
7230                 if (this._zoomAnimated) {
 
7231                         map.off('zoomanim', this._animateZoom, this);
 
7235                 this._removeShadow();
 
7238         getEvents: function () {
 
7241                         viewreset: this.update
 
7245         // @method getLatLng: LatLng
 
7246         // Returns the current geographical position of the marker.
 
7247         getLatLng: function () {
 
7248                 return this._latlng;
 
7251         // @method setLatLng(latlng: LatLng): this
 
7252         // Changes the marker position to the given point.
 
7253         setLatLng: function (latlng) {
 
7254                 var oldLatLng = this._latlng;
 
7255                 this._latlng = toLatLng(latlng);
 
7258                 // @event move: Event
 
7259                 // 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`.
 
7260                 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
 
7263         // @method setZIndexOffset(offset: Number): this
 
7264         // Changes the [zIndex offset](#marker-zindexoffset) of the marker.
 
7265         setZIndexOffset: function (offset) {
 
7266                 this.options.zIndexOffset = offset;
 
7267                 return this.update();
 
7270         // @method setIcon(icon: Icon): this
 
7271         // Changes the marker icon.
 
7272         setIcon: function (icon) {
 
7274                 this.options.icon = icon;
 
7282                         this.bindPopup(this._popup, this._popup.options);
 
7288         getElement: function () {
 
7292         update: function () {
 
7295                         var pos = this._map.latLngToLayerPoint(this._latlng).round();
 
7302         _initIcon: function () {
 
7303                 var options = this.options,
 
7304                     classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
 
7306                 var icon = options.icon.createIcon(this._icon),
 
7309                 // if we're not reusing the icon, remove the old one and init new one
 
7310                 if (icon !== this._icon) {
 
7316                         if (options.title) {
 
7317                                 icon.title = options.title;
 
7320                                 icon.alt = options.alt;
 
7324                 addClass(icon, classToAdd);
 
7326                 if (options.keyboard) {
 
7327                         icon.tabIndex = '0';
 
7332                 if (options.riseOnHover) {
 
7334                                 mouseover: this._bringToFront,
 
7335                                 mouseout: this._resetZIndex
 
7339                 var newShadow = options.icon.createShadow(this._shadow),
 
7342                 if (newShadow !== this._shadow) {
 
7343                         this._removeShadow();
 
7348                         addClass(newShadow, classToAdd);
 
7351                 this._shadow = newShadow;
 
7354                 if (options.opacity < 1) {
 
7355                         this._updateOpacity();
 
7360                         this.getPane().appendChild(this._icon);
 
7362                 this._initInteraction();
 
7363                 if (newShadow && addShadow) {
 
7364                         this.getPane('shadowPane').appendChild(this._shadow);
 
7368         _removeIcon: function () {
 
7369                 if (this.options.riseOnHover) {
 
7371                                 mouseover: this._bringToFront,
 
7372                                 mouseout: this._resetZIndex
 
7377                 this.removeInteractiveTarget(this._icon);
 
7382         _removeShadow: function () {
 
7384                         remove(this._shadow);
 
7386                 this._shadow = null;
 
7389         _setPos: function (pos) {
 
7390                 setPosition(this._icon, pos);
 
7393                         setPosition(this._shadow, pos);
 
7396                 this._zIndex = pos.y + this.options.zIndexOffset;
 
7398                 this._resetZIndex();
 
7401         _updateZIndex: function (offset) {
 
7402                 this._icon.style.zIndex = this._zIndex + offset;
 
7405         _animateZoom: function (opt) {
 
7406                 var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
 
7411         _initInteraction: function () {
 
7413                 if (!this.options.interactive) { return; }
 
7415                 addClass(this._icon, 'leaflet-interactive');
 
7417                 this.addInteractiveTarget(this._icon);
 
7420                         var draggable = this.options.draggable;
 
7421                         if (this.dragging) {
 
7422                                 draggable = this.dragging.enabled();
 
7423                                 this.dragging.disable();
 
7426                         this.dragging = new MarkerDrag(this);
 
7429                                 this.dragging.enable();
 
7434         // @method setOpacity(opacity: Number): this
 
7435         // Changes the opacity of the marker.
 
7436         setOpacity: function (opacity) {
 
7437                 this.options.opacity = opacity;
 
7439                         this._updateOpacity();
 
7445         _updateOpacity: function () {
 
7446                 var opacity = this.options.opacity;
 
7448                 setOpacity(this._icon, opacity);
 
7451                         setOpacity(this._shadow, opacity);
 
7455         _bringToFront: function () {
 
7456                 this._updateZIndex(this.options.riseOffset);
 
7459         _resetZIndex: function () {
 
7460                 this._updateZIndex(0);
 
7463         _getPopupAnchor: function () {
 
7464                 return this.options.icon.options.popupAnchor || [0, 0];
 
7467         _getTooltipAnchor: function () {
 
7468                 return this.options.icon.options.tooltipAnchor || [0, 0];
 
7473 // factory L.marker(latlng: LatLng, options? : Marker options)
 
7475 // @factory L.marker(latlng: LatLng, options? : Marker options)
 
7476 // Instantiates a Marker object given a geographical point and optionally an options object.
 
7477 function marker(latlng, options) {
 
7478         return new Marker(latlng, options);
 
7484  * @inherits Interactive layer
 
7486  * An abstract class that contains options and constants shared between vector
 
7487  * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
 
7490 var Path = Layer.extend({
 
7493         // @aka Path options
 
7495                 // @option stroke: Boolean = true
 
7496                 // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
 
7499                 // @option color: String = '#3388ff'
 
7503                 // @option weight: Number = 3
 
7504                 // Stroke width in pixels
 
7507                 // @option opacity: Number = 1.0
 
7511                 // @option lineCap: String= 'round'
 
7512                 // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
 
7515                 // @option lineJoin: String = 'round'
 
7516                 // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
 
7519                 // @option dashArray: String = null
 
7520                 // 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).
 
7523                 // @option dashOffset: String = null
 
7524                 // 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).
 
7527                 // @option fill: Boolean = depends
 
7528                 // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
 
7531                 // @option fillColor: String = *
 
7532                 // Fill color. Defaults to the value of the [`color`](#path-color) option
 
7535                 // @option fillOpacity: Number = 0.2
 
7539                 // @option fillRule: String = 'evenodd'
 
7540                 // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
 
7541                 fillRule: 'evenodd',
 
7545                 // Option inherited from "Interactive layer" abstract class
 
7548                 // @option bubblingMouseEvents: Boolean = true
 
7549                 // When `true`, a mouse event on this path will trigger the same event on the map
 
7550                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
 
7551                 bubblingMouseEvents: true
 
7554         beforeAdd: function (map) {
 
7555                 // Renderer is set here because we need to call renderer.getEvents
 
7556                 // before this.getEvents.
 
7557                 this._renderer = map.getRenderer(this);
 
7560         onAdd: function () {
 
7561                 this._renderer._initPath(this);
 
7563                 this._renderer._addPath(this);
 
7566         onRemove: function () {
 
7567                 this._renderer._removePath(this);
 
7570         // @method redraw(): this
 
7571         // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
 
7572         redraw: function () {
 
7574                         this._renderer._updatePath(this);
 
7579         // @method setStyle(style: Path options): this
 
7580         // Changes the appearance of a Path based on the options in the `Path options` object.
 
7581         setStyle: function (style) {
 
7582                 setOptions(this, style);
 
7583                 if (this._renderer) {
 
7584                         this._renderer._updateStyle(this);
 
7589         // @method bringToFront(): this
 
7590         // Brings the layer to the top of all path layers.
 
7591         bringToFront: function () {
 
7592                 if (this._renderer) {
 
7593                         this._renderer._bringToFront(this);
 
7598         // @method bringToBack(): this
 
7599         // Brings the layer to the bottom of all path layers.
 
7600         bringToBack: function () {
 
7601                 if (this._renderer) {
 
7602                         this._renderer._bringToBack(this);
 
7607         getElement: function () {
 
7611         _reset: function () {
 
7612                 // defined in child classes
 
7617         _clickTolerance: function () {
 
7618                 // used when doing hit detection for Canvas layers
 
7619                 return (this.options.stroke ? this.options.weight / 2 : 0) + (touch ? 10 : 0);
 
7624  * @class CircleMarker
 
7625  * @aka L.CircleMarker
 
7628  * A circle of a fixed size with radius specified in pixels. Extends `Path`.
 
7631 var CircleMarker = Path.extend({
 
7634         // @aka CircleMarker options
 
7638                 // @option radius: Number = 10
 
7639                 // Radius of the circle marker, in pixels
 
7643         initialize: function (latlng, options) {
 
7644                 setOptions(this, options);
 
7645                 this._latlng = toLatLng(latlng);
 
7646                 this._radius = this.options.radius;
 
7649         // @method setLatLng(latLng: LatLng): this
 
7650         // Sets the position of a circle marker to a new location.
 
7651         setLatLng: function (latlng) {
 
7652                 this._latlng = toLatLng(latlng);
 
7654                 return this.fire('move', {latlng: this._latlng});
 
7657         // @method getLatLng(): LatLng
 
7658         // Returns the current geographical position of the circle marker
 
7659         getLatLng: function () {
 
7660                 return this._latlng;
 
7663         // @method setRadius(radius: Number): this
 
7664         // Sets the radius of a circle marker. Units are in pixels.
 
7665         setRadius: function (radius) {
 
7666                 this.options.radius = this._radius = radius;
 
7667                 return this.redraw();
 
7670         // @method getRadius(): Number
 
7671         // Returns the current radius of the circle
 
7672         getRadius: function () {
 
7673                 return this._radius;
 
7676         setStyle : function (options) {
 
7677                 var radius = options && options.radius || this._radius;
 
7678                 Path.prototype.setStyle.call(this, options);
 
7679                 this.setRadius(radius);
 
7683         _project: function () {
 
7684                 this._point = this._map.latLngToLayerPoint(this._latlng);
 
7685                 this._updateBounds();
 
7688         _updateBounds: function () {
 
7689                 var r = this._radius,
 
7690                     r2 = this._radiusY || r,
 
7691                     w = this._clickTolerance(),
 
7692                     p = [r + w, r2 + w];
 
7693                 this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
 
7696         _update: function () {
 
7702         _updatePath: function () {
 
7703                 this._renderer._updateCircle(this);
 
7706         _empty: function () {
 
7707                 return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
 
7710         // Needed by the `Canvas` renderer for interactivity
 
7711         _containsPoint: function (p) {
 
7712                 return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
 
7717 // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
 
7718 // Instantiates a circle marker object given a geographical point, and an optional options object.
 
7719 function circleMarker(latlng, options) {
 
7720         return new CircleMarker(latlng, options);
 
7726  * @inherits CircleMarker
 
7728  * A class for drawing circle overlays on a map. Extends `CircleMarker`.
 
7730  * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
 
7735  * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
 
7739 var Circle = CircleMarker.extend({
 
7741         initialize: function (latlng, options, legacyOptions) {
 
7742                 if (typeof options === 'number') {
 
7743                         // Backwards compatibility with 0.7.x factory (latlng, radius, options?)
 
7744                         options = extend({}, legacyOptions, {radius: options});
 
7746                 setOptions(this, options);
 
7747                 this._latlng = toLatLng(latlng);
 
7749                 if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
 
7752                 // @aka Circle options
 
7753                 // @option radius: Number; Radius of the circle, in meters.
 
7754                 this._mRadius = this.options.radius;
 
7757         // @method setRadius(radius: Number): this
 
7758         // Sets the radius of a circle. Units are in meters.
 
7759         setRadius: function (radius) {
 
7760                 this._mRadius = radius;
 
7761                 return this.redraw();
 
7764         // @method getRadius(): Number
 
7765         // Returns the current radius of a circle. Units are in meters.
 
7766         getRadius: function () {
 
7767                 return this._mRadius;
 
7770         // @method getBounds(): LatLngBounds
 
7771         // Returns the `LatLngBounds` of the path.
 
7772         getBounds: function () {
 
7773                 var half = [this._radius, this._radiusY || this._radius];
 
7775                 return new LatLngBounds(
 
7776                         this._map.layerPointToLatLng(this._point.subtract(half)),
 
7777                         this._map.layerPointToLatLng(this._point.add(half)));
 
7780         setStyle: Path.prototype.setStyle,
 
7782         _project: function () {
 
7784                 var lng = this._latlng.lng,
 
7785                     lat = this._latlng.lat,
 
7787                     crs = map.options.crs;
 
7789                 if (crs.distance === Earth.distance) {
 
7790                         var d = Math.PI / 180,
 
7791                             latR = (this._mRadius / Earth.R) / d,
 
7792                             top = map.project([lat + latR, lng]),
 
7793                             bottom = map.project([lat - latR, lng]),
 
7794                             p = top.add(bottom).divideBy(2),
 
7795                             lat2 = map.unproject(p).lat,
 
7796                             lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
 
7797                                     (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
 
7799                         if (isNaN(lngR) || lngR === 0) {
 
7800                                 lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
 
7803                         this._point = p.subtract(map.getPixelOrigin());
 
7804                         this._radius = isNaN(lngR) ? 0 : Math.max(Math.round(p.x - map.project([lat2, lng - lngR]).x), 1);
 
7805                         this._radiusY = Math.max(Math.round(p.y - top.y), 1);
 
7808                         var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
 
7810                         this._point = map.latLngToLayerPoint(this._latlng);
 
7811                         this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
 
7814                 this._updateBounds();
 
7818 // @factory L.circle(latlng: LatLng, options?: Circle options)
 
7819 // Instantiates a circle object given a geographical point, and an options object
 
7820 // which contains the circle radius.
 
7822 // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
 
7823 // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
 
7824 // Do not use in new applications or plugins.
 
7825 function circle(latlng, options, legacyOptions) {
 
7826         return new Circle(latlng, options, legacyOptions);
 
7834  * A class for drawing polyline overlays on a map. Extends `Path`.
 
7839  * // create a red polyline from an array of LatLng points
 
7846  * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
 
7848  * // zoom the map to the polyline
 
7849  * map.fitBounds(polyline.getBounds());
 
7852  * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
 
7855  * // create a red polyline from an array of arrays of LatLng points
 
7857  *      [[45.51, -122.68],
 
7868 var Polyline = Path.extend({
 
7871         // @aka Polyline options
 
7873                 // @option smoothFactor: Number = 1.0
 
7874                 // How much to simplify the polyline on each zoom level. More means
 
7875                 // better performance and smoother look, and less means more accurate representation.
 
7878                 // @option noClip: Boolean = false
 
7879                 // Disable polyline clipping.
 
7883         initialize: function (latlngs, options) {
 
7884                 setOptions(this, options);
 
7885                 this._setLatLngs(latlngs);
 
7888         // @method getLatLngs(): LatLng[]
 
7889         // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
 
7890         getLatLngs: function () {
 
7891                 return this._latlngs;
 
7894         // @method setLatLngs(latlngs: LatLng[]): this
 
7895         // Replaces all the points in the polyline with the given array of geographical points.
 
7896         setLatLngs: function (latlngs) {
 
7897                 this._setLatLngs(latlngs);
 
7898                 return this.redraw();
 
7901         // @method isEmpty(): Boolean
 
7902         // Returns `true` if the Polyline has no LatLngs.
 
7903         isEmpty: function () {
 
7904                 return !this._latlngs.length;
 
7907         closestLayerPoint: function (p) {
 
7908                 var minDistance = Infinity,
 
7910                     closest = _sqClosestPointOnSegment,
 
7913                 for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
 
7914                         var points = this._parts[j];
 
7916                         for (var i = 1, len = points.length; i < len; i++) {
 
7920                                 var sqDist = closest(p, p1, p2, true);
 
7922                                 if (sqDist < minDistance) {
 
7923                                         minDistance = sqDist;
 
7924                                         minPoint = closest(p, p1, p2);
 
7929                         minPoint.distance = Math.sqrt(minDistance);
 
7934         // @method getCenter(): LatLng
 
7935         // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
 
7936         getCenter: function () {
 
7937                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
7939                         throw new Error('Must add layer to map before using getCenter()');
 
7942                 var i, halfDist, segDist, dist, p1, p2, ratio,
 
7943                     points = this._rings[0],
 
7944                     len = points.length;
 
7946                 if (!len) { return null; }
 
7948                 // polyline centroid algorithm; only uses the first ring if there are multiple
 
7950                 for (i = 0, halfDist = 0; i < len - 1; i++) {
 
7951                         halfDist += points[i].distanceTo(points[i + 1]) / 2;
 
7954                 // The line is so small in the current view that all points are on the same pixel.
 
7955                 if (halfDist === 0) {
 
7956                         return this._map.layerPointToLatLng(points[0]);
 
7959                 for (i = 0, dist = 0; i < len - 1; i++) {
 
7962                         segDist = p1.distanceTo(p2);
 
7965                         if (dist > halfDist) {
 
7966                                 ratio = (dist - halfDist) / segDist;
 
7967                                 return this._map.layerPointToLatLng([
 
7968                                         p2.x - ratio * (p2.x - p1.x),
 
7969                                         p2.y - ratio * (p2.y - p1.y)
 
7975         // @method getBounds(): LatLngBounds
 
7976         // Returns the `LatLngBounds` of the path.
 
7977         getBounds: function () {
 
7978                 return this._bounds;
 
7981         // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
 
7982         // Adds a given point to the polyline. By default, adds to the first ring of
 
7983         // the polyline in case of a multi-polyline, but can be overridden by passing
 
7984         // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
 
7985         addLatLng: function (latlng, latlngs) {
 
7986                 latlngs = latlngs || this._defaultShape();
 
7987                 latlng = toLatLng(latlng);
 
7988                 latlngs.push(latlng);
 
7989                 this._bounds.extend(latlng);
 
7990                 return this.redraw();
 
7993         _setLatLngs: function (latlngs) {
 
7994                 this._bounds = new LatLngBounds();
 
7995                 this._latlngs = this._convertLatLngs(latlngs);
 
7998         _defaultShape: function () {
 
7999                 return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
 
8002         // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
 
8003         _convertLatLngs: function (latlngs) {
 
8005                     flat = isFlat(latlngs);
 
8007                 for (var i = 0, len = latlngs.length; i < len; i++) {
 
8009                                 result[i] = toLatLng(latlngs[i]);
 
8010                                 this._bounds.extend(result[i]);
 
8012                                 result[i] = this._convertLatLngs(latlngs[i]);
 
8019         _project: function () {
 
8020                 var pxBounds = new Bounds();
 
8022                 this._projectLatlngs(this._latlngs, this._rings, pxBounds);
 
8024                 var w = this._clickTolerance(),
 
8025                     p = new Point(w, w);
 
8027                 if (this._bounds.isValid() && pxBounds.isValid()) {
 
8028                         pxBounds.min._subtract(p);
 
8029                         pxBounds.max._add(p);
 
8030                         this._pxBounds = pxBounds;
 
8034         // recursively turns latlngs into a set of rings with projected coordinates
 
8035         _projectLatlngs: function (latlngs, result, projectedBounds) {
 
8036                 var flat = latlngs[0] instanceof LatLng,
 
8037                     len = latlngs.length,
 
8042                         for (i = 0; i < len; i++) {
 
8043                                 ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
 
8044                                 projectedBounds.extend(ring[i]);
 
8048                         for (i = 0; i < len; i++) {
 
8049                                 this._projectLatlngs(latlngs[i], result, projectedBounds);
 
8054         // clip polyline by renderer bounds so that we have less to render for performance
 
8055         _clipPoints: function () {
 
8056                 var bounds = this._renderer._bounds;
 
8059                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
8063                 if (this.options.noClip) {
 
8064                         this._parts = this._rings;
 
8068                 var parts = this._parts,
 
8069                     i, j, k, len, len2, segment, points;
 
8071                 for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
 
8072                         points = this._rings[i];
 
8074                         for (j = 0, len2 = points.length; j < len2 - 1; j++) {
 
8075                                 segment = clipSegment(points[j], points[j + 1], bounds, j, true);
 
8077                                 if (!segment) { continue; }
 
8079                                 parts[k] = parts[k] || [];
 
8080                                 parts[k].push(segment[0]);
 
8082                                 // if segment goes out of screen, or it's the last one, it's the end of the line part
 
8083                                 if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
 
8084                                         parts[k].push(segment[1]);
 
8091         // simplify each clipped part of the polyline for performance
 
8092         _simplifyPoints: function () {
 
8093                 var parts = this._parts,
 
8094                     tolerance = this.options.smoothFactor;
 
8096                 for (var i = 0, len = parts.length; i < len; i++) {
 
8097                         parts[i] = simplify(parts[i], tolerance);
 
8101         _update: function () {
 
8102                 if (!this._map) { return; }
 
8105                 this._simplifyPoints();
 
8109         _updatePath: function () {
 
8110                 this._renderer._updatePoly(this);
 
8113         // Needed by the `Canvas` renderer for interactivity
 
8114         _containsPoint: function (p, closed) {
 
8115                 var i, j, k, len, len2, part,
 
8116                     w = this._clickTolerance();
 
8118                 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
 
8120                 // hit detection for polylines
 
8121                 for (i = 0, len = this._parts.length; i < len; i++) {
 
8122                         part = this._parts[i];
 
8124                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
8125                                 if (!closed && (j === 0)) { continue; }
 
8127                                 if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
 
8136 // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
 
8137 // Instantiates a polyline object given an array of geographical points and
 
8138 // optionally an options object. You can create a `Polyline` object with
 
8139 // multiple separate lines (`MultiPolyline`) by passing an array of arrays
 
8140 // of geographic points.
 
8141 function polyline(latlngs, options) {
 
8142         return new Polyline(latlngs, options);
 
8145 // Retrocompat. Allow plugins to support Leaflet versions before and after 1.1.
 
8146 Polyline._flat = _flat;
 
8151  * @inherits Polyline
 
8153  * A class for drawing polygon overlays on a map. Extends `Polyline`.
 
8155  * 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.
 
8161  * // create a red polygon from an array of LatLng points
 
8162  * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
 
8164  * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
 
8166  * // zoom the map to the polygon
 
8167  * map.fitBounds(polygon.getBounds());
 
8170  * 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:
 
8174  *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
 
8175  *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
 
8179  * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
 
8183  *   [ // first polygon
 
8184  *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
 
8185  *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
 
8187  *   [ // second polygon
 
8188  *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
 
8194 var Polygon = Polyline.extend({
 
8200         isEmpty: function () {
 
8201                 return !this._latlngs.length || !this._latlngs[0].length;
 
8204         getCenter: function () {
 
8205                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
8207                         throw new Error('Must add layer to map before using getCenter()');
 
8210                 var i, j, p1, p2, f, area, x, y, center,
 
8211                     points = this._rings[0],
 
8212                     len = points.length;
 
8214                 if (!len) { return null; }
 
8216                 // polygon centroid algorithm; only uses the first ring if there are multiple
 
8220                 for (i = 0, j = len - 1; i < len; j = i++) {
 
8224                         f = p1.y * p2.x - p2.y * p1.x;
 
8225                         x += (p1.x + p2.x) * f;
 
8226                         y += (p1.y + p2.y) * f;
 
8231                         // Polygon is so small that all points are on same pixel.
 
8234                         center = [x / area, y / area];
 
8236                 return this._map.layerPointToLatLng(center);
 
8239         _convertLatLngs: function (latlngs) {
 
8240                 var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
 
8241                     len = result.length;
 
8243                 // remove last point if it equals first one
 
8244                 if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
 
8250         _setLatLngs: function (latlngs) {
 
8251                 Polyline.prototype._setLatLngs.call(this, latlngs);
 
8252                 if (isFlat(this._latlngs)) {
 
8253                         this._latlngs = [this._latlngs];
 
8257         _defaultShape: function () {
 
8258                 return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
 
8261         _clipPoints: function () {
 
8262                 // polygons need a different clipping algorithm so we redefine that
 
8264                 var bounds = this._renderer._bounds,
 
8265                     w = this.options.weight,
 
8266                     p = new Point(w, w);
 
8268                 // increase clip padding by stroke width to avoid stroke on clip edges
 
8269                 bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
 
8272                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
8276                 if (this.options.noClip) {
 
8277                         this._parts = this._rings;
 
8281                 for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
 
8282                         clipped = clipPolygon(this._rings[i], bounds, true);
 
8283                         if (clipped.length) {
 
8284                                 this._parts.push(clipped);
 
8289         _updatePath: function () {
 
8290                 this._renderer._updatePoly(this, true);
 
8293         // Needed by the `Canvas` renderer for interactivity
 
8294         _containsPoint: function (p) {
 
8296                     part, p1, p2, i, j, k, len, len2;
 
8298                 if (!this._pxBounds.contains(p)) { return false; }
 
8300                 // ray casting algorithm for detecting if point is in polygon
 
8301                 for (i = 0, len = this._parts.length; i < len; i++) {
 
8302                         part = this._parts[i];
 
8304                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
8308                                 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)) {
 
8314                 // also check if it's on polygon stroke
 
8315                 return inside || Polyline.prototype._containsPoint.call(this, p, true);
 
8321 // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
 
8322 function polygon(latlngs, options) {
 
8323         return new Polygon(latlngs, options);
 
8329  * @inherits FeatureGroup
 
8331  * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
 
8332  * GeoJSON data and display it on the map. Extends `FeatureGroup`.
 
8338  *      style: function (feature) {
 
8339  *              return {color: feature.properties.color};
 
8341  * }).bindPopup(function (layer) {
 
8342  *      return layer.feature.properties.description;
 
8347 var GeoJSON = FeatureGroup.extend({
 
8350          * @aka GeoJSON options
 
8352          * @option pointToLayer: Function = *
 
8353          * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
 
8354          * called when data is added, passing the GeoJSON point feature and its `LatLng`.
 
8355          * The default is to spawn a default `Marker`:
 
8357          * function(geoJsonPoint, latlng) {
 
8358          *      return L.marker(latlng);
 
8362          * @option style: Function = *
 
8363          * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
 
8364          * called internally when data is added.
 
8365          * The default value is to not override any defaults:
 
8367          * function (geoJsonFeature) {
 
8372          * @option onEachFeature: Function = *
 
8373          * A `Function` that will be called once for each created `Feature`, after it has
 
8374          * been created and styled. Useful for attaching events and popups to features.
 
8375          * The default is to do nothing with the newly created layers:
 
8377          * function (feature, layer) {}
 
8380          * @option filter: Function = *
 
8381          * A `Function` that will be used to decide whether to include a feature or not.
 
8382          * The default is to include all features:
 
8384          * function (geoJsonFeature) {
 
8388          * Note: dynamically changing the `filter` option will have effect only on newly
 
8389          * added data. It will _not_ re-evaluate already included features.
 
8391          * @option coordsToLatLng: Function = *
 
8392          * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
 
8393          * The default is the `coordsToLatLng` static method.
 
8396         initialize: function (geojson, options) {
 
8397                 setOptions(this, options);
 
8402                         this.addData(geojson);
 
8406         // @method addData( <GeoJSON> data ): this
 
8407         // Adds a GeoJSON object to the layer.
 
8408         addData: function (geojson) {
 
8409                 var features = isArray(geojson) ? geojson : geojson.features,
 
8413                         for (i = 0, len = features.length; i < len; i++) {
 
8414                                 // only add this if geometry or geometries are set and not null
 
8415                                 feature = features[i];
 
8416                                 if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
 
8417                                         this.addData(feature);
 
8423                 var options = this.options;
 
8425                 if (options.filter && !options.filter(geojson)) { return this; }
 
8427                 var layer = geometryToLayer(geojson, options);
 
8431                 layer.feature = asFeature(geojson);
 
8433                 layer.defaultOptions = layer.options;
 
8434                 this.resetStyle(layer);
 
8436                 if (options.onEachFeature) {
 
8437                         options.onEachFeature(geojson, layer);
 
8440                 return this.addLayer(layer);
 
8443         // @method resetStyle( <Path> layer ): this
 
8444         // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
 
8445         resetStyle: function (layer) {
 
8446                 // reset any custom styles
 
8447                 layer.options = extend({}, layer.defaultOptions);
 
8448                 this._setLayerStyle(layer, this.options.style);
 
8452         // @method setStyle( <Function> style ): this
 
8453         // Changes styles of GeoJSON vector layers with the given style function.
 
8454         setStyle: function (style) {
 
8455                 return this.eachLayer(function (layer) {
 
8456                         this._setLayerStyle(layer, style);
 
8460         _setLayerStyle: function (layer, style) {
 
8461                 if (typeof style === 'function') {
 
8462                         style = style(layer.feature);
 
8464                 if (layer.setStyle) {
 
8465                         layer.setStyle(style);
 
8471 // There are several static functions which can be called without instantiating L.GeoJSON:
 
8473 // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
 
8474 // Creates a `Layer` from a given GeoJSON feature. Can use a custom
 
8475 // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
 
8476 // functions if provided as options.
 
8477 function geometryToLayer(geojson, options) {
 
8479         var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
 
8480             coords = geometry ? geometry.coordinates : null,
 
8482             pointToLayer = options && options.pointToLayer,
 
8483             _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
 
8484             latlng, latlngs, i, len;
 
8486         if (!coords && !geometry) {
 
8490         switch (geometry.type) {
 
8492                 latlng = _coordsToLatLng(coords);
 
8493                 return pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng);
 
8496                 for (i = 0, len = coords.length; i < len; i++) {
 
8497                         latlng = _coordsToLatLng(coords[i]);
 
8498                         layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng));
 
8500                 return new FeatureGroup(layers);
 
8503         case 'MultiLineString':
 
8504                 latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
 
8505                 return new Polyline(latlngs, options);
 
8508         case 'MultiPolygon':
 
8509                 latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
 
8510                 return new Polygon(latlngs, options);
 
8512         case 'GeometryCollection':
 
8513                 for (i = 0, len = geometry.geometries.length; i < len; i++) {
 
8514                         var layer = geometryToLayer({
 
8515                                 geometry: geometry.geometries[i],
 
8517                                 properties: geojson.properties
 
8524                 return new FeatureGroup(layers);
 
8527                 throw new Error('Invalid GeoJSON object.');
 
8531 // @function coordsToLatLng(coords: Array): LatLng
 
8532 // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
 
8533 // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
 
8534 function coordsToLatLng(coords) {
 
8535         return new LatLng(coords[1], coords[0], coords[2]);
 
8538 // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
 
8539 // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
 
8540 // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
 
8541 // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
 
8542 function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
 
8545         for (var i = 0, len = coords.length, latlng; i < len; i++) {
 
8546                 latlng = levelsDeep ?
 
8547                                 coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
 
8548                                 (_coordsToLatLng || coordsToLatLng)(coords[i]);
 
8550                 latlngs.push(latlng);
 
8556 // @function latLngToCoords(latlng: LatLng, precision?: Number): Array
 
8557 // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
 
8558 function latLngToCoords(latlng, precision) {
 
8559         precision = typeof precision === 'number' ? precision : 6;
 
8560         return latlng.alt !== undefined ?
 
8561                         [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
 
8562                         [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
 
8565 // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
 
8566 // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
 
8567 // `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.
 
8568 function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
 
8571         for (var i = 0, len = latlngs.length; i < len; i++) {
 
8572                 coords.push(levelsDeep ?
 
8573                         latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) :
 
8574                         latLngToCoords(latlngs[i], precision));
 
8577         if (!levelsDeep && closed) {
 
8578                 coords.push(coords[0]);
 
8584 function getFeature(layer, newGeometry) {
 
8585         return layer.feature ?
 
8586                         extend({}, layer.feature, {geometry: newGeometry}) :
 
8587                         asFeature(newGeometry);
 
8590 // @function asFeature(geojson: Object): Object
 
8591 // Normalize GeoJSON geometries/features into GeoJSON features.
 
8592 function asFeature(geojson) {
 
8593         if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
 
8604 var PointToGeoJSON = {
 
8605         toGeoJSON: function (precision) {
 
8606                 return getFeature(this, {
 
8608                         coordinates: latLngToCoords(this.getLatLng(), precision)
 
8613 // @namespace Marker
 
8614 // @method toGeoJSON(): Object
 
8615 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
 
8616 Marker.include(PointToGeoJSON);
 
8618 // @namespace CircleMarker
 
8619 // @method toGeoJSON(): Object
 
8620 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
 
8621 Circle.include(PointToGeoJSON);
 
8622 CircleMarker.include(PointToGeoJSON);
 
8625 // @namespace Polyline
 
8626 // @method toGeoJSON(): Object
 
8627 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
 
8629         toGeoJSON: function (precision) {
 
8630                 var multi = !isFlat(this._latlngs);
 
8632                 var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
 
8634                 return getFeature(this, {
 
8635                         type: (multi ? 'Multi' : '') + 'LineString',
 
8641 // @namespace Polygon
 
8642 // @method toGeoJSON(): Object
 
8643 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
 
8645         toGeoJSON: function (precision) {
 
8646                 var holes = !isFlat(this._latlngs),
 
8647                     multi = holes && !isFlat(this._latlngs[0]);
 
8649                 var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
 
8655                 return getFeature(this, {
 
8656                         type: (multi ? 'Multi' : '') + 'Polygon',
 
8663 // @namespace LayerGroup
 
8664 LayerGroup.include({
 
8665         toMultiPoint: function (precision) {
 
8668                 this.eachLayer(function (layer) {
 
8669                         coords.push(layer.toGeoJSON(precision).geometry.coordinates);
 
8672                 return getFeature(this, {
 
8678         // @method toGeoJSON(): Object
 
8679         // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
 
8680         toGeoJSON: function (precision) {
 
8682                 var type = this.feature && this.feature.geometry && this.feature.geometry.type;
 
8684                 if (type === 'MultiPoint') {
 
8685                         return this.toMultiPoint(precision);
 
8688                 var isGeometryCollection = type === 'GeometryCollection',
 
8691                 this.eachLayer(function (layer) {
 
8692                         if (layer.toGeoJSON) {
 
8693                                 var json = layer.toGeoJSON(precision);
 
8694                                 if (isGeometryCollection) {
 
8695                                         jsons.push(json.geometry);
 
8697                                         var feature = asFeature(json);
 
8698                                         // Squash nested feature collections
 
8699                                         if (feature.type === 'FeatureCollection') {
 
8700                                                 jsons.push.apply(jsons, feature.features);
 
8702                                                 jsons.push(feature);
 
8708                 if (isGeometryCollection) {
 
8709                         return getFeature(this, {
 
8711                                 type: 'GeometryCollection'
 
8716                         type: 'FeatureCollection',
 
8722 // @namespace GeoJSON
 
8723 // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
 
8724 // Creates a GeoJSON layer. Optionally accepts an object in
 
8725 // [GeoJSON format](http://geojson.org/geojson-spec.html) to display on the map
 
8726 // (you can alternatively add it later with `addData` method) and an `options` object.
 
8727 function geoJSON(geojson, options) {
 
8728         return new GeoJSON(geojson, options);
 
8731 // Backward compatibility.
 
8732 var geoJson = geoJSON;
 
8735  * @class ImageOverlay
 
8736  * @aka L.ImageOverlay
 
8737  * @inherits Interactive layer
 
8739  * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
 
8744  * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
 
8745  *      imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
 
8746  * L.imageOverlay(imageUrl, imageBounds).addTo(map);
 
8750 var ImageOverlay = Layer.extend({
 
8753         // @aka ImageOverlay options
 
8755                 // @option opacity: Number = 1.0
 
8756                 // The opacity of the image overlay.
 
8759                 // @option alt: String = ''
 
8760                 // Text for the `alt` attribute of the image (useful for accessibility).
 
8763                 // @option interactive: Boolean = false
 
8764                 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
 
8767                 // @option crossOrigin: Boolean = false
 
8768                 // If true, the image will have its crossOrigin attribute set to ''. This is needed if you want to access image pixel data.
 
8771                 // @option errorOverlayUrl: String = ''
 
8772                 // URL to the overlay image to show in place of the overlay that failed to load.
 
8773                 errorOverlayUrl: '',
 
8775                 // @option zIndex: Number = 1
 
8776                 // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the tile layer.
 
8779                 // @option className: String = ''
 
8780                 // A custom class name to assign to the image. Empty by default.
 
8784         initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
 
8786                 this._bounds = toLatLngBounds(bounds);
 
8788                 setOptions(this, options);
 
8791         onAdd: function () {
 
8795                         if (this.options.opacity < 1) {
 
8796                                 this._updateOpacity();
 
8800                 if (this.options.interactive) {
 
8801                         addClass(this._image, 'leaflet-interactive');
 
8802                         this.addInteractiveTarget(this._image);
 
8805                 this.getPane().appendChild(this._image);
 
8809         onRemove: function () {
 
8810                 remove(this._image);
 
8811                 if (this.options.interactive) {
 
8812                         this.removeInteractiveTarget(this._image);
 
8816         // @method setOpacity(opacity: Number): this
 
8817         // Sets the opacity of the overlay.
 
8818         setOpacity: function (opacity) {
 
8819                 this.options.opacity = opacity;
 
8822                         this._updateOpacity();
 
8827         setStyle: function (styleOpts) {
 
8828                 if (styleOpts.opacity) {
 
8829                         this.setOpacity(styleOpts.opacity);
 
8834         // @method bringToFront(): this
 
8835         // Brings the layer to the top of all overlays.
 
8836         bringToFront: function () {
 
8838                         toFront(this._image);
 
8843         // @method bringToBack(): this
 
8844         // Brings the layer to the bottom of all overlays.
 
8845         bringToBack: function () {
 
8847                         toBack(this._image);
 
8852         // @method setUrl(url: String): this
 
8853         // Changes the URL of the image.
 
8854         setUrl: function (url) {
 
8858                         this._image.src = url;
 
8863         // @method setBounds(bounds: LatLngBounds): this
 
8864         // Update the bounds that this ImageOverlay covers
 
8865         setBounds: function (bounds) {
 
8866                 this._bounds = toLatLngBounds(bounds);
 
8874         getEvents: function () {
 
8877                         viewreset: this._reset
 
8880                 if (this._zoomAnimated) {
 
8881                         events.zoomanim = this._animateZoom;
 
8887         // @method: setZIndex(value: Number) : this
 
8888         // Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
 
8889         setZIndex: function (value) {
 
8890                 this.options.zIndex = value;
 
8891                 this._updateZIndex();
 
8895         // @method getBounds(): LatLngBounds
 
8896         // Get the bounds that this ImageOverlay covers
 
8897         getBounds: function () {
 
8898                 return this._bounds;
 
8901         // @method getElement(): HTMLElement
 
8902         // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
 
8903         // used by this overlay.
 
8904         getElement: function () {
 
8908         _initImage: function () {
 
8909                 var img = this._image = create$1('img',
 
8910                                 'leaflet-image-layer ' + (this._zoomAnimated ? 'leaflet-zoom-animated' : '') +
 
8911                                  (this.options.className || ''));
 
8913                 img.onselectstart = falseFn;
 
8914                 img.onmousemove = falseFn;
 
8916                 // @event load: Event
 
8917                 // Fired when the ImageOverlay layer has loaded its image
 
8918                 img.onload = bind(this.fire, this, 'load');
 
8919                 img.onerror = bind(this._overlayOnError, this, 'error');
 
8921                 if (this.options.crossOrigin) {
 
8922                         img.crossOrigin = '';
 
8925                 if (this.options.zIndex) {
 
8926                         this._updateZIndex();
 
8929                 img.src = this._url;
 
8930                 img.alt = this.options.alt;
 
8933         _animateZoom: function (e) {
 
8934                 var scale = this._map.getZoomScale(e.zoom),
 
8935                     offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
 
8937                 setTransform(this._image, offset, scale);
 
8940         _reset: function () {
 
8941                 var image = this._image,
 
8942                     bounds = new Bounds(
 
8943                         this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
 
8944                         this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
 
8945                     size = bounds.getSize();
 
8947                 setPosition(image, bounds.min);
 
8949                 image.style.width  = size.x + 'px';
 
8950                 image.style.height = size.y + 'px';
 
8953         _updateOpacity: function () {
 
8954                 setOpacity(this._image, this.options.opacity);
 
8957         _updateZIndex: function () {
 
8958                 if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
 
8959                         this._image.style.zIndex = this.options.zIndex;
 
8963         _overlayOnError: function () {
 
8964                 // @event error: Event
 
8965                 // Fired when the ImageOverlay layer has loaded its image
 
8968                 var errorUrl = this.options.errorOverlayUrl;
 
8969                 if (errorUrl && this._url !== errorUrl) {
 
8970                         this._url = errorUrl;
 
8971                         this._image.src = errorUrl;
 
8976 // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
 
8977 // Instantiates an image overlay object given the URL of the image and the
 
8978 // geographical bounds it is tied to.
 
8979 var imageOverlay = function (url, bounds, options) {
 
8980         return new ImageOverlay(url, bounds, options);
 
8984  * @class VideoOverlay
 
8985  * @aka L.VideoOverlay
 
8986  * @inherits ImageOverlay
 
8988  * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
 
8990  * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
 
8996  * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
 
8997  *      videoBounds = [[ 32, -130], [ 13, -100]];
 
8998  * L.VideoOverlay(videoUrl, videoBounds ).addTo(map);
 
9002 var VideoOverlay = ImageOverlay.extend({
 
9005         // @aka VideoOverlay options
 
9007                 // @option autoplay: Boolean = true
 
9008                 // Whether the video starts playing automatically when loaded.
 
9011                 // @option loop: Boolean = true
 
9012                 // Whether the video will loop back to the beginning when played.
 
9016         _initImage: function () {
 
9017                 var wasElementSupplied = this._url.tagName === 'VIDEO';
 
9018                 var vid = this._image = wasElementSupplied ? this._url : create$1('video');
 
9020                 vid.class = vid.class || '';
 
9021                 vid.class += 'leaflet-image-layer ' + (this._zoomAnimated ? 'leaflet-zoom-animated' : '');
 
9023                 vid.onselectstart = falseFn;
 
9024                 vid.onmousemove = falseFn;
 
9026                 // @event load: Event
 
9027                 // Fired when the video has finished loading the first frame
 
9028                 vid.onloadeddata = bind(this.fire, this, 'load');
 
9030                 if (wasElementSupplied) { return; }
 
9032                 if (!isArray(this._url)) { this._url = [this._url]; }
 
9034                 vid.autoplay = !!this.options.autoplay;
 
9035                 vid.loop = !!this.options.loop;
 
9036                 for (var i = 0; i < this._url.length; i++) {
 
9037                         var source = create$1('source');
 
9038                         source.src = this._url[i];
 
9039                         vid.appendChild(source);
 
9043         // @method getElement(): HTMLVideoElement
 
9044         // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
 
9045         // used by this overlay.
 
9049 // @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options)
 
9050 // Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the
 
9051 // geographical bounds it is tied to.
 
9053 function videoOverlay(video, bounds, options) {
 
9054         return new VideoOverlay(video, bounds, options);
 
9061  * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
 
9064 // @namespace DivOverlay
 
9065 var DivOverlay = Layer.extend({
 
9068         // @aka DivOverlay options
 
9070                 // @option offset: Point = Point(0, 7)
 
9071                 // The offset of the popup position. Useful to control the anchor
 
9072                 // of the popup when opening it on some overlays.
 
9075                 // @option className: String = ''
 
9076                 // A custom CSS class name to assign to the popup.
 
9079                 // @option pane: String = 'popupPane'
 
9080                 // `Map pane` where the popup will be added.
 
9084         initialize: function (options, source) {
 
9085                 setOptions(this, options);
 
9087                 this._source = source;
 
9090         onAdd: function (map) {
 
9091                 this._zoomAnimated = map._zoomAnimated;
 
9093                 if (!this._container) {
 
9097                 if (map._fadeAnimated) {
 
9098                         setOpacity(this._container, 0);
 
9101                 clearTimeout(this._removeTimeout);
 
9102                 this.getPane().appendChild(this._container);
 
9105                 if (map._fadeAnimated) {
 
9106                         setOpacity(this._container, 1);
 
9109                 this.bringToFront();
 
9112         onRemove: function (map) {
 
9113                 if (map._fadeAnimated) {
 
9114                         setOpacity(this._container, 0);
 
9115                         this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200);
 
9117                         remove(this._container);
 
9122         // @method getLatLng: LatLng
 
9123         // Returns the geographical point of popup.
 
9124         getLatLng: function () {
 
9125                 return this._latlng;
 
9128         // @method setLatLng(latlng: LatLng): this
 
9129         // Sets the geographical point where the popup will open.
 
9130         setLatLng: function (latlng) {
 
9131                 this._latlng = toLatLng(latlng);
 
9133                         this._updatePosition();
 
9139         // @method getContent: String|HTMLElement
 
9140         // Returns the content of the popup.
 
9141         getContent: function () {
 
9142                 return this._content;
 
9145         // @method setContent(htmlContent: String|HTMLElement|Function): this
 
9146         // 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.
 
9147         setContent: function (content) {
 
9148                 this._content = content;
 
9153         // @method getElement: String|HTMLElement
 
9154         // Alias for [getContent()](#popup-getcontent)
 
9155         getElement: function () {
 
9156                 return this._container;
 
9159         // @method update: null
 
9160         // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
 
9161         update: function () {
 
9162                 if (!this._map) { return; }
 
9164                 this._container.style.visibility = 'hidden';
 
9166                 this._updateContent();
 
9167                 this._updateLayout();
 
9168                 this._updatePosition();
 
9170                 this._container.style.visibility = '';
 
9175         getEvents: function () {
 
9177                         zoom: this._updatePosition,
 
9178                         viewreset: this._updatePosition
 
9181                 if (this._zoomAnimated) {
 
9182                         events.zoomanim = this._animateZoom;
 
9187         // @method isOpen: Boolean
 
9188         // Returns `true` when the popup is visible on the map.
 
9189         isOpen: function () {
 
9190                 return !!this._map && this._map.hasLayer(this);
 
9193         // @method bringToFront: this
 
9194         // Brings this popup in front of other popups (in the same map pane).
 
9195         bringToFront: function () {
 
9197                         toFront(this._container);
 
9202         // @method bringToBack: this
 
9203         // Brings this popup to the back of other popups (in the same map pane).
 
9204         bringToBack: function () {
 
9206                         toBack(this._container);
 
9211         _updateContent: function () {
 
9212                 if (!this._content) { return; }
 
9214                 var node = this._contentNode;
 
9215                 var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
 
9217                 if (typeof content === 'string') {
 
9218                         node.innerHTML = content;
 
9220                         while (node.hasChildNodes()) {
 
9221                                 node.removeChild(node.firstChild);
 
9223                         node.appendChild(content);
 
9225                 this.fire('contentupdate');
 
9228         _updatePosition: function () {
 
9229                 if (!this._map) { return; }
 
9231                 var pos = this._map.latLngToLayerPoint(this._latlng),
 
9232                     offset = toPoint(this.options.offset),
 
9233                     anchor = this._getAnchor();
 
9235                 if (this._zoomAnimated) {
 
9236                         setPosition(this._container, pos.add(anchor));
 
9238                         offset = offset.add(pos).add(anchor);
 
9241                 var bottom = this._containerBottom = -offset.y,
 
9242                     left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
 
9244                 // bottom position the popup in case the height of the popup changes (images loading etc)
 
9245                 this._container.style.bottom = bottom + 'px';
 
9246                 this._container.style.left = left + 'px';
 
9249         _getAnchor: function () {
 
9257  * @inherits DivOverlay
 
9259  * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
 
9260  * open popups while making sure that only one popup is open at one time
 
9261  * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
 
9265  * If you want to just bind a popup to marker click and then open it, it's really easy:
 
9268  * marker.bindPopup(popupContent).openPopup();
 
9270  * Path overlays like polylines also have a `bindPopup` method.
 
9271  * Here's a more complicated way to open a popup on a map:
 
9274  * var popup = L.popup()
 
9275  *      .setLatLng(latlng)
 
9276  *      .setContent('<p>Hello world!<br />This is a nice popup.</p>')
 
9283 var Popup = DivOverlay.extend({
 
9286         // @aka Popup options
 
9288                 // @option maxWidth: Number = 300
 
9289                 // Max width of the popup, in pixels.
 
9292                 // @option minWidth: Number = 50
 
9293                 // Min width of the popup, in pixels.
 
9296                 // @option maxHeight: Number = null
 
9297                 // If set, creates a scrollable container of the given height
 
9298                 // inside a popup if its content exceeds it.
 
9301                 // @option autoPan: Boolean = true
 
9302                 // Set it to `false` if you don't want the map to do panning animation
 
9303                 // to fit the opened popup.
 
9306                 // @option autoPanPaddingTopLeft: Point = null
 
9307                 // The margin between the popup and the top left corner of the map
 
9308                 // view after autopanning was performed.
 
9309                 autoPanPaddingTopLeft: null,
 
9311                 // @option autoPanPaddingBottomRight: Point = null
 
9312                 // The margin between the popup and the bottom right corner of the map
 
9313                 // view after autopanning was performed.
 
9314                 autoPanPaddingBottomRight: null,
 
9316                 // @option autoPanPadding: Point = Point(5, 5)
 
9317                 // Equivalent of setting both top left and bottom right autopan padding to the same value.
 
9318                 autoPanPadding: [5, 5],
 
9320                 // @option keepInView: Boolean = false
 
9321                 // Set it to `true` if you want to prevent users from panning the popup
 
9322                 // off of the screen while it is open.
 
9325                 // @option closeButton: Boolean = true
 
9326                 // Controls the presence of a close button in the popup.
 
9329                 // @option autoClose: Boolean = true
 
9330                 // Set it to `false` if you want to override the default behavior of
 
9331                 // the popup closing when another popup is opened.
 
9334                 // @option closeOnClick: Boolean = *
 
9335                 // Set it if you want to override the default behavior of the popup closing when user clicks
 
9336                 // on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
 
9338                 // @option className: String = ''
 
9339                 // A custom CSS class name to assign to the popup.
 
9344         // @method openOn(map: Map): this
 
9345         // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
 
9346         openOn: function (map) {
 
9347                 map.openPopup(this);
 
9351         onAdd: function (map) {
 
9352                 DivOverlay.prototype.onAdd.call(this, map);
 
9355                 // @section Popup events
 
9356                 // @event popupopen: PopupEvent
 
9357                 // Fired when a popup is opened in the map
 
9358                 map.fire('popupopen', {popup: this});
 
9362                         // @section Popup events
 
9363                         // @event popupopen: PopupEvent
 
9364                         // Fired when a popup bound to this layer is opened
 
9365                         this._source.fire('popupopen', {popup: this}, true);
 
9366                         // For non-path layers, we toggle the popup when clicking
 
9367                         // again the layer, so prevent the map to reopen it.
 
9368                         if (!(this._source instanceof Path)) {
 
9369                                 this._source.on('preclick', stopPropagation);
 
9374         onRemove: function (map) {
 
9375                 DivOverlay.prototype.onRemove.call(this, map);
 
9378                 // @section Popup events
 
9379                 // @event popupclose: PopupEvent
 
9380                 // Fired when a popup in the map is closed
 
9381                 map.fire('popupclose', {popup: this});
 
9385                         // @section Popup events
 
9386                         // @event popupclose: PopupEvent
 
9387                         // Fired when a popup bound to this layer is closed
 
9388                         this._source.fire('popupclose', {popup: this}, true);
 
9389                         if (!(this._source instanceof Path)) {
 
9390                                 this._source.off('preclick', stopPropagation);
 
9395         getEvents: function () {
 
9396                 var events = DivOverlay.prototype.getEvents.call(this);
 
9398                 if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
 
9399                         events.preclick = this._close;
 
9402                 if (this.options.keepInView) {
 
9403                         events.moveend = this._adjustPan;
 
9409         _close: function () {
 
9411                         this._map.closePopup(this);
 
9415         _initLayout: function () {
 
9416                 var prefix = 'leaflet-popup',
 
9417                     container = this._container = create$1('div',
 
9418                         prefix + ' ' + (this.options.className || '') +
 
9419                         ' leaflet-zoom-animated');
 
9421                 var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
 
9422                 this._contentNode = create$1('div', prefix + '-content', wrapper);
 
9424                 disableClickPropagation(wrapper);
 
9425                 disableScrollPropagation(this._contentNode);
 
9426                 on(wrapper, 'contextmenu', stopPropagation);
 
9428                 this._tipContainer = create$1('div', prefix + '-tip-container', container);
 
9429                 this._tip = create$1('div', prefix + '-tip', this._tipContainer);
 
9431                 if (this.options.closeButton) {
 
9432                         var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
 
9433                         closeButton.href = '#close';
 
9434                         closeButton.innerHTML = '×';
 
9436                         on(closeButton, 'click', this._onCloseButtonClick, this);
 
9440         _updateLayout: function () {
 
9441                 var container = this._contentNode,
 
9442                     style = container.style;
 
9445                 style.whiteSpace = 'nowrap';
 
9447                 var width = container.offsetWidth;
 
9448                 width = Math.min(width, this.options.maxWidth);
 
9449                 width = Math.max(width, this.options.minWidth);
 
9451                 style.width = (width + 1) + 'px';
 
9452                 style.whiteSpace = '';
 
9456                 var height = container.offsetHeight,
 
9457                     maxHeight = this.options.maxHeight,
 
9458                     scrolledClass = 'leaflet-popup-scrolled';
 
9460                 if (maxHeight && height > maxHeight) {
 
9461                         style.height = maxHeight + 'px';
 
9462                         addClass(container, scrolledClass);
 
9464                         removeClass(container, scrolledClass);
 
9467                 this._containerWidth = this._container.offsetWidth;
 
9470         _animateZoom: function (e) {
 
9471                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
 
9472                     anchor = this._getAnchor();
 
9473                 setPosition(this._container, pos.add(anchor));
 
9476         _adjustPan: function () {
 
9477                 if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; }
 
9479                 var map = this._map,
 
9480                     marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
 
9481                     containerHeight = this._container.offsetHeight + marginBottom,
 
9482                     containerWidth = this._containerWidth,
 
9483                     layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
 
9485                 layerPos._add(getPosition(this._container));
 
9487                 var containerPos = map.layerPointToContainerPoint(layerPos),
 
9488                     padding = toPoint(this.options.autoPanPadding),
 
9489                     paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
 
9490                     paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
 
9491                     size = map.getSize(),
 
9495                 if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
 
9496                         dx = containerPos.x + containerWidth - size.x + paddingBR.x;
 
9498                 if (containerPos.x - dx - paddingTL.x < 0) { // left
 
9499                         dx = containerPos.x - paddingTL.x;
 
9501                 if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
 
9502                         dy = containerPos.y + containerHeight - size.y + paddingBR.y;
 
9504                 if (containerPos.y - dy - paddingTL.y < 0) { // top
 
9505                         dy = containerPos.y - paddingTL.y;
 
9509                 // @section Popup events
 
9510                 // @event autopanstart: Event
 
9511                 // Fired when the map starts autopanning when opening a popup.
 
9514                             .fire('autopanstart')
 
9519         _onCloseButtonClick: function (e) {
 
9524         _getAnchor: function () {
 
9525                 // Where should we anchor the popup on the source layer?
 
9526                 return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
 
9532 // @factory L.popup(options?: Popup options, source?: Layer)
 
9533 // 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.
 
9534 var popup = function (options, source) {
 
9535         return new Popup(options, source);
 
9540  * @section Interaction Options
 
9541  * @option closePopupOnClick: Boolean = true
 
9542  * Set it to `false` if you don't want popups to close when user clicks the map.
 
9545         closePopupOnClick: true
 
9550 // @section Methods for Layers and Controls
 
9552         // @method openPopup(popup: Popup): this
 
9553         // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
 
9555         // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
 
9556         // Creates a popup with the specified content and options and opens it in the given point on a map.
 
9557         openPopup: function (popup, latlng, options) {
 
9558                 if (!(popup instanceof Popup)) {
 
9559                         popup = new Popup(options).setContent(popup);
 
9563                         popup.setLatLng(latlng);
 
9566                 if (this.hasLayer(popup)) {
 
9570                 if (this._popup && this._popup.options.autoClose) {
 
9574                 this._popup = popup;
 
9575                 return this.addLayer(popup);
 
9578         // @method closePopup(popup?: Popup): this
 
9579         // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
 
9580         closePopup: function (popup) {
 
9581                 if (!popup || popup === this._popup) {
 
9582                         popup = this._popup;
 
9586                         this.removeLayer(popup);
 
9594  * @section Popup methods example
 
9596  * All layers share a set of methods convenient for binding popups to it.
 
9599  * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
 
9600  * layer.openPopup();
 
9601  * layer.closePopup();
 
9604  * 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.
 
9607 // @section Popup methods
 
9610         // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
 
9611         // Binds a popup to the layer with the passed `content` and sets up the
 
9612         // necessary event listeners. If a `Function` is passed it will receive
 
9613         // the layer as the first argument and should return a `String` or `HTMLElement`.
 
9614         bindPopup: function (content, options) {
 
9616                 if (content instanceof Popup) {
 
9617                         setOptions(content, options);
 
9618                         this._popup = content;
 
9619                         content._source = this;
 
9621                         if (!this._popup || options) {
 
9622                                 this._popup = new Popup(options, this);
 
9624                         this._popup.setContent(content);
 
9627                 if (!this._popupHandlersAdded) {
 
9629                                 click: this._openPopup,
 
9630                                 keypress: this._onKeyPress,
 
9631                                 remove: this.closePopup,
 
9632                                 move: this._movePopup
 
9634                         this._popupHandlersAdded = true;
 
9640         // @method unbindPopup(): this
 
9641         // Removes the popup previously bound with `bindPopup`.
 
9642         unbindPopup: function () {
 
9645                                 click: this._openPopup,
 
9646                                 keypress: this._onKeyPress,
 
9647                                 remove: this.closePopup,
 
9648                                 move: this._movePopup
 
9650                         this._popupHandlersAdded = false;
 
9656         // @method openPopup(latlng?: LatLng): this
 
9657         // Opens the bound popup at the specificed `latlng` or at the default popup anchor if no `latlng` is passed.
 
9658         openPopup: function (layer, latlng) {
 
9659                 if (!(layer instanceof Layer)) {
 
9664                 if (layer instanceof FeatureGroup) {
 
9665                         for (var id in this._layers) {
 
9666                                 layer = this._layers[id];
 
9672                         latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
 
9675                 if (this._popup && this._map) {
 
9676                         // set popup source to this layer
 
9677                         this._popup._source = layer;
 
9679                         // update the popup (content, layout, ect...)
 
9680                         this._popup.update();
 
9682                         // open the popup on the map
 
9683                         this._map.openPopup(this._popup, latlng);
 
9689         // @method closePopup(): this
 
9690         // Closes the popup bound to this layer if it is open.
 
9691         closePopup: function () {
 
9693                         this._popup._close();
 
9698         // @method togglePopup(): this
 
9699         // Opens or closes the popup bound to this layer depending on its current state.
 
9700         togglePopup: function (target) {
 
9702                         if (this._popup._map) {
 
9705                                 this.openPopup(target);
 
9711         // @method isPopupOpen(): boolean
 
9712         // Returns `true` if the popup bound to this layer is currently open.
 
9713         isPopupOpen: function () {
 
9714                 return (this._popup ? this._popup.isOpen() : false);
 
9717         // @method setPopupContent(content: String|HTMLElement|Popup): this
 
9718         // Sets the content of the popup bound to this layer.
 
9719         setPopupContent: function (content) {
 
9721                         this._popup.setContent(content);
 
9726         // @method getPopup(): Popup
 
9727         // Returns the popup bound to this layer.
 
9728         getPopup: function () {
 
9732         _openPopup: function (e) {
 
9733                 var layer = e.layer || e.target;
 
9743                 // prevent map click
 
9746                 // if this inherits from Path its a vector and we can just
 
9747                 // open the popup at the new location
 
9748                 if (layer instanceof Path) {
 
9749                         this.openPopup(e.layer || e.target, e.latlng);
 
9753                 // otherwise treat it like a marker and figure out
 
9754                 // if we should toggle it open/closed
 
9755                 if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
 
9758                         this.openPopup(layer, e.latlng);
 
9762         _movePopup: function (e) {
 
9763                 this._popup.setLatLng(e.latlng);
 
9766         _onKeyPress: function (e) {
 
9767                 if (e.originalEvent.keyCode === 13) {
 
9775  * @inherits DivOverlay
 
9777  * Used to display small texts on top of map layers.
 
9782  * marker.bindTooltip("my tooltip text").openTooltip();
 
9784  * Note about tooltip offset. Leaflet takes two options in consideration
 
9785  * for computing tooltip offseting:
 
9786  * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
 
9787  *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
 
9788  *   move it to the bottom. Negatives will move to the left and top.
 
9789  * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
 
9790  *   should adapt this value if you use a custom icon.
 
9794 // @namespace Tooltip
 
9795 var Tooltip = DivOverlay.extend({
 
9798         // @aka Tooltip options
 
9800                 // @option pane: String = 'tooltipPane'
 
9801                 // `Map pane` where the tooltip will be added.
 
9802                 pane: 'tooltipPane',
 
9804                 // @option offset: Point = Point(0, 0)
 
9805                 // Optional offset of the tooltip position.
 
9808                 // @option direction: String = 'auto'
 
9809                 // Direction where to open the tooltip. Possible values are: `right`, `left`,
 
9810                 // `top`, `bottom`, `center`, `auto`.
 
9811                 // `auto` will dynamicaly switch between `right` and `left` according to the tooltip
 
9812                 // position on the map.
 
9815                 // @option permanent: Boolean = false
 
9816                 // Whether to open the tooltip permanently or only on mouseover.
 
9819                 // @option sticky: Boolean = false
 
9820                 // If true, the tooltip will follow the mouse instead of being fixed at the feature center.
 
9823                 // @option interactive: Boolean = false
 
9824                 // If true, the tooltip will listen to the feature events.
 
9827                 // @option opacity: Number = 0.9
 
9828                 // Tooltip container opacity.
 
9832         onAdd: function (map) {
 
9833                 DivOverlay.prototype.onAdd.call(this, map);
 
9834                 this.setOpacity(this.options.opacity);
 
9837                 // @section Tooltip events
 
9838                 // @event tooltipopen: TooltipEvent
 
9839                 // Fired when a tooltip is opened in the map.
 
9840                 map.fire('tooltipopen', {tooltip: this});
 
9844                         // @section Tooltip events
 
9845                         // @event tooltipopen: TooltipEvent
 
9846                         // Fired when a tooltip bound to this layer is opened.
 
9847                         this._source.fire('tooltipopen', {tooltip: this}, true);
 
9851         onRemove: function (map) {
 
9852                 DivOverlay.prototype.onRemove.call(this, map);
 
9855                 // @section Tooltip events
 
9856                 // @event tooltipclose: TooltipEvent
 
9857                 // Fired when a tooltip in the map is closed.
 
9858                 map.fire('tooltipclose', {tooltip: this});
 
9862                         // @section Tooltip events
 
9863                         // @event tooltipclose: TooltipEvent
 
9864                         // Fired when a tooltip bound to this layer is closed.
 
9865                         this._source.fire('tooltipclose', {tooltip: this}, true);
 
9869         getEvents: function () {
 
9870                 var events = DivOverlay.prototype.getEvents.call(this);
 
9872                 if (touch && !this.options.permanent) {
 
9873                         events.preclick = this._close;
 
9879         _close: function () {
 
9881                         this._map.closeTooltip(this);
 
9885         _initLayout: function () {
 
9886                 var prefix = 'leaflet-tooltip',
 
9887                     className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
 
9889                 this._contentNode = this._container = create$1('div', className);
 
9892         _updateLayout: function () {},
 
9894         _adjustPan: function () {},
 
9896         _setPosition: function (pos) {
 
9897                 var map = this._map,
 
9898                     container = this._container,
 
9899                     centerPoint = map.latLngToContainerPoint(map.getCenter()),
 
9900                     tooltipPoint = map.layerPointToContainerPoint(pos),
 
9901                     direction = this.options.direction,
 
9902                     tooltipWidth = container.offsetWidth,
 
9903                     tooltipHeight = container.offsetHeight,
 
9904                     offset = toPoint(this.options.offset),
 
9905                     anchor = this._getAnchor();
 
9907                 if (direction === 'top') {
 
9908                         pos = pos.add(toPoint(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true));
 
9909                 } else if (direction === 'bottom') {
 
9910                         pos = pos.subtract(toPoint(tooltipWidth / 2 - offset.x, -offset.y, true));
 
9911                 } else if (direction === 'center') {
 
9912                         pos = pos.subtract(toPoint(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true));
 
9913                 } else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
 
9914                         direction = 'right';
 
9915                         pos = pos.add(toPoint(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true));
 
9918                         pos = pos.subtract(toPoint(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true));
 
9921                 removeClass(container, 'leaflet-tooltip-right');
 
9922                 removeClass(container, 'leaflet-tooltip-left');
 
9923                 removeClass(container, 'leaflet-tooltip-top');
 
9924                 removeClass(container, 'leaflet-tooltip-bottom');
 
9925                 addClass(container, 'leaflet-tooltip-' + direction);
 
9926                 setPosition(container, pos);
 
9929         _updatePosition: function () {
 
9930                 var pos = this._map.latLngToLayerPoint(this._latlng);
 
9931                 this._setPosition(pos);
 
9934         setOpacity: function (opacity) {
 
9935                 this.options.opacity = opacity;
 
9937                 if (this._container) {
 
9938                         setOpacity(this._container, opacity);
 
9942         _animateZoom: function (e) {
 
9943                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
 
9944                 this._setPosition(pos);
 
9947         _getAnchor: function () {
 
9948                 // Where should we anchor the tooltip on the source layer?
 
9949                 return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
 
9954 // @namespace Tooltip
 
9955 // @factory L.tooltip(options?: Tooltip options, source?: Layer)
 
9956 // 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.
 
9957 var tooltip = function (options, source) {
 
9958         return new Tooltip(options, source);
 
9962 // @section Methods for Layers and Controls
 
9965         // @method openTooltip(tooltip: Tooltip): this
 
9966         // Opens the specified tooltip.
 
9968         // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
 
9969         // Creates a tooltip with the specified content and options and open it.
 
9970         openTooltip: function (tooltip, latlng, options) {
 
9971                 if (!(tooltip instanceof Tooltip)) {
 
9972                         tooltip = new Tooltip(options).setContent(tooltip);
 
9976                         tooltip.setLatLng(latlng);
 
9979                 if (this.hasLayer(tooltip)) {
 
9983                 return this.addLayer(tooltip);
 
9986         // @method closeTooltip(tooltip?: Tooltip): this
 
9987         // Closes the tooltip given as parameter.
 
9988         closeTooltip: function (tooltip) {
 
9990                         this.removeLayer(tooltip);
 
9999  * @section Tooltip methods example
 
10001  * All layers share a set of methods convenient for binding tooltips to it.
 
10004  * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
 
10005  * layer.openTooltip();
 
10006  * layer.closeTooltip();
 
10010 // @section Tooltip methods
 
10013         // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
 
10014         // Binds a tooltip to the layer with the passed `content` and sets up the
 
10015         // necessary event listeners. If a `Function` is passed it will receive
 
10016         // the layer as the first argument and should return a `String` or `HTMLElement`.
 
10017         bindTooltip: function (content, options) {
 
10019                 if (content instanceof Tooltip) {
 
10020                         setOptions(content, options);
 
10021                         this._tooltip = content;
 
10022                         content._source = this;
 
10024                         if (!this._tooltip || options) {
 
10025                                 this._tooltip = new Tooltip(options, this);
 
10027                         this._tooltip.setContent(content);
 
10031                 this._initTooltipInteractions();
 
10033                 if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
 
10034                         this.openTooltip();
 
10040         // @method unbindTooltip(): this
 
10041         // Removes the tooltip previously bound with `bindTooltip`.
 
10042         unbindTooltip: function () {
 
10043                 if (this._tooltip) {
 
10044                         this._initTooltipInteractions(true);
 
10045                         this.closeTooltip();
 
10046                         this._tooltip = null;
 
10051         _initTooltipInteractions: function (remove$$1) {
 
10052                 if (!remove$$1 && this._tooltipHandlersAdded) { return; }
 
10053                 var onOff = remove$$1 ? 'off' : 'on',
 
10055                         remove: this.closeTooltip,
 
10056                         move: this._moveTooltip
 
10058                 if (!this._tooltip.options.permanent) {
 
10059                         events.mouseover = this._openTooltip;
 
10060                         events.mouseout = this.closeTooltip;
 
10061                         if (this._tooltip.options.sticky) {
 
10062                                 events.mousemove = this._moveTooltip;
 
10065                                 events.click = this._openTooltip;
 
10068                         events.add = this._openTooltip;
 
10070                 this[onOff](events);
 
10071                 this._tooltipHandlersAdded = !remove$$1;
 
10074         // @method openTooltip(latlng?: LatLng): this
 
10075         // Opens the bound tooltip at the specificed `latlng` or at the default tooltip anchor if no `latlng` is passed.
 
10076         openTooltip: function (layer, latlng) {
 
10077                 if (!(layer instanceof Layer)) {
 
10082                 if (layer instanceof FeatureGroup) {
 
10083                         for (var id in this._layers) {
 
10084                                 layer = this._layers[id];
 
10090                         latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
 
10093                 if (this._tooltip && this._map) {
 
10095                         // set tooltip source to this layer
 
10096                         this._tooltip._source = layer;
 
10098                         // update the tooltip (content, layout, ect...)
 
10099                         this._tooltip.update();
 
10101                         // open the tooltip on the map
 
10102                         this._map.openTooltip(this._tooltip, latlng);
 
10104                         // Tooltip container may not be defined if not permanent and never
 
10106                         if (this._tooltip.options.interactive && this._tooltip._container) {
 
10107                                 addClass(this._tooltip._container, 'leaflet-clickable');
 
10108                                 this.addInteractiveTarget(this._tooltip._container);
 
10115         // @method closeTooltip(): this
 
10116         // Closes the tooltip bound to this layer if it is open.
 
10117         closeTooltip: function () {
 
10118                 if (this._tooltip) {
 
10119                         this._tooltip._close();
 
10120                         if (this._tooltip.options.interactive && this._tooltip._container) {
 
10121                                 removeClass(this._tooltip._container, 'leaflet-clickable');
 
10122                                 this.removeInteractiveTarget(this._tooltip._container);
 
10128         // @method toggleTooltip(): this
 
10129         // Opens or closes the tooltip bound to this layer depending on its current state.
 
10130         toggleTooltip: function (target) {
 
10131                 if (this._tooltip) {
 
10132                         if (this._tooltip._map) {
 
10133                                 this.closeTooltip();
 
10135                                 this.openTooltip(target);
 
10141         // @method isTooltipOpen(): boolean
 
10142         // Returns `true` if the tooltip bound to this layer is currently open.
 
10143         isTooltipOpen: function () {
 
10144                 return this._tooltip.isOpen();
 
10147         // @method setTooltipContent(content: String|HTMLElement|Tooltip): this
 
10148         // Sets the content of the tooltip bound to this layer.
 
10149         setTooltipContent: function (content) {
 
10150                 if (this._tooltip) {
 
10151                         this._tooltip.setContent(content);
 
10156         // @method getTooltip(): Tooltip
 
10157         // Returns the tooltip bound to this layer.
 
10158         getTooltip: function () {
 
10159                 return this._tooltip;
 
10162         _openTooltip: function (e) {
 
10163                 var layer = e.layer || e.target;
 
10165                 if (!this._tooltip || !this._map) {
 
10168                 this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
 
10171         _moveTooltip: function (e) {
 
10172                 var latlng = e.latlng, containerPoint, layerPoint;
 
10173                 if (this._tooltip.options.sticky && e.originalEvent) {
 
10174                         containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
 
10175                         layerPoint = this._map.containerPointToLayerPoint(containerPoint);
 
10176                         latlng = this._map.layerPointToLatLng(layerPoint);
 
10178                 this._tooltip.setLatLng(latlng);
 
10187  * Represents a lightweight icon for markers that uses a simple `<div>`
 
10188  * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
 
10192  * var myIcon = L.divIcon({className: 'my-div-icon'});
 
10193  * // you can set .my-div-icon styles in CSS
 
10195  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
 
10198  * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
 
10201 var DivIcon = Icon.extend({
 
10204                 // @aka DivIcon options
 
10205                 iconSize: [12, 12], // also can be set through CSS
 
10207                 // iconAnchor: (Point),
 
10208                 // popupAnchor: (Point),
 
10210                 // @option html: String = ''
 
10211                 // Custom HTML code to put inside the div element, empty by default.
 
10214                 // @option bgPos: Point = [0, 0]
 
10215                 // Optional relative position of the background, in pixels
 
10218                 className: 'leaflet-div-icon'
 
10221         createIcon: function (oldIcon) {
 
10222                 var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
 
10223                     options = this.options;
 
10225                 div.innerHTML = options.html !== false ? options.html : '';
 
10227                 if (options.bgPos) {
 
10228                         var bgPos = toPoint(options.bgPos);
 
10229                         div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
 
10231                 this._setIconStyles(div, 'icon');
 
10236         createShadow: function () {
 
10241 // @factory L.divIcon(options: DivIcon options)
 
10242 // Creates a `DivIcon` instance with the given options.
 
10243 function divIcon(options) {
 
10244         return new DivIcon(options);
 
10247 Icon.Default = IconDefault;
 
10254  * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
 
10255  * 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.
 
10258  * @section Synchronous usage
 
10261  * 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.
 
10264  * var CanvasLayer = L.GridLayer.extend({
 
10265  *     createTile: function(coords){
 
10266  *         // create a <canvas> element for drawing
 
10267  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
10269  *         // setup tile width and height according to the options
 
10270  *         var size = this.getTileSize();
 
10271  *         tile.width = size.x;
 
10272  *         tile.height = size.y;
 
10274  *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
 
10275  *         var ctx = tile.getContext('2d');
 
10277  *         // return the tile so it can be rendered on screen
 
10283  * @section Asynchronous usage
 
10286  * 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.
 
10289  * var CanvasLayer = L.GridLayer.extend({
 
10290  *     createTile: function(coords, done){
 
10293  *         // create a <canvas> element for drawing
 
10294  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
10296  *         // setup tile width and height according to the options
 
10297  *         var size = this.getTileSize();
 
10298  *         tile.width = size.x;
 
10299  *         tile.height = size.y;
 
10301  *         // draw something asynchronously and pass the tile to the done() callback
 
10302  *         setTimeout(function() {
 
10303  *             done(error, tile);
 
10315 var GridLayer = Layer.extend({
 
10318         // @aka GridLayer options
 
10320                 // @option tileSize: Number|Point = 256
 
10321                 // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
 
10324                 // @option opacity: Number = 1.0
 
10325                 // Opacity of the tiles. Can be used in the `createTile()` function.
 
10328                 // @option updateWhenIdle: Boolean = (depends)
 
10329                 // Load new tiles only when panning ends.
 
10330                 // `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation.
 
10331                 // `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the
 
10332                 // [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers.
 
10333                 updateWhenIdle: mobile,
 
10335                 // @option updateWhenZooming: Boolean = true
 
10336                 // 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.
 
10337                 updateWhenZooming: true,
 
10339                 // @option updateInterval: Number = 200
 
10340                 // Tiles will not update more than once every `updateInterval` milliseconds when panning.
 
10341                 updateInterval: 200,
 
10343                 // @option zIndex: Number = 1
 
10344                 // The explicit zIndex of the tile layer.
 
10347                 // @option bounds: LatLngBounds = undefined
 
10348                 // If set, tiles will only be loaded inside the set `LatLngBounds`.
 
10351                 // @option minZoom: Number = 0
 
10352                 // The minimum zoom level down to which this layer will be displayed (inclusive).
 
10355                 // @option maxZoom: Number = undefined
 
10356                 // The maximum zoom level up to which this layer will be displayed (inclusive).
 
10357                 maxZoom: undefined,
 
10359                 // @option maxNativeZoom: Number = undefined
 
10360                 // Maximum zoom number the tile source has available. If it is specified,
 
10361                 // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
 
10362                 // from `maxNativeZoom` level and auto-scaled.
 
10363                 maxNativeZoom: undefined,
 
10365                 // @option minNativeZoom: Number = undefined
 
10366                 // Minimum zoom number the tile source has available. If it is specified,
 
10367                 // the tiles on all zoom levels lower than `minNativeZoom` will be loaded
 
10368                 // from `minNativeZoom` level and auto-scaled.
 
10369                 minNativeZoom: undefined,
 
10371                 // @option noWrap: Boolean = false
 
10372                 // Whether the layer is wrapped around the antimeridian. If `true`, the
 
10373                 // GridLayer will only be displayed once at low zoom levels. Has no
 
10374                 // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
 
10375                 // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
 
10376                 // tiles outside the CRS limits.
 
10379                 // @option pane: String = 'tilePane'
 
10380                 // `Map pane` where the grid layer will be added.
 
10383                 // @option className: String = ''
 
10384                 // A custom class name to assign to the tile layer. Empty by default.
 
10387                 // @option keepBuffer: Number = 2
 
10388                 // When panning the map, keep this many rows and columns of tiles before unloading them.
 
10392         initialize: function (options) {
 
10393                 setOptions(this, options);
 
10396         onAdd: function () {
 
10397                 this._initContainer();
 
10406         beforeAdd: function (map) {
 
10407                 map._addZoomLimit(this);
 
10410         onRemove: function (map) {
 
10411                 this._removeAllTiles();
 
10412                 remove(this._container);
 
10413                 map._removeZoomLimit(this);
 
10414                 this._container = null;
 
10415                 this._tileZoom = null;
 
10418         // @method bringToFront: this
 
10419         // Brings the tile layer to the top of all tile layers.
 
10420         bringToFront: function () {
 
10422                         toFront(this._container);
 
10423                         this._setAutoZIndex(Math.max);
 
10428         // @method bringToBack: this
 
10429         // Brings the tile layer to the bottom of all tile layers.
 
10430         bringToBack: function () {
 
10432                         toBack(this._container);
 
10433                         this._setAutoZIndex(Math.min);
 
10438         // @method getContainer: HTMLElement
 
10439         // Returns the HTML element that contains the tiles for this layer.
 
10440         getContainer: function () {
 
10441                 return this._container;
 
10444         // @method setOpacity(opacity: Number): this
 
10445         // Changes the [opacity](#gridlayer-opacity) of the grid layer.
 
10446         setOpacity: function (opacity) {
 
10447                 this.options.opacity = opacity;
 
10448                 this._updateOpacity();
 
10452         // @method setZIndex(zIndex: Number): this
 
10453         // Changes the [zIndex](#gridlayer-zindex) of the grid layer.
 
10454         setZIndex: function (zIndex) {
 
10455                 this.options.zIndex = zIndex;
 
10456                 this._updateZIndex();
 
10461         // @method isLoading: Boolean
 
10462         // Returns `true` if any tile in the grid layer has not finished loading.
 
10463         isLoading: function () {
 
10464                 return this._loading;
 
10467         // @method redraw: this
 
10468         // Causes the layer to clear all the tiles and request them again.
 
10469         redraw: function () {
 
10471                         this._removeAllTiles();
 
10477         getEvents: function () {
 
10479                         viewprereset: this._invalidateAll,
 
10480                         viewreset: this._resetView,
 
10481                         zoom: this._resetView,
 
10482                         moveend: this._onMoveEnd
 
10485                 if (!this.options.updateWhenIdle) {
 
10486                         // update tiles on move, but not more often than once per given interval
 
10487                         if (!this._onMove) {
 
10488                                 this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
 
10491                         events.move = this._onMove;
 
10494                 if (this._zoomAnimated) {
 
10495                         events.zoomanim = this._animateZoom;
 
10501         // @section Extension methods
 
10502         // Layers extending `GridLayer` shall reimplement the following method.
 
10503         // @method createTile(coords: Object, done?: Function): HTMLElement
 
10504         // Called only internally, must be overriden by classes extending `GridLayer`.
 
10505         // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
 
10506         // is specified, it must be called when the tile has finished loading and drawing.
 
10507         createTile: function () {
 
10508                 return document.createElement('div');
 
10512         // @method getTileSize: Point
 
10513         // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
 
10514         getTileSize: function () {
 
10515                 var s = this.options.tileSize;
 
10516                 return s instanceof Point ? s : new Point(s, s);
 
10519         _updateZIndex: function () {
 
10520                 if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
 
10521                         this._container.style.zIndex = this.options.zIndex;
 
10525         _setAutoZIndex: function (compare) {
 
10526                 // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
 
10528                 var layers = this.getPane().children,
 
10529                     edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
 
10531                 for (var i = 0, len = layers.length, zIndex; i < len; i++) {
 
10533                         zIndex = layers[i].style.zIndex;
 
10535                         if (layers[i] !== this._container && zIndex) {
 
10536                                 edgeZIndex = compare(edgeZIndex, +zIndex);
 
10540                 if (isFinite(edgeZIndex)) {
 
10541                         this.options.zIndex = edgeZIndex + compare(-1, 1);
 
10542                         this._updateZIndex();
 
10546         _updateOpacity: function () {
 
10547                 if (!this._map) { return; }
 
10549                 // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
 
10550                 if (ielt9) { return; }
 
10552                 setOpacity(this._container, this.options.opacity);
 
10554                 var now = +new Date(),
 
10558                 for (var key in this._tiles) {
 
10559                         var tile = this._tiles[key];
 
10560                         if (!tile.current || !tile.loaded) { continue; }
 
10562                         var fade = Math.min(1, (now - tile.loaded) / 200);
 
10564                         setOpacity(tile.el, fade);
 
10571                                         this._onOpaqueTile(tile);
 
10573                                 tile.active = true;
 
10577                 if (willPrune && !this._noPrune) { this._pruneTiles(); }
 
10580                         cancelAnimFrame(this._fadeFrame);
 
10581                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
 
10585         _onOpaqueTile: falseFn,
 
10587         _initContainer: function () {
 
10588                 if (this._container) { return; }
 
10590                 this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
 
10591                 this._updateZIndex();
 
10593                 if (this.options.opacity < 1) {
 
10594                         this._updateOpacity();
 
10597                 this.getPane().appendChild(this._container);
 
10600         _updateLevels: function () {
 
10602                 var zoom = this._tileZoom,
 
10603                     maxZoom = this.options.maxZoom;
 
10605                 if (zoom === undefined) { return undefined; }
 
10607                 for (var z in this._levels) {
 
10608                         if (this._levels[z].el.children.length || z === zoom) {
 
10609                                 this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
 
10610                                 this._onUpdateLevel(z);
 
10612                                 remove(this._levels[z].el);
 
10613                                 this._removeTilesAtZoom(z);
 
10614                                 this._onRemoveLevel(z);
 
10615                                 delete this._levels[z];
 
10619                 var level = this._levels[zoom],
 
10623                         level = this._levels[zoom] = {};
 
10625                         level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
 
10626                         level.el.style.zIndex = maxZoom;
 
10628                         level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
 
10631                         this._setZoomTransform(level, map.getCenter(), map.getZoom());
 
10633                         // force the browser to consider the newly added element for transition
 
10634                         falseFn(level.el.offsetWidth);
 
10636                         this._onCreateLevel(level);
 
10639                 this._level = level;
 
10644         _onUpdateLevel: falseFn,
 
10646         _onRemoveLevel: falseFn,
 
10648         _onCreateLevel: falseFn,
 
10650         _pruneTiles: function () {
 
10657                 var zoom = this._map.getZoom();
 
10658                 if (zoom > this.options.maxZoom ||
 
10659                         zoom < this.options.minZoom) {
 
10660                         this._removeAllTiles();
 
10664                 for (key in this._tiles) {
 
10665                         tile = this._tiles[key];
 
10666                         tile.retain = tile.current;
 
10669                 for (key in this._tiles) {
 
10670                         tile = this._tiles[key];
 
10671                         if (tile.current && !tile.active) {
 
10672                                 var coords = tile.coords;
 
10673                                 if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
 
10674                                         this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
 
10679                 for (key in this._tiles) {
 
10680                         if (!this._tiles[key].retain) {
 
10681                                 this._removeTile(key);
 
10686         _removeTilesAtZoom: function (zoom) {
 
10687                 for (var key in this._tiles) {
 
10688                         if (this._tiles[key].coords.z !== zoom) {
 
10691                         this._removeTile(key);
 
10695         _removeAllTiles: function () {
 
10696                 for (var key in this._tiles) {
 
10697                         this._removeTile(key);
 
10701         _invalidateAll: function () {
 
10702                 for (var z in this._levels) {
 
10703                         remove(this._levels[z].el);
 
10704                         this._onRemoveLevel(z);
 
10705                         delete this._levels[z];
 
10707                 this._removeAllTiles();
 
10709                 this._tileZoom = null;
 
10712         _retainParent: function (x, y, z, minZoom) {
 
10713                 var x2 = Math.floor(x / 2),
 
10714                     y2 = Math.floor(y / 2),
 
10716                     coords2 = new Point(+x2, +y2);
 
10719                 var key = this._tileCoordsToKey(coords2),
 
10720                     tile = this._tiles[key];
 
10722                 if (tile && tile.active) {
 
10723                         tile.retain = true;
 
10726                 } else if (tile && tile.loaded) {
 
10727                         tile.retain = true;
 
10730                 if (z2 > minZoom) {
 
10731                         return this._retainParent(x2, y2, z2, minZoom);
 
10737         _retainChildren: function (x, y, z, maxZoom) {
 
10739                 for (var i = 2 * x; i < 2 * x + 2; i++) {
 
10740                         for (var j = 2 * y; j < 2 * y + 2; j++) {
 
10742                                 var coords = new Point(i, j);
 
10745                                 var key = this._tileCoordsToKey(coords),
 
10746                                     tile = this._tiles[key];
 
10748                                 if (tile && tile.active) {
 
10749                                         tile.retain = true;
 
10752                                 } else if (tile && tile.loaded) {
 
10753                                         tile.retain = true;
 
10756                                 if (z + 1 < maxZoom) {
 
10757                                         this._retainChildren(i, j, z + 1, maxZoom);
 
10763         _resetView: function (e) {
 
10764                 var animating = e && (e.pinch || e.flyTo);
 
10765                 this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
 
10768         _animateZoom: function (e) {
 
10769                 this._setView(e.center, e.zoom, true, e.noUpdate);
 
10772         _clampZoom: function (zoom) {
 
10773                 var options = this.options;
 
10775                 if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
 
10776                         return options.minNativeZoom;
 
10779                 if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
 
10780                         return options.maxNativeZoom;
 
10786         _setView: function (center, zoom, noPrune, noUpdate) {
 
10787                 var tileZoom = this._clampZoom(Math.round(zoom));
 
10788                 if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
 
10789                     (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
 
10790                         tileZoom = undefined;
 
10793                 var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
 
10795                 if (!noUpdate || tileZoomChanged) {
 
10797                         this._tileZoom = tileZoom;
 
10799                         if (this._abortLoading) {
 
10800                                 this._abortLoading();
 
10803                         this._updateLevels();
 
10806                         if (tileZoom !== undefined) {
 
10807                                 this._update(center);
 
10811                                 this._pruneTiles();
 
10814                         // Flag to prevent _updateOpacity from pruning tiles during
 
10815                         // a zoom anim or a pinch gesture
 
10816                         this._noPrune = !!noPrune;
 
10819                 this._setZoomTransforms(center, zoom);
 
10822         _setZoomTransforms: function (center, zoom) {
 
10823                 for (var i in this._levels) {
 
10824                         this._setZoomTransform(this._levels[i], center, zoom);
 
10828         _setZoomTransform: function (level, center, zoom) {
 
10829                 var scale = this._map.getZoomScale(zoom, level.zoom),
 
10830                     translate = level.origin.multiplyBy(scale)
 
10831                         .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
 
10834                         setTransform(level.el, translate, scale);
 
10836                         setPosition(level.el, translate);
 
10840         _resetGrid: function () {
 
10841                 var map = this._map,
 
10842                     crs = map.options.crs,
 
10843                     tileSize = this._tileSize = this.getTileSize(),
 
10844                     tileZoom = this._tileZoom;
 
10846                 var bounds = this._map.getPixelWorldBounds(this._tileZoom);
 
10848                         this._globalTileRange = this._pxBoundsToTileRange(bounds);
 
10851                 this._wrapX = crs.wrapLng && !this.options.noWrap && [
 
10852                         Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
 
10853                         Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
 
10855                 this._wrapY = crs.wrapLat && !this.options.noWrap && [
 
10856                         Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
 
10857                         Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
 
10861         _onMoveEnd: function () {
 
10862                 if (!this._map || this._map._animatingZoom) { return; }
 
10867         _getTiledPixelBounds: function (center) {
 
10868                 var map = this._map,
 
10869                     mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
 
10870                     scale = map.getZoomScale(mapZoom, this._tileZoom),
 
10871                     pixelCenter = map.project(center, this._tileZoom).floor(),
 
10872                     halfSize = map.getSize().divideBy(scale * 2);
 
10874                 return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
 
10877         // Private method to load tiles in the grid's active zoom level according to map bounds
 
10878         _update: function (center) {
 
10879                 var map = this._map;
 
10880                 if (!map) { return; }
 
10881                 var zoom = this._clampZoom(map.getZoom());
 
10883                 if (center === undefined) { center = map.getCenter(); }
 
10884                 if (this._tileZoom === undefined) { return; }   // if out of minzoom/maxzoom
 
10886                 var pixelBounds = this._getTiledPixelBounds(center),
 
10887                     tileRange = this._pxBoundsToTileRange(pixelBounds),
 
10888                     tileCenter = tileRange.getCenter(),
 
10890                     margin = this.options.keepBuffer,
 
10891                     noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
 
10892                                               tileRange.getTopRight().add([margin, -margin]));
 
10894                 // Sanity check: panic if the tile range contains Infinity somewhere.
 
10895                 if (!(isFinite(tileRange.min.x) &&
 
10896                       isFinite(tileRange.min.y) &&
 
10897                       isFinite(tileRange.max.x) &&
 
10898                       isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
 
10900                 for (var key in this._tiles) {
 
10901                         var c = this._tiles[key].coords;
 
10902                         if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
 
10903                                 this._tiles[key].current = false;
 
10907                 // _update just loads more tiles. If the tile zoom level differs too much
 
10908                 // from the map's, let _setView reset levels and prune old tiles.
 
10909                 if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
 
10911                 // create a queue of coordinates to load tiles from
 
10912                 for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
 
10913                         for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
 
10914                                 var coords = new Point(i, j);
 
10915                                 coords.z = this._tileZoom;
 
10917                                 if (!this._isValidTile(coords)) { continue; }
 
10919                                 if (!this._tiles[this._tileCoordsToKey(coords)]) {
 
10920                                         queue.push(coords);
 
10925                 // sort tile queue to load tiles in order of their distance to center
 
10926                 queue.sort(function (a, b) {
 
10927                         return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
 
10930                 if (queue.length !== 0) {
 
10931                         // if it's the first batch of tiles to load
 
10932                         if (!this._loading) {
 
10933                                 this._loading = true;
 
10934                                 // @event loading: Event
 
10935                                 // Fired when the grid layer starts loading tiles.
 
10936                                 this.fire('loading');
 
10939                         // create DOM fragment to append tiles in one batch
 
10940                         var fragment = document.createDocumentFragment();
 
10942                         for (i = 0; i < queue.length; i++) {
 
10943                                 this._addTile(queue[i], fragment);
 
10946                         this._level.el.appendChild(fragment);
 
10950         _isValidTile: function (coords) {
 
10951                 var crs = this._map.options.crs;
 
10953                 if (!crs.infinite) {
 
10954                         // don't load tile if it's out of bounds and not wrapped
 
10955                         var bounds = this._globalTileRange;
 
10956                         if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
 
10957                             (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
 
10960                 if (!this.options.bounds) { return true; }
 
10962                 // don't load tile if it doesn't intersect the bounds in options
 
10963                 var tileBounds = this._tileCoordsToBounds(coords);
 
10964                 return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
 
10967         _keyToBounds: function (key) {
 
10968                 return this._tileCoordsToBounds(this._keyToTileCoords(key));
 
10971         // converts tile coordinates to its geographical bounds
 
10972         _tileCoordsToBounds: function (coords) {
 
10974                 var map = this._map,
 
10975                     tileSize = this.getTileSize(),
 
10977                     nwPoint = coords.scaleBy(tileSize),
 
10978                     sePoint = nwPoint.add(tileSize),
 
10980                     nw = map.unproject(nwPoint, coords.z),
 
10981                     se = map.unproject(sePoint, coords.z),
 
10982                     bounds = new LatLngBounds(nw, se);
 
10984                 if (!this.options.noWrap) {
 
10985                         map.wrapLatLngBounds(bounds);
 
10991         // converts tile coordinates to key for the tile cache
 
10992         _tileCoordsToKey: function (coords) {
 
10993                 return coords.x + ':' + coords.y + ':' + coords.z;
 
10996         // converts tile cache key to coordinates
 
10997         _keyToTileCoords: function (key) {
 
10998                 var k = key.split(':'),
 
10999                     coords = new Point(+k[0], +k[1]);
 
11004         _removeTile: function (key) {
 
11005                 var tile = this._tiles[key];
 
11006                 if (!tile) { return; }
 
11010                 delete this._tiles[key];
 
11012                 // @event tileunload: TileEvent
 
11013                 // Fired when a tile is removed (e.g. when a tile goes off the screen).
 
11014                 this.fire('tileunload', {
 
11016                         coords: this._keyToTileCoords(key)
 
11020         _initTile: function (tile) {
 
11021                 addClass(tile, 'leaflet-tile');
 
11023                 var tileSize = this.getTileSize();
 
11024                 tile.style.width = tileSize.x + 'px';
 
11025                 tile.style.height = tileSize.y + 'px';
 
11027                 tile.onselectstart = falseFn;
 
11028                 tile.onmousemove = falseFn;
 
11030                 // update opacity on tiles in IE7-8 because of filter inheritance problems
 
11031                 if (ielt9 && this.options.opacity < 1) {
 
11032                         setOpacity(tile, this.options.opacity);
 
11035                 // without this hack, tiles disappear after zoom on Chrome for Android
 
11036                 // https://github.com/Leaflet/Leaflet/issues/2078
 
11037                 if (android && !android23) {
 
11038                         tile.style.WebkitBackfaceVisibility = 'hidden';
 
11042         _addTile: function (coords, container) {
 
11043                 var tilePos = this._getTilePos(coords),
 
11044                     key = this._tileCoordsToKey(coords);
 
11046                 var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
 
11048                 this._initTile(tile);
 
11050                 // if createTile is defined with a second argument ("done" callback),
 
11051                 // we know that tile is async and will be ready later; otherwise
 
11052                 if (this.createTile.length < 2) {
 
11053                         // mark tile as ready, but delay one frame for opacity animation to happen
 
11054                         requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
 
11057                 setPosition(tile, tilePos);
 
11059                 // save tile in cache
 
11060                 this._tiles[key] = {
 
11066                 container.appendChild(tile);
 
11067                 // @event tileloadstart: TileEvent
 
11068                 // Fired when a tile is requested and starts loading.
 
11069                 this.fire('tileloadstart', {
 
11075         _tileReady: function (coords, err, tile) {
 
11076                 if (!this._map) { return; }
 
11079                         // @event tileerror: TileErrorEvent
 
11080                         // Fired when there is an error loading a tile.
 
11081                         this.fire('tileerror', {
 
11088                 var key = this._tileCoordsToKey(coords);
 
11090                 tile = this._tiles[key];
 
11091                 if (!tile) { return; }
 
11093                 tile.loaded = +new Date();
 
11094                 if (this._map._fadeAnimated) {
 
11095                         setOpacity(tile.el, 0);
 
11096                         cancelAnimFrame(this._fadeFrame);
 
11097                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
 
11099                         tile.active = true;
 
11100                         this._pruneTiles();
 
11104                         addClass(tile.el, 'leaflet-tile-loaded');
 
11106                         // @event tileload: TileEvent
 
11107                         // Fired when a tile loads.
 
11108                         this.fire('tileload', {
 
11114                 if (this._noTilesToLoad()) {
 
11115                         this._loading = false;
 
11116                         // @event load: Event
 
11117                         // Fired when the grid layer loaded all visible tiles.
 
11120                         if (ielt9 || !this._map._fadeAnimated) {
 
11121                                 requestAnimFrame(this._pruneTiles, this);
 
11123                                 // Wait a bit more than 0.2 secs (the duration of the tile fade-in)
 
11124                                 // to trigger a pruning.
 
11125                                 setTimeout(bind(this._pruneTiles, this), 250);
 
11130         _getTilePos: function (coords) {
 
11131                 return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
 
11134         _wrapCoords: function (coords) {
 
11135                 var newCoords = new Point(
 
11136                         this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
 
11137                         this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
 
11138                 newCoords.z = coords.z;
 
11142         _pxBoundsToTileRange: function (bounds) {
 
11143                 var tileSize = this.getTileSize();
 
11145                         bounds.min.unscaleBy(tileSize).floor(),
 
11146                         bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
 
11149         _noTilesToLoad: function () {
 
11150                 for (var key in this._tiles) {
 
11151                         if (!this._tiles[key].loaded) { return false; }
 
11157 // @factory L.gridLayer(options?: GridLayer options)
 
11158 // Creates a new instance of GridLayer with the supplied options.
 
11159 function gridLayer(options) {
 
11160         return new GridLayer(options);
 
11165  * @inherits GridLayer
 
11167  * Used to load and display tile layers on the map. Extends `GridLayer`.
 
11172  * L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar'}).addTo(map);
 
11175  * @section URL template
 
11178  * A string of the following form:
 
11181  * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
 
11184  * `{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.
 
11186  * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
 
11189  * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
 
11194 var TileLayer = GridLayer.extend({
 
11197         // @aka TileLayer options
 
11199                 // @option minZoom: Number = 0
 
11200                 // The minimum zoom level down to which this layer will be displayed (inclusive).
 
11203                 // @option maxZoom: Number = 18
 
11204                 // The maximum zoom level up to which this layer will be displayed (inclusive).
 
11207                 // @option subdomains: String|String[] = 'abc'
 
11208                 // 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.
 
11211                 // @option errorTileUrl: String = ''
 
11212                 // URL to the tile image to show in place of the tile that failed to load.
 
11215                 // @option zoomOffset: Number = 0
 
11216                 // The zoom number used in tile URLs will be offset with this value.
 
11219                 // @option tms: Boolean = false
 
11220                 // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
 
11223                 // @option zoomReverse: Boolean = false
 
11224                 // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
 
11225                 zoomReverse: false,
 
11227                 // @option detectRetina: Boolean = false
 
11228                 // 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.
 
11229                 detectRetina: false,
 
11231                 // @option crossOrigin: Boolean = false
 
11232                 // If true, all tiles will have their crossOrigin attribute set to ''. This is needed if you want to access tile pixel data.
 
11236         initialize: function (url, options) {
 
11240                 options = setOptions(this, options);
 
11242                 // detecting retina displays, adjusting tileSize and zoom levels
 
11243                 if (options.detectRetina && retina && options.maxZoom > 0) {
 
11245                         options.tileSize = Math.floor(options.tileSize / 2);
 
11247                         if (!options.zoomReverse) {
 
11248                                 options.zoomOffset++;
 
11251                                 options.zoomOffset--;
 
11255                         options.minZoom = Math.max(0, options.minZoom);
 
11258                 if (typeof options.subdomains === 'string') {
 
11259                         options.subdomains = options.subdomains.split('');
 
11262                 // for https://github.com/Leaflet/Leaflet/issues/137
 
11264                         this.on('tileunload', this._onTileRemove);
 
11268         // @method setUrl(url: String, noRedraw?: Boolean): this
 
11269         // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
 
11270         setUrl: function (url, noRedraw) {
 
11279         // @method createTile(coords: Object, done?: Function): HTMLElement
 
11280         // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
 
11281         // to return an `<img>` HTML element with the appropiate image URL given `coords`. The `done`
 
11282         // callback is called when the tile has been loaded.
 
11283         createTile: function (coords, done) {
 
11284                 var tile = document.createElement('img');
 
11286                 on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
 
11287                 on(tile, 'error', bind(this._tileOnError, this, done, tile));
 
11289                 if (this.options.crossOrigin) {
 
11290                         tile.crossOrigin = '';
 
11294                  Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
 
11295                  http://www.w3.org/TR/WCAG20-TECHS/H67
 
11300                  Set role="presentation" to force screen readers to ignore this
 
11301                  https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
 
11303                 tile.setAttribute('role', 'presentation');
 
11305                 tile.src = this.getTileUrl(coords);
 
11310         // @section Extension methods
 
11312         // Layers extending `TileLayer` might reimplement the following method.
 
11313         // @method getTileUrl(coords: Object): String
 
11314         // Called only internally, returns the URL for a tile given its coordinates.
 
11315         // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
 
11316         getTileUrl: function (coords) {
 
11318                         r: retina ? '@2x' : '',
 
11319                         s: this._getSubdomain(coords),
 
11322                         z: this._getZoomForUrl()
 
11324                 if (this._map && !this._map.options.crs.infinite) {
 
11325                         var invertedY = this._globalTileRange.max.y - coords.y;
 
11326                         if (this.options.tms) {
 
11327                                 data['y'] = invertedY;
 
11329                         data['-y'] = invertedY;
 
11332                 return template(this._url, extend(data, this.options));
 
11335         _tileOnLoad: function (done, tile) {
 
11336                 // For https://github.com/Leaflet/Leaflet/issues/3332
 
11338                         setTimeout(bind(done, this, null, tile), 0);
 
11344         _tileOnError: function (done, tile, e) {
 
11345                 var errorUrl = this.options.errorTileUrl;
 
11346                 if (errorUrl && tile.src !== errorUrl) {
 
11347                         tile.src = errorUrl;
 
11352         _onTileRemove: function (e) {
 
11353                 e.tile.onload = null;
 
11356         _getZoomForUrl: function () {
 
11357                 var zoom = this._tileZoom,
 
11358                 maxZoom = this.options.maxZoom,
 
11359                 zoomReverse = this.options.zoomReverse,
 
11360                 zoomOffset = this.options.zoomOffset;
 
11363                         zoom = maxZoom - zoom;
 
11366                 return zoom + zoomOffset;
 
11369         _getSubdomain: function (tilePoint) {
 
11370                 var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
 
11371                 return this.options.subdomains[index];
 
11374         // stops loading all tiles in the background layer
 
11375         _abortLoading: function () {
 
11377                 for (i in this._tiles) {
 
11378                         if (this._tiles[i].coords.z !== this._tileZoom) {
 
11379                                 tile = this._tiles[i].el;
 
11381                                 tile.onload = falseFn;
 
11382                                 tile.onerror = falseFn;
 
11384                                 if (!tile.complete) {
 
11385                                         tile.src = emptyImageUrl;
 
11394 // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
 
11395 // Instantiates a tile layer object given a `URL template` and optionally an options object.
 
11397 function tileLayer(url, options) {
 
11398         return new TileLayer(url, options);
 
11402  * @class TileLayer.WMS
 
11403  * @inherits TileLayer
 
11404  * @aka L.TileLayer.WMS
 
11405  * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
 
11410  * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
 
11411  *      layers: 'nexrad-n0r-900913',
 
11412  *      format: 'image/png',
 
11413  *      transparent: true,
 
11414  *      attribution: "Weather data © 2012 IEM Nexrad"
 
11419 var TileLayerWMS = TileLayer.extend({
 
11422         // @aka TileLayer.WMS options
 
11423         // If any custom options not documented here are used, they will be sent to the
 
11424         // WMS server as extra parameters in each request URL. This can be useful for
 
11425         // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
 
11426         defaultWmsParams: {
 
11430                 // @option layers: String = ''
 
11431                 // **(required)** Comma-separated list of WMS layers to show.
 
11434                 // @option styles: String = ''
 
11435                 // Comma-separated list of WMS styles.
 
11438                 // @option format: String = 'image/jpeg'
 
11439                 // WMS image format (use `'image/png'` for layers with transparency).
 
11440                 format: 'image/jpeg',
 
11442                 // @option transparent: Boolean = false
 
11443                 // If `true`, the WMS service will return images with transparency.
 
11444                 transparent: false,
 
11446                 // @option version: String = '1.1.1'
 
11447                 // Version of the WMS service to use
 
11452                 // @option crs: CRS = null
 
11453                 // Coordinate Reference System to use for the WMS requests, defaults to
 
11454                 // map CRS. Don't change this if you're not sure what it means.
 
11457                 // @option uppercase: Boolean = false
 
11458                 // If `true`, WMS request parameter keys will be uppercase.
 
11462         initialize: function (url, options) {
 
11466                 var wmsParams = extend({}, this.defaultWmsParams);
 
11468                 // all keys that are not TileLayer options go to WMS params
 
11469                 for (var i in options) {
 
11470                         if (!(i in this.options)) {
 
11471                                 wmsParams[i] = options[i];
 
11475                 options = setOptions(this, options);
 
11477                 wmsParams.width = wmsParams.height = options.tileSize * (options.detectRetina && retina ? 2 : 1);
 
11479                 this.wmsParams = wmsParams;
 
11482         onAdd: function (map) {
 
11484                 this._crs = this.options.crs || map.options.crs;
 
11485                 this._wmsVersion = parseFloat(this.wmsParams.version);
 
11487                 var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
 
11488                 this.wmsParams[projectionKey] = this._crs.code;
 
11490                 TileLayer.prototype.onAdd.call(this, map);
 
11493         getTileUrl: function (coords) {
 
11495                 var tileBounds = this._tileCoordsToBounds(coords),
 
11496                     nw = this._crs.project(tileBounds.getNorthWest()),
 
11497                     se = this._crs.project(tileBounds.getSouthEast()),
 
11499                     bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
 
11500                             [se.y, nw.x, nw.y, se.x] :
 
11501                             [nw.x, se.y, se.x, nw.y]).join(','),
 
11503                     url = TileLayer.prototype.getTileUrl.call(this, coords);
 
11506                         getParamString(this.wmsParams, url, this.options.uppercase) +
 
11507                         (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
 
11510         // @method setParams(params: Object, noRedraw?: Boolean): this
 
11511         // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
 
11512         setParams: function (params, noRedraw) {
 
11514                 extend(this.wmsParams, params);
 
11525 // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
 
11526 // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
 
11527 function tileLayerWMS(url, options) {
 
11528         return new TileLayerWMS(url, options);
 
11531 TileLayer.WMS = TileLayerWMS;
 
11532 tileLayer.wms = tileLayerWMS;
 
11539  * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
 
11540  * DOM container of the renderer, its bounds, and its zoom animation.
 
11542  * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
 
11543  * itself can be added or removed to the map. All paths use a renderer, which can
 
11544  * be implicit (the map will decide the type of renderer and use it automatically)
 
11545  * or explicit (using the [`renderer`](#path-renderer) option of the path).
 
11547  * Do not use this class directly, use `SVG` and `Canvas` instead.
 
11549  * @event update: Event
 
11550  * Fired when the renderer updates its bounds, center and zoom, for example when
 
11551  * its map has moved
 
11554 var Renderer = Layer.extend({
 
11557         // @aka Renderer options
 
11559                 // @option padding: Number = 0.1
 
11560                 // How much to extend the clip area around the map view (relative to its size)
 
11561                 // e.g. 0.1 would be 10% of map view in each direction
 
11565         initialize: function (options) {
 
11566                 setOptions(this, options);
 
11568                 this._layers = this._layers || {};
 
11571         onAdd: function () {
 
11572                 if (!this._container) {
 
11573                         this._initContainer(); // defined by renderer implementations
 
11575                         if (this._zoomAnimated) {
 
11576                                 addClass(this._container, 'leaflet-zoom-animated');
 
11580                 this.getPane().appendChild(this._container);
 
11582                 this.on('update', this._updatePaths, this);
 
11585         onRemove: function () {
 
11586                 this.off('update', this._updatePaths, this);
 
11587                 this._destroyContainer();
 
11590         getEvents: function () {
 
11592                         viewreset: this._reset,
 
11593                         zoom: this._onZoom,
 
11594                         moveend: this._update,
 
11595                         zoomend: this._onZoomEnd
 
11597                 if (this._zoomAnimated) {
 
11598                         events.zoomanim = this._onAnimZoom;
 
11603         _onAnimZoom: function (ev) {
 
11604                 this._updateTransform(ev.center, ev.zoom);
 
11607         _onZoom: function () {
 
11608                 this._updateTransform(this._map.getCenter(), this._map.getZoom());
 
11611         _updateTransform: function (center, zoom) {
 
11612                 var scale = this._map.getZoomScale(zoom, this._zoom),
 
11613                     position = getPosition(this._container),
 
11614                     viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
 
11615                     currentCenterPoint = this._map.project(this._center, zoom),
 
11616                     destCenterPoint = this._map.project(center, zoom),
 
11617                     centerOffset = destCenterPoint.subtract(currentCenterPoint),
 
11619                     topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
 
11622                         setTransform(this._container, topLeftOffset, scale);
 
11624                         setPosition(this._container, topLeftOffset);
 
11628         _reset: function () {
 
11630                 this._updateTransform(this._center, this._zoom);
 
11632                 for (var id in this._layers) {
 
11633                         this._layers[id]._reset();
 
11637         _onZoomEnd: function () {
 
11638                 for (var id in this._layers) {
 
11639                         this._layers[id]._project();
 
11643         _updatePaths: function () {
 
11644                 for (var id in this._layers) {
 
11645                         this._layers[id]._update();
 
11649         _update: function () {
 
11650                 // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
 
11651                 // Subclasses are responsible of firing the 'update' event.
 
11652                 var p = this.options.padding,
 
11653                     size = this._map.getSize(),
 
11654                     min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
 
11656                 this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
 
11658                 this._center = this._map.getCenter();
 
11659                 this._zoom = this._map.getZoom();
 
11665  * @inherits Renderer
 
11668  * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
 
11669  * Inherits `Renderer`.
 
11671  * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
 
11672  * available in all web browsers, notably IE8, and overlapping geometries might
 
11673  * not display properly in some edge cases.
 
11677  * Use Canvas by default for all paths in the map:
 
11680  * var map = L.map('map', {
 
11681  *      renderer: L.canvas()
 
11685  * Use a Canvas renderer with extra padding for specific vector geometries:
 
11688  * var map = L.map('map');
 
11689  * var myRenderer = L.canvas({ padding: 0.5 });
 
11690  * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
11691  * var circle = L.circle( center, { renderer: myRenderer } );
 
11695 var Canvas = Renderer.extend({
 
11696         getEvents: function () {
 
11697                 var events = Renderer.prototype.getEvents.call(this);
 
11698                 events.viewprereset = this._onViewPreReset;
 
11702         _onViewPreReset: function () {
 
11703                 // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
 
11704                 this._postponeUpdatePaths = true;
 
11707         onAdd: function () {
 
11708                 Renderer.prototype.onAdd.call(this);
 
11710                 // Redraw vectors since canvas is cleared upon removal,
 
11711                 // in case of removing the renderer itself from the map.
 
11715         _initContainer: function () {
 
11716                 var container = this._container = document.createElement('canvas');
 
11718                 on(container, 'mousemove', throttle(this._onMouseMove, 32, this), this);
 
11719                 on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
 
11720                 on(container, 'mouseout', this._handleMouseOut, this);
 
11722                 this._ctx = container.getContext('2d');
 
11725         _destroyContainer: function () {
 
11727                 remove(this._container);
 
11728                 off(this._container);
 
11729                 delete this._container;
 
11732         _updatePaths: function () {
 
11733                 if (this._postponeUpdatePaths) { return; }
 
11736                 this._redrawBounds = null;
 
11737                 for (var id in this._layers) {
 
11738                         layer = this._layers[id];
 
11744         _update: function () {
 
11745                 if (this._map._animatingZoom && this._bounds) { return; }
 
11747                 this._drawnLayers = {};
 
11749                 Renderer.prototype._update.call(this);
 
11751                 var b = this._bounds,
 
11752                     container = this._container,
 
11753                     size = b.getSize(),
 
11754                     m = retina ? 2 : 1;
 
11756                 setPosition(container, b.min);
 
11758                 // set canvas size (also clearing it); use double size on retina
 
11759                 container.width = m * size.x;
 
11760                 container.height = m * size.y;
 
11761                 container.style.width = size.x + 'px';
 
11762                 container.style.height = size.y + 'px';
 
11765                         this._ctx.scale(2, 2);
 
11768                 // translate so we use the same path coordinates after canvas element moves
 
11769                 this._ctx.translate(-b.min.x, -b.min.y);
 
11771                 // Tell paths to redraw themselves
 
11772                 this.fire('update');
 
11775         _reset: function () {
 
11776                 Renderer.prototype._reset.call(this);
 
11778                 if (this._postponeUpdatePaths) {
 
11779                         this._postponeUpdatePaths = false;
 
11780                         this._updatePaths();
 
11784         _initPath: function (layer) {
 
11785                 this._updateDashArray(layer);
 
11786                 this._layers[stamp(layer)] = layer;
 
11788                 var order = layer._order = {
 
11790                         prev: this._drawLast,
 
11793                 if (this._drawLast) { this._drawLast.next = order; }
 
11794                 this._drawLast = order;
 
11795                 this._drawFirst = this._drawFirst || this._drawLast;
 
11798         _addPath: function (layer) {
 
11799                 this._requestRedraw(layer);
 
11802         _removePath: function (layer) {
 
11803                 var order = layer._order;
 
11804                 var next = order.next;
 
11805                 var prev = order.prev;
 
11810                         this._drawLast = prev;
 
11815                         this._drawFirst = next;
 
11818                 delete layer._order;
 
11820                 delete this._layers[L.stamp(layer)];
 
11822                 this._requestRedraw(layer);
 
11825         _updatePath: function (layer) {
 
11826                 // Redraw the union of the layer's old pixel
 
11827                 // bounds and the new pixel bounds.
 
11828                 this._extendRedrawBounds(layer);
 
11831                 // The redraw will extend the redraw bounds
 
11832                 // with the new pixel bounds.
 
11833                 this._requestRedraw(layer);
 
11836         _updateStyle: function (layer) {
 
11837                 this._updateDashArray(layer);
 
11838                 this._requestRedraw(layer);
 
11841         _updateDashArray: function (layer) {
 
11842                 if (layer.options.dashArray) {
 
11843                         var parts = layer.options.dashArray.split(','),
 
11846                         for (i = 0; i < parts.length; i++) {
 
11847                                 dashArray.push(Number(parts[i]));
 
11849                         layer.options._dashArray = dashArray;
 
11853         _requestRedraw: function (layer) {
 
11854                 if (!this._map) { return; }
 
11856                 this._extendRedrawBounds(layer);
 
11857                 this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
 
11860         _extendRedrawBounds: function (layer) {
 
11861                 if (layer._pxBounds) {
 
11862                         var padding = (layer.options.weight || 0) + 1;
 
11863                         this._redrawBounds = this._redrawBounds || new Bounds();
 
11864                         this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
 
11865                         this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
 
11869         _redraw: function () {
 
11870                 this._redrawRequest = null;
 
11872                 if (this._redrawBounds) {
 
11873                         this._redrawBounds.min._floor();
 
11874                         this._redrawBounds.max._ceil();
 
11877                 this._clear(); // clear layers in redraw bounds
 
11878                 this._draw(); // draw layers
 
11880                 this._redrawBounds = null;
 
11883         _clear: function () {
 
11884                 var bounds = this._redrawBounds;
 
11886                         var size = bounds.getSize();
 
11887                         this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
 
11889                         this._ctx.clearRect(0, 0, this._container.width, this._container.height);
 
11893         _draw: function () {
 
11894                 var layer, bounds = this._redrawBounds;
 
11897                         var size = bounds.getSize();
 
11898                         this._ctx.beginPath();
 
11899                         this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
 
11903                 this._drawing = true;
 
11905                 for (var order = this._drawFirst; order; order = order.next) {
 
11906                         layer = order.layer;
 
11907                         if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
 
11908                                 layer._updatePath();
 
11912                 this._drawing = false;
 
11914                 this._ctx.restore();  // Restore state before clipping.
 
11917         _updatePoly: function (layer, closed) {
 
11918                 if (!this._drawing) { return; }
 
11921                     parts = layer._parts,
 
11922                     len = parts.length,
 
11925                 if (!len) { return; }
 
11927                 this._drawnLayers[layer._leaflet_id] = layer;
 
11931                 for (i = 0; i < len; i++) {
 
11932                         for (j = 0, len2 = parts[i].length; j < len2; j++) {
 
11934                                 ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
 
11941                 this._fillStroke(ctx, layer);
 
11943                 // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
 
11946         _updateCircle: function (layer) {
 
11948                 if (!this._drawing || layer._empty()) { return; }
 
11950                 var p = layer._point,
 
11953                     s = (layer._radiusY || r) / r;
 
11955                 this._drawnLayers[layer._leaflet_id] = layer;
 
11963                 ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
 
11969                 this._fillStroke(ctx, layer);
 
11972         _fillStroke: function (ctx, layer) {
 
11973                 var options = layer.options;
 
11975                 if (options.fill) {
 
11976                         ctx.globalAlpha = options.fillOpacity;
 
11977                         ctx.fillStyle = options.fillColor || options.color;
 
11978                         ctx.fill(options.fillRule || 'evenodd');
 
11981                 if (options.stroke && options.weight !== 0) {
 
11982                         if (ctx.setLineDash) {
 
11983                                 ctx.setLineDash(layer.options && layer.options._dashArray || []);
 
11985                         ctx.globalAlpha = options.opacity;
 
11986                         ctx.lineWidth = options.weight;
 
11987                         ctx.strokeStyle = options.color;
 
11988                         ctx.lineCap = options.lineCap;
 
11989                         ctx.lineJoin = options.lineJoin;
 
11994         // Canvas obviously doesn't have mouse events for individual drawn objects,
 
11995         // so we emulate that by calculating what's under the mouse on mousemove/click manually
 
11997         _onClick: function (e) {
 
11998                 var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
 
12000                 for (var order = this._drawFirst; order; order = order.next) {
 
12001                         layer = order.layer;
 
12002                         if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
 
12003                                 clickedLayer = layer;
 
12006                 if (clickedLayer)  {
 
12008                         this._fireEvent([clickedLayer], e);
 
12012         _onMouseMove: function (e) {
 
12013                 if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
 
12015                 var point = this._map.mouseEventToLayerPoint(e);
 
12016                 this._handleMouseHover(e, point);
 
12020         _handleMouseOut: function (e) {
 
12021                 var layer = this._hoveredLayer;
 
12023                         // if we're leaving the layer, fire mouseout
 
12024                         removeClass(this._container, 'leaflet-interactive');
 
12025                         this._fireEvent([layer], e, 'mouseout');
 
12026                         this._hoveredLayer = null;
 
12030         _handleMouseHover: function (e, point) {
 
12031                 var layer, candidateHoveredLayer;
 
12033                 for (var order = this._drawFirst; order; order = order.next) {
 
12034                         layer = order.layer;
 
12035                         if (layer.options.interactive && layer._containsPoint(point)) {
 
12036                                 candidateHoveredLayer = layer;
 
12040                 if (candidateHoveredLayer !== this._hoveredLayer) {
 
12041                         this._handleMouseOut(e);
 
12043                         if (candidateHoveredLayer) {
 
12044                                 addClass(this._container, 'leaflet-interactive'); // change cursor
 
12045                                 this._fireEvent([candidateHoveredLayer], e, 'mouseover');
 
12046                                 this._hoveredLayer = candidateHoveredLayer;
 
12050                 if (this._hoveredLayer) {
 
12051                         this._fireEvent([this._hoveredLayer], e);
 
12055         _fireEvent: function (layers, e, type) {
 
12056                 this._map._fireDOMEvent(e, type || e.type, layers);
 
12059         _bringToFront: function (layer) {
 
12060                 var order = layer._order;
 
12061                 var next = order.next;
 
12062                 var prev = order.prev;
 
12073                         // Update first entry unless this is the
 
12075                         this._drawFirst = next;
 
12078                 order.prev = this._drawLast;
 
12079                 this._drawLast.next = order;
 
12082                 this._drawLast = order;
 
12084                 this._requestRedraw(layer);
 
12087         _bringToBack: function (layer) {
 
12088                 var order = layer._order;
 
12089                 var next = order.next;
 
12090                 var prev = order.prev;
 
12101                         // Update last entry unless this is the
 
12103                         this._drawLast = prev;
 
12108                 order.next = this._drawFirst;
 
12109                 this._drawFirst.prev = order;
 
12110                 this._drawFirst = order;
 
12112                 this._requestRedraw(layer);
 
12116 // @factory L.canvas(options?: Renderer options)
 
12117 // Creates a Canvas renderer with the given options.
 
12118 function canvas$1(options) {
 
12119         return canvas ? new Canvas(options) : null;
 
12123  * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
 
12127 var vmlCreate = (function () {
 
12129                 document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
 
12130                 return function (name) {
 
12131                         return document.createElement('<lvml:' + name + ' class="lvml">');
 
12134                 return function (name) {
 
12135                         return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
 
12144  * 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.
 
12146  * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
 
12147  * with old versions of Internet Explorer.
 
12150 // mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
 
12153         _initContainer: function () {
 
12154                 this._container = create$1('div', 'leaflet-vml-container');
 
12157         _update: function () {
 
12158                 if (this._map._animatingZoom) { return; }
 
12159                 Renderer.prototype._update.call(this);
 
12160                 this.fire('update');
 
12163         _initPath: function (layer) {
 
12164                 var container = layer._container = vmlCreate('shape');
 
12166                 addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
 
12168                 container.coordsize = '1 1';
 
12170                 layer._path = vmlCreate('path');
 
12171                 container.appendChild(layer._path);
 
12173                 this._updateStyle(layer);
 
12174                 this._layers[stamp(layer)] = layer;
 
12177         _addPath: function (layer) {
 
12178                 var container = layer._container;
 
12179                 this._container.appendChild(container);
 
12181                 if (layer.options.interactive) {
 
12182                         layer.addInteractiveTarget(container);
 
12186         _removePath: function (layer) {
 
12187                 var container = layer._container;
 
12189                 layer.removeInteractiveTarget(container);
 
12190                 delete this._layers[stamp(layer)];
 
12193         _updateStyle: function (layer) {
 
12194                 var stroke = layer._stroke,
 
12195                     fill = layer._fill,
 
12196                     options = layer.options,
 
12197                     container = layer._container;
 
12199                 container.stroked = !!options.stroke;
 
12200                 container.filled = !!options.fill;
 
12202                 if (options.stroke) {
 
12204                                 stroke = layer._stroke = vmlCreate('stroke');
 
12206                         container.appendChild(stroke);
 
12207                         stroke.weight = options.weight + 'px';
 
12208                         stroke.color = options.color;
 
12209                         stroke.opacity = options.opacity;
 
12211                         if (options.dashArray) {
 
12212                                 stroke.dashStyle = isArray(options.dashArray) ?
 
12213                                     options.dashArray.join(' ') :
 
12214                                     options.dashArray.replace(/( *, *)/g, ' ');
 
12216                                 stroke.dashStyle = '';
 
12218                         stroke.endcap = options.lineCap.replace('butt', 'flat');
 
12219                         stroke.joinstyle = options.lineJoin;
 
12221                 } else if (stroke) {
 
12222                         container.removeChild(stroke);
 
12223                         layer._stroke = null;
 
12226                 if (options.fill) {
 
12228                                 fill = layer._fill = vmlCreate('fill');
 
12230                         container.appendChild(fill);
 
12231                         fill.color = options.fillColor || options.color;
 
12232                         fill.opacity = options.fillOpacity;
 
12235                         container.removeChild(fill);
 
12236                         layer._fill = null;
 
12240         _updateCircle: function (layer) {
 
12241                 var p = layer._point.round(),
 
12242                     r = Math.round(layer._radius),
 
12243                     r2 = Math.round(layer._radiusY || r);
 
12245                 this._setPath(layer, layer._empty() ? 'M0 0' :
 
12246                                 'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
 
12249         _setPath: function (layer, path) {
 
12250                 layer._path.v = path;
 
12253         _bringToFront: function (layer) {
 
12254                 toFront(layer._container);
 
12257         _bringToBack: function (layer) {
 
12258                 toBack(layer._container);
 
12262 var create$2 = vml ? vmlCreate : svgCreate;
 
12266  * @inherits Renderer
 
12269  * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
 
12270  * Inherits `Renderer`.
 
12272  * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
 
12273  * available in all web browsers, notably Android 2.x and 3.x.
 
12275  * Although SVG is not available on IE7 and IE8, these browsers support
 
12276  * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
 
12277  * (a now deprecated technology), and the SVG renderer will fall back to VML in
 
12282  * Use SVG by default for all paths in the map:
 
12285  * var map = L.map('map', {
 
12286  *      renderer: L.svg()
 
12290  * Use a SVG renderer with extra padding for specific vector geometries:
 
12293  * var map = L.map('map');
 
12294  * var myRenderer = L.svg({ padding: 0.5 });
 
12295  * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
12296  * var circle = L.circle( center, { renderer: myRenderer } );
 
12300 var SVG = Renderer.extend({
 
12302         getEvents: function () {
 
12303                 var events = Renderer.prototype.getEvents.call(this);
 
12304                 events.zoomstart = this._onZoomStart;
 
12308         _initContainer: function () {
 
12309                 this._container = create$2('svg');
 
12311                 // makes it possible to click through svg root; we'll reset it back in individual paths
 
12312                 this._container.setAttribute('pointer-events', 'none');
 
12314                 this._rootGroup = create$2('g');
 
12315                 this._container.appendChild(this._rootGroup);
 
12318         _destroyContainer: function () {
 
12319                 remove(this._container);
 
12320                 off(this._container);
 
12321                 delete this._container;
 
12322                 delete this._rootGroup;
 
12325         _onZoomStart: function () {
 
12326                 // Drag-then-pinch interactions might mess up the center and zoom.
 
12327                 // In this case, the easiest way to prevent this is re-do the renderer
 
12328                 //   bounds and padding when the zooming starts.
 
12332         _update: function () {
 
12333                 if (this._map._animatingZoom && this._bounds) { return; }
 
12335                 Renderer.prototype._update.call(this);
 
12337                 var b = this._bounds,
 
12338                     size = b.getSize(),
 
12339                     container = this._container;
 
12341                 // set size of svg-container if changed
 
12342                 if (!this._svgSize || !this._svgSize.equals(size)) {
 
12343                         this._svgSize = size;
 
12344                         container.setAttribute('width', size.x);
 
12345                         container.setAttribute('height', size.y);
 
12348                 // movement: update container viewBox so that we don't have to change coordinates of individual layers
 
12349                 setPosition(container, b.min);
 
12350                 container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
 
12352                 this.fire('update');
 
12355         // methods below are called by vector layers implementations
 
12357         _initPath: function (layer) {
 
12358                 var path = layer._path = create$2('path');
 
12361                 // @option className: String = null
 
12362                 // Custom class name set on an element. Only for SVG renderer.
 
12363                 if (layer.options.className) {
 
12364                         addClass(path, layer.options.className);
 
12367                 if (layer.options.interactive) {
 
12368                         addClass(path, 'leaflet-interactive');
 
12371                 this._updateStyle(layer);
 
12372                 this._layers[stamp(layer)] = layer;
 
12375         _addPath: function (layer) {
 
12376                 if (!this._rootGroup) { this._initContainer(); }
 
12377                 this._rootGroup.appendChild(layer._path);
 
12378                 layer.addInteractiveTarget(layer._path);
 
12381         _removePath: function (layer) {
 
12382                 remove(layer._path);
 
12383                 layer.removeInteractiveTarget(layer._path);
 
12384                 delete this._layers[stamp(layer)];
 
12387         _updatePath: function (layer) {
 
12392         _updateStyle: function (layer) {
 
12393                 var path = layer._path,
 
12394                     options = layer.options;
 
12396                 if (!path) { return; }
 
12398                 if (options.stroke) {
 
12399                         path.setAttribute('stroke', options.color);
 
12400                         path.setAttribute('stroke-opacity', options.opacity);
 
12401                         path.setAttribute('stroke-width', options.weight);
 
12402                         path.setAttribute('stroke-linecap', options.lineCap);
 
12403                         path.setAttribute('stroke-linejoin', options.lineJoin);
 
12405                         if (options.dashArray) {
 
12406                                 path.setAttribute('stroke-dasharray', options.dashArray);
 
12408                                 path.removeAttribute('stroke-dasharray');
 
12411                         if (options.dashOffset) {
 
12412                                 path.setAttribute('stroke-dashoffset', options.dashOffset);
 
12414                                 path.removeAttribute('stroke-dashoffset');
 
12417                         path.setAttribute('stroke', 'none');
 
12420                 if (options.fill) {
 
12421                         path.setAttribute('fill', options.fillColor || options.color);
 
12422                         path.setAttribute('fill-opacity', options.fillOpacity);
 
12423                         path.setAttribute('fill-rule', options.fillRule || 'evenodd');
 
12425                         path.setAttribute('fill', 'none');
 
12429         _updatePoly: function (layer, closed) {
 
12430                 this._setPath(layer, pointsToPath(layer._parts, closed));
 
12433         _updateCircle: function (layer) {
 
12434                 var p = layer._point,
 
12436                     r2 = layer._radiusY || r,
 
12437                     arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
 
12439                 // drawing a circle with two half-arcs
 
12440                 var d = layer._empty() ? 'M0 0' :
 
12441                                 'M' + (p.x - r) + ',' + p.y +
 
12442                                 arc + (r * 2) + ',0 ' +
 
12443                                 arc + (-r * 2) + ',0 ';
 
12445                 this._setPath(layer, d);
 
12448         _setPath: function (layer, path) {
 
12449                 layer._path.setAttribute('d', path);
 
12452         // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
 
12453         _bringToFront: function (layer) {
 
12454                 toFront(layer._path);
 
12457         _bringToBack: function (layer) {
 
12458                 toBack(layer._path);
 
12463         SVG.include(vmlMixin);
 
12466 // @factory L.svg(options?: Renderer options)
 
12467 // Creates a SVG renderer with the given options.
 
12468 function svg$1(options) {
 
12469         return svg || vml ? new SVG(options) : null;
 
12473         // @namespace Map; @method getRenderer(layer: Path): Renderer
 
12474         // Returns the instance of `Renderer` that should be used to render the given
 
12475         // `Path`. It will ensure that the `renderer` options of the map and paths
 
12476         // are respected, and that the renderers do exist on the map.
 
12477         getRenderer: function (layer) {
 
12478                 // @namespace Path; @option renderer: Renderer
 
12479                 // Use this specific instance of `Renderer` for this path. Takes
 
12480                 // precedence over the map's [default renderer](#map-renderer).
 
12481                 var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
 
12484                         // @namespace Map; @option preferCanvas: Boolean = false
 
12485                         // Whether `Path`s should be rendered on a `Canvas` renderer.
 
12486                         // By default, all `Path`s are rendered in a `SVG` renderer.
 
12487                         renderer = this._renderer = (this.options.preferCanvas && canvas$1()) || svg$1();
 
12490                 if (!this.hasLayer(renderer)) {
 
12491                         this.addLayer(renderer);
 
12496         _getPaneRenderer: function (name) {
 
12497                 if (name === 'overlayPane' || name === undefined) {
 
12501                 var renderer = this._paneRenderers[name];
 
12502                 if (renderer === undefined) {
 
12503                         renderer = (SVG && svg$1({pane: name})) || (Canvas && canvas$1({pane: name}));
 
12504                         this._paneRenderers[name] = renderer;
 
12511  * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
 
12517  * @inherits Polygon
 
12519  * A class for drawing rectangle overlays on a map. Extends `Polygon`.
 
12524  * // define rectangle geographical bounds
 
12525  * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
 
12527  * // create an orange rectangle
 
12528  * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
 
12530  * // zoom the map to the rectangle bounds
 
12531  * map.fitBounds(bounds);
 
12537 var Rectangle = Polygon.extend({
 
12538         initialize: function (latLngBounds, options) {
 
12539                 Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
 
12542         // @method setBounds(latLngBounds: LatLngBounds): this
 
12543         // Redraws the rectangle with the passed bounds.
 
12544         setBounds: function (latLngBounds) {
 
12545                 return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
 
12548         _boundsToLatLngs: function (latLngBounds) {
 
12549                 latLngBounds = toLatLngBounds(latLngBounds);
 
12551                         latLngBounds.getSouthWest(),
 
12552                         latLngBounds.getNorthWest(),
 
12553                         latLngBounds.getNorthEast(),
 
12554                         latLngBounds.getSouthEast()
 
12560 // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
 
12561 function rectangle(latLngBounds, options) {
 
12562         return new Rectangle(latLngBounds, options);
 
12565 SVG.create = create$2;
 
12566 SVG.pointsToPath = pointsToPath;
 
12568 GeoJSON.geometryToLayer = geometryToLayer;
 
12569 GeoJSON.coordsToLatLng = coordsToLatLng;
 
12570 GeoJSON.coordsToLatLngs = coordsToLatLngs;
 
12571 GeoJSON.latLngToCoords = latLngToCoords;
 
12572 GeoJSON.latLngsToCoords = latLngsToCoords;
 
12573 GeoJSON.getFeature = getFeature;
 
12574 GeoJSON.asFeature = asFeature;
 
12577  * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
 
12578  * (zoom to a selected bounding box), enabled by default.
 
12582 // @section Interaction Options
 
12584         // @option boxZoom: Boolean = true
 
12585         // Whether the map can be zoomed to a rectangular area specified by
 
12586         // dragging the mouse while pressing the shift key.
 
12590 var BoxZoom = Handler.extend({
 
12591         initialize: function (map) {
 
12593                 this._container = map._container;
 
12594                 this._pane = map._panes.overlayPane;
 
12595                 this._resetStateTimeout = 0;
 
12596                 map.on('unload', this._destroy, this);
 
12599         addHooks: function () {
 
12600                 on(this._container, 'mousedown', this._onMouseDown, this);
 
12603         removeHooks: function () {
 
12604                 off(this._container, 'mousedown', this._onMouseDown, this);
 
12607         moved: function () {
 
12608                 return this._moved;
 
12611         _destroy: function () {
 
12612                 remove(this._pane);
 
12616         _resetState: function () {
 
12617                 this._resetStateTimeout = 0;
 
12618                 this._moved = false;
 
12621         _clearDeferredResetState: function () {
 
12622                 if (this._resetStateTimeout !== 0) {
 
12623                         clearTimeout(this._resetStateTimeout);
 
12624                         this._resetStateTimeout = 0;
 
12628         _onMouseDown: function (e) {
 
12629                 if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
 
12631                 // Clear the deferred resetState if it hasn't executed yet, otherwise it
 
12632                 // will interrupt the interaction and orphan a box element in the container.
 
12633                 this._clearDeferredResetState();
 
12634                 this._resetState();
 
12636                 disableTextSelection();
 
12637                 disableImageDrag();
 
12639                 this._startPoint = this._map.mouseEventToContainerPoint(e);
 
12643                         mousemove: this._onMouseMove,
 
12644                         mouseup: this._onMouseUp,
 
12645                         keydown: this._onKeyDown
 
12649         _onMouseMove: function (e) {
 
12650                 if (!this._moved) {
 
12651                         this._moved = true;
 
12653                         this._box = create$1('div', 'leaflet-zoom-box', this._container);
 
12654                         addClass(this._container, 'leaflet-crosshair');
 
12656                         this._map.fire('boxzoomstart');
 
12659                 this._point = this._map.mouseEventToContainerPoint(e);
 
12661                 var bounds = new Bounds(this._point, this._startPoint),
 
12662                     size = bounds.getSize();
 
12664                 setPosition(this._box, bounds.min);
 
12666                 this._box.style.width  = size.x + 'px';
 
12667                 this._box.style.height = size.y + 'px';
 
12670         _finish: function () {
 
12673                         removeClass(this._container, 'leaflet-crosshair');
 
12676                 enableTextSelection();
 
12681                         mousemove: this._onMouseMove,
 
12682                         mouseup: this._onMouseUp,
 
12683                         keydown: this._onKeyDown
 
12687         _onMouseUp: function (e) {
 
12688                 if ((e.which !== 1) && (e.button !== 1)) { return; }
 
12692                 if (!this._moved) { return; }
 
12693                 // Postpone to next JS tick so internal click event handling
 
12694                 // still see it as "moved".
 
12695                 this._clearDeferredResetState();
 
12696                 this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
 
12698                 var bounds = new LatLngBounds(
 
12699                         this._map.containerPointToLatLng(this._startPoint),
 
12700                         this._map.containerPointToLatLng(this._point));
 
12704                         .fire('boxzoomend', {boxZoomBounds: bounds});
 
12707         _onKeyDown: function (e) {
 
12708                 if (e.keyCode === 27) {
 
12714 // @section Handlers
 
12715 // @property boxZoom: Handler
 
12716 // Box (shift-drag with mouse) zoom handler.
 
12717 Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
 
12720  * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
 
12724 // @section Interaction Options
 
12727         // @option doubleClickZoom: Boolean|String = true
 
12728         // Whether the map can be zoomed in by double clicking on it and
 
12729         // zoomed out by double clicking while holding shift. If passed
 
12730         // `'center'`, double-click zoom will zoom to the center of the
 
12731         //  view regardless of where the mouse was.
 
12732         doubleClickZoom: true
 
12735 var DoubleClickZoom = Handler.extend({
 
12736         addHooks: function () {
 
12737                 this._map.on('dblclick', this._onDoubleClick, this);
 
12740         removeHooks: function () {
 
12741                 this._map.off('dblclick', this._onDoubleClick, this);
 
12744         _onDoubleClick: function (e) {
 
12745                 var map = this._map,
 
12746                     oldZoom = map.getZoom(),
 
12747                     delta = map.options.zoomDelta,
 
12748                     zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
 
12750                 if (map.options.doubleClickZoom === 'center') {
 
12753                         map.setZoomAround(e.containerPoint, zoom);
 
12758 // @section Handlers
 
12760 // Map properties include interaction handlers that allow you to control
 
12761 // interaction behavior in runtime, enabling or disabling certain features such
 
12762 // as dragging or touch zoom (see `Handler` methods). For example:
 
12765 // map.doubleClickZoom.disable();
 
12768 // @property doubleClickZoom: Handler
 
12769 // Double click zoom handler.
 
12770 Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
 
12773  * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
 
12777 // @section Interaction Options
 
12779         // @option dragging: Boolean = true
 
12780         // Whether the map be draggable with mouse/touch or not.
 
12783         // @section Panning Inertia Options
 
12784         // @option inertia: Boolean = *
 
12785         // If enabled, panning of the map will have an inertia effect where
 
12786         // the map builds momentum while dragging and continues moving in
 
12787         // the same direction for some time. Feels especially nice on touch
 
12788         // devices. Enabled by default unless running on old Android devices.
 
12789         inertia: !android23,
 
12791         // @option inertiaDeceleration: Number = 3000
 
12792         // The rate with which the inertial movement slows down, in pixels/second².
 
12793         inertiaDeceleration: 3400, // px/s^2
 
12795         // @option inertiaMaxSpeed: Number = Infinity
 
12796         // Max speed of the inertial movement, in pixels/second.
 
12797         inertiaMaxSpeed: Infinity, // px/s
 
12799         // @option easeLinearity: Number = 0.2
 
12800         easeLinearity: 0.2,
 
12802         // TODO refactor, move to CRS
 
12803         // @option worldCopyJump: Boolean = false
 
12804         // With this option enabled, the map tracks when you pan to another "copy"
 
12805         // of the world and seamlessly jumps to the original one so that all overlays
 
12806         // like markers and vector layers are still visible.
 
12807         worldCopyJump: false,
 
12809         // @option maxBoundsViscosity: Number = 0.0
 
12810         // If `maxBounds` is set, this option will control how solid the bounds
 
12811         // are when dragging the map around. The default value of `0.0` allows the
 
12812         // user to drag outside the bounds at normal speed, higher values will
 
12813         // slow down map dragging outside bounds, and `1.0` makes the bounds fully
 
12814         // solid, preventing the user from dragging outside the bounds.
 
12815         maxBoundsViscosity: 0.0
 
12818 var Drag = Handler.extend({
 
12819         addHooks: function () {
 
12820                 if (!this._draggable) {
 
12821                         var map = this._map;
 
12823                         this._draggable = new Draggable(map._mapPane, map._container);
 
12825                         this._draggable.on({
 
12826                                 dragstart: this._onDragStart,
 
12827                                 drag: this._onDrag,
 
12828                                 dragend: this._onDragEnd
 
12831                         this._draggable.on('predrag', this._onPreDragLimit, this);
 
12832                         if (map.options.worldCopyJump) {
 
12833                                 this._draggable.on('predrag', this._onPreDragWrap, this);
 
12834                                 map.on('zoomend', this._onZoomEnd, this);
 
12836                                 map.whenReady(this._onZoomEnd, this);
 
12839                 addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
 
12840                 this._draggable.enable();
 
12841                 this._positions = [];
 
12845         removeHooks: function () {
 
12846                 removeClass(this._map._container, 'leaflet-grab');
 
12847                 removeClass(this._map._container, 'leaflet-touch-drag');
 
12848                 this._draggable.disable();
 
12851         moved: function () {
 
12852                 return this._draggable && this._draggable._moved;
 
12855         moving: function () {
 
12856                 return this._draggable && this._draggable._moving;
 
12859         _onDragStart: function () {
 
12860                 var map = this._map;
 
12863                 if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
 
12864                         var bounds = toLatLngBounds(this._map.options.maxBounds);
 
12866                         this._offsetLimit = toBounds(
 
12867                                 this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
 
12868                                 this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
 
12869                                         .add(this._map.getSize()));
 
12871                         this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
 
12873                         this._offsetLimit = null;
 
12878                     .fire('dragstart');
 
12880                 if (map.options.inertia) {
 
12881                         this._positions = [];
 
12886         _onDrag: function (e) {
 
12887                 if (this._map.options.inertia) {
 
12888                         var time = this._lastTime = +new Date(),
 
12889                             pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
 
12891                         this._positions.push(pos);
 
12892                         this._times.push(time);
 
12894                         if (time - this._times[0] > 50) {
 
12895                                 this._positions.shift();
 
12896                                 this._times.shift();
 
12905         _onZoomEnd: function () {
 
12906                 var pxCenter = this._map.getSize().divideBy(2),
 
12907                     pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
 
12909                 this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
 
12910                 this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
 
12913         _viscousLimit: function (value, threshold) {
 
12914                 return value - (value - threshold) * this._viscosity;
 
12917         _onPreDragLimit: function () {
 
12918                 if (!this._viscosity || !this._offsetLimit) { return; }
 
12920                 var offset = this._draggable._newPos.subtract(this._draggable._startPos);
 
12922                 var limit = this._offsetLimit;
 
12923                 if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
 
12924                 if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
 
12925                 if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
 
12926                 if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
 
12928                 this._draggable._newPos = this._draggable._startPos.add(offset);
 
12931         _onPreDragWrap: function () {
 
12932                 // TODO refactor to be able to adjust map pane position after zoom
 
12933                 var worldWidth = this._worldWidth,
 
12934                     halfWidth = Math.round(worldWidth / 2),
 
12935                     dx = this._initialWorldOffset,
 
12936                     x = this._draggable._newPos.x,
 
12937                     newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
 
12938                     newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
 
12939                     newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
 
12941                 this._draggable._absPos = this._draggable._newPos.clone();
 
12942                 this._draggable._newPos.x = newX;
 
12945         _onDragEnd: function (e) {
 
12946                 var map = this._map,
 
12947                     options = map.options,
 
12949                     noInertia = !options.inertia || this._times.length < 2;
 
12951                 map.fire('dragend', e);
 
12954                         map.fire('moveend');
 
12958                         var direction = this._lastPos.subtract(this._positions[0]),
 
12959                             duration = (this._lastTime - this._times[0]) / 1000,
 
12960                             ease = options.easeLinearity,
 
12962                             speedVector = direction.multiplyBy(ease / duration),
 
12963                             speed = speedVector.distanceTo([0, 0]),
 
12965                             limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
 
12966                             limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
 
12968                             decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
 
12969                             offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
 
12971                         if (!offset.x && !offset.y) {
 
12972                                 map.fire('moveend');
 
12975                                 offset = map._limitOffset(offset, map.options.maxBounds);
 
12977                                 requestAnimFrame(function () {
 
12978                                         map.panBy(offset, {
 
12979                                                 duration: decelerationDuration,
 
12980                                                 easeLinearity: ease,
 
12990 // @section Handlers
 
12991 // @property dragging: Handler
 
12992 // Map dragging handler (by both mouse and touch).
 
12993 Map.addInitHook('addHandler', 'dragging', Drag);
 
12996  * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
 
13000 // @section Keyboard Navigation Options
 
13002         // @option keyboard: Boolean = true
 
13003         // Makes the map focusable and allows users to navigate the map with keyboard
 
13004         // arrows and `+`/`-` keys.
 
13007         // @option keyboardPanDelta: Number = 80
 
13008         // Amount of pixels to pan when pressing an arrow key.
 
13009         keyboardPanDelta: 80
 
13012 var Keyboard = Handler.extend({
 
13019                 zoomIn:  [187, 107, 61, 171],
 
13020                 zoomOut: [189, 109, 54, 173]
 
13023         initialize: function (map) {
 
13026                 this._setPanDelta(map.options.keyboardPanDelta);
 
13027                 this._setZoomDelta(map.options.zoomDelta);
 
13030         addHooks: function () {
 
13031                 var container = this._map._container;
 
13033                 // make the container focusable by tabbing
 
13034                 if (container.tabIndex <= 0) {
 
13035                         container.tabIndex = '0';
 
13039                         focus: this._onFocus,
 
13040                         blur: this._onBlur,
 
13041                         mousedown: this._onMouseDown
 
13045                         focus: this._addHooks,
 
13046                         blur: this._removeHooks
 
13050         removeHooks: function () {
 
13051                 this._removeHooks();
 
13053                 off(this._map._container, {
 
13054                         focus: this._onFocus,
 
13055                         blur: this._onBlur,
 
13056                         mousedown: this._onMouseDown
 
13060                         focus: this._addHooks,
 
13061                         blur: this._removeHooks
 
13065         _onMouseDown: function () {
 
13066                 if (this._focused) { return; }
 
13068                 var body = document.body,
 
13069                     docEl = document.documentElement,
 
13070                     top = body.scrollTop || docEl.scrollTop,
 
13071                     left = body.scrollLeft || docEl.scrollLeft;
 
13073                 this._map._container.focus();
 
13075                 window.scrollTo(left, top);
 
13078         _onFocus: function () {
 
13079                 this._focused = true;
 
13080                 this._map.fire('focus');
 
13083         _onBlur: function () {
 
13084                 this._focused = false;
 
13085                 this._map.fire('blur');
 
13088         _setPanDelta: function (panDelta) {
 
13089                 var keys = this._panKeys = {},
 
13090                     codes = this.keyCodes,
 
13093                 for (i = 0, len = codes.left.length; i < len; i++) {
 
13094                         keys[codes.left[i]] = [-1 * panDelta, 0];
 
13096                 for (i = 0, len = codes.right.length; i < len; i++) {
 
13097                         keys[codes.right[i]] = [panDelta, 0];
 
13099                 for (i = 0, len = codes.down.length; i < len; i++) {
 
13100                         keys[codes.down[i]] = [0, panDelta];
 
13102                 for (i = 0, len = codes.up.length; i < len; i++) {
 
13103                         keys[codes.up[i]] = [0, -1 * panDelta];
 
13107         _setZoomDelta: function (zoomDelta) {
 
13108                 var keys = this._zoomKeys = {},
 
13109                     codes = this.keyCodes,
 
13112                 for (i = 0, len = codes.zoomIn.length; i < len; i++) {
 
13113                         keys[codes.zoomIn[i]] = zoomDelta;
 
13115                 for (i = 0, len = codes.zoomOut.length; i < len; i++) {
 
13116                         keys[codes.zoomOut[i]] = -zoomDelta;
 
13120         _addHooks: function () {
 
13121                 on(document, 'keydown', this._onKeyDown, this);
 
13124         _removeHooks: function () {
 
13125                 off(document, 'keydown', this._onKeyDown, this);
 
13128         _onKeyDown: function (e) {
 
13129                 if (e.altKey || e.ctrlKey || e.metaKey) { return; }
 
13131                 var key = e.keyCode,
 
13135                 if (key in this._panKeys) {
 
13137                         if (map._panAnim && map._panAnim._inProgress) { return; }
 
13139                         offset = this._panKeys[key];
 
13141                                 offset = toPoint(offset).multiplyBy(3);
 
13146                         if (map.options.maxBounds) {
 
13147                                 map.panInsideBounds(map.options.maxBounds);
 
13150                 } else if (key in this._zoomKeys) {
 
13151                         map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
 
13153                 } else if (key === 27 && map._popup) {
 
13164 // @section Handlers
 
13165 // @section Handlers
 
13166 // @property keyboard: Handler
 
13167 // Keyboard navigation handler.
 
13168 Map.addInitHook('addHandler', 'keyboard', Keyboard);
 
13171  * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
 
13175 // @section Interaction Options
 
13177         // @section Mousewheel options
 
13178         // @option scrollWheelZoom: Boolean|String = true
 
13179         // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
 
13180         // it will zoom to the center of the view regardless of where the mouse was.
 
13181         scrollWheelZoom: true,
 
13183         // @option wheelDebounceTime: Number = 40
 
13184         // Limits the rate at which a wheel can fire (in milliseconds). By default
 
13185         // user can't zoom via wheel more often than once per 40 ms.
 
13186         wheelDebounceTime: 40,
 
13188         // @option wheelPxPerZoomLevel: Number = 60
 
13189         // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
 
13190         // mean a change of one full zoom level. Smaller values will make wheel-zooming
 
13191         // faster (and vice versa).
 
13192         wheelPxPerZoomLevel: 60
 
13195 var ScrollWheelZoom = Handler.extend({
 
13196         addHooks: function () {
 
13197                 on(this._map._container, 'mousewheel', this._onWheelScroll, this);
 
13202         removeHooks: function () {
 
13203                 off(this._map._container, 'mousewheel', this._onWheelScroll, this);
 
13206         _onWheelScroll: function (e) {
 
13207                 var delta = getWheelDelta(e);
 
13209                 var debounce = this._map.options.wheelDebounceTime;
 
13211                 this._delta += delta;
 
13212                 this._lastMousePos = this._map.mouseEventToContainerPoint(e);
 
13214                 if (!this._startTime) {
 
13215                         this._startTime = +new Date();
 
13218                 var left = Math.max(debounce - (+new Date() - this._startTime), 0);
 
13220                 clearTimeout(this._timer);
 
13221                 this._timer = setTimeout(bind(this._performZoom, this), left);
 
13226         _performZoom: function () {
 
13227                 var map = this._map,
 
13228                     zoom = map.getZoom(),
 
13229                     snap = this._map.options.zoomSnap || 0;
 
13231                 map._stop(); // stop panning and fly animations if any
 
13233                 // map the delta with a sigmoid function to -4..4 range leaning on -1..1
 
13234                 var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
 
13235                     d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
 
13236                     d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
 
13237                     delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
 
13240                 this._startTime = null;
 
13242                 if (!delta) { return; }
 
13244                 if (map.options.scrollWheelZoom === 'center') {
 
13245                         map.setZoom(zoom + delta);
 
13247                         map.setZoomAround(this._lastMousePos, zoom + delta);
 
13252 // @section Handlers
 
13253 // @property scrollWheelZoom: Handler
 
13254 // Scroll wheel zoom handler.
 
13255 Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
 
13258  * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
 
13262 // @section Interaction Options
 
13264         // @section Touch interaction options
 
13265         // @option tap: Boolean = true
 
13266         // Enables mobile hacks for supporting instant taps (fixing 200ms click
 
13267         // delay on iOS/Android) and touch holds (fired as `contextmenu` events).
 
13270         // @option tapTolerance: Number = 15
 
13271         // The max number of pixels a user can shift his finger during touch
 
13272         // for it to be considered a valid tap.
 
13276 var Tap = Handler.extend({
 
13277         addHooks: function () {
 
13278                 on(this._map._container, 'touchstart', this._onDown, this);
 
13281         removeHooks: function () {
 
13282                 off(this._map._container, 'touchstart', this._onDown, this);
 
13285         _onDown: function (e) {
 
13286                 if (!e.touches) { return; }
 
13290                 this._fireClick = true;
 
13292                 // don't simulate click or track longpress if more than 1 touch
 
13293                 if (e.touches.length > 1) {
 
13294                         this._fireClick = false;
 
13295                         clearTimeout(this._holdTimeout);
 
13299                 var first = e.touches[0],
 
13302                 this._startPos = this._newPos = new Point(first.clientX, first.clientY);
 
13304                 // if touching a link, highlight it
 
13305                 if (el.tagName && el.tagName.toLowerCase() === 'a') {
 
13306                         addClass(el, 'leaflet-active');
 
13309                 // simulate long hold but setting a timeout
 
13310                 this._holdTimeout = setTimeout(bind(function () {
 
13311                         if (this._isTapValid()) {
 
13312                                 this._fireClick = false;
 
13314                                 this._simulateEvent('contextmenu', first);
 
13318                 this._simulateEvent('mousedown', first);
 
13321                         touchmove: this._onMove,
 
13322                         touchend: this._onUp
 
13326         _onUp: function (e) {
 
13327                 clearTimeout(this._holdTimeout);
 
13330                         touchmove: this._onMove,
 
13331                         touchend: this._onUp
 
13334                 if (this._fireClick && e && e.changedTouches) {
 
13336                         var first = e.changedTouches[0],
 
13339                         if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
 
13340                                 removeClass(el, 'leaflet-active');
 
13343                         this._simulateEvent('mouseup', first);
 
13345                         // simulate click if the touch didn't move too much
 
13346                         if (this._isTapValid()) {
 
13347                                 this._simulateEvent('click', first);
 
13352         _isTapValid: function () {
 
13353                 return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
 
13356         _onMove: function (e) {
 
13357                 var first = e.touches[0];
 
13358                 this._newPos = new Point(first.clientX, first.clientY);
 
13359                 this._simulateEvent('mousemove', first);
 
13362         _simulateEvent: function (type, e) {
 
13363                 var simulatedEvent = document.createEvent('MouseEvents');
 
13365                 simulatedEvent._simulated = true;
 
13366                 e.target._simulatedClick = true;
 
13368                 simulatedEvent.initMouseEvent(
 
13369                         type, true, true, window, 1,
 
13370                         e.screenX, e.screenY,
 
13371                         e.clientX, e.clientY,
 
13372                         false, false, false, false, 0, null);
 
13374                 e.target.dispatchEvent(simulatedEvent);
 
13378 // @section Handlers
 
13379 // @property tap: Handler
 
13380 // Mobile touch hacks (quick tap and touch hold) handler.
 
13381 if (touch && !pointer) {
 
13382         Map.addInitHook('addHandler', 'tap', Tap);
 
13386  * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
 
13390 // @section Interaction Options
 
13392         // @section Touch interaction options
 
13393         // @option touchZoom: Boolean|String = *
 
13394         // Whether the map can be zoomed by touch-dragging with two fingers. If
 
13395         // passed `'center'`, it will zoom to the center of the view regardless of
 
13396         // where the touch events (fingers) were. Enabled for touch-capable web
 
13397         // browsers except for old Androids.
 
13398         touchZoom: touch && !android23,
 
13400         // @option bounceAtZoomLimits: Boolean = true
 
13401         // Set it to false if you don't want the map to zoom beyond min/max zoom
 
13402         // and then bounce back when pinch-zooming.
 
13403         bounceAtZoomLimits: true
 
13406 var TouchZoom = Handler.extend({
 
13407         addHooks: function () {
 
13408                 addClass(this._map._container, 'leaflet-touch-zoom');
 
13409                 on(this._map._container, 'touchstart', this._onTouchStart, this);
 
13412         removeHooks: function () {
 
13413                 removeClass(this._map._container, 'leaflet-touch-zoom');
 
13414                 off(this._map._container, 'touchstart', this._onTouchStart, this);
 
13417         _onTouchStart: function (e) {
 
13418                 var map = this._map;
 
13419                 if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
 
13421                 var p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
13422                     p2 = map.mouseEventToContainerPoint(e.touches[1]);
 
13424                 this._centerPoint = map.getSize()._divideBy(2);
 
13425                 this._startLatLng = map.containerPointToLatLng(this._centerPoint);
 
13426                 if (map.options.touchZoom !== 'center') {
 
13427                         this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
 
13430                 this._startDist = p1.distanceTo(p2);
 
13431                 this._startZoom = map.getZoom();
 
13433                 this._moved = false;
 
13434                 this._zooming = true;
 
13438                 on(document, 'touchmove', this._onTouchMove, this);
 
13439                 on(document, 'touchend', this._onTouchEnd, this);
 
13444         _onTouchMove: function (e) {
 
13445                 if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
 
13447                 var map = this._map,
 
13448                     p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
13449                     p2 = map.mouseEventToContainerPoint(e.touches[1]),
 
13450                     scale = p1.distanceTo(p2) / this._startDist;
 
13452                 this._zoom = map.getScaleZoom(scale, this._startZoom);
 
13454                 if (!map.options.bounceAtZoomLimits && (
 
13455                         (this._zoom < map.getMinZoom() && scale < 1) ||
 
13456                         (this._zoom > map.getMaxZoom() && scale > 1))) {
 
13457                         this._zoom = map._limitZoom(this._zoom);
 
13460                 if (map.options.touchZoom === 'center') {
 
13461                         this._center = this._startLatLng;
 
13462                         if (scale === 1) { return; }
 
13464                         // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
 
13465                         var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
 
13466                         if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
 
13467                         this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
 
13470                 if (!this._moved) {
 
13471                         map._moveStart(true);
 
13472                         this._moved = true;
 
13475                 cancelAnimFrame(this._animRequest);
 
13477                 var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
 
13478                 this._animRequest = requestAnimFrame(moveFn, this, true);
 
13483         _onTouchEnd: function () {
 
13484                 if (!this._moved || !this._zooming) {
 
13485                         this._zooming = false;
 
13489                 this._zooming = false;
 
13490                 cancelAnimFrame(this._animRequest);
 
13492                 off(document, 'touchmove', this._onTouchMove);
 
13493                 off(document, 'touchend', this._onTouchEnd);
 
13495                 // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
 
13496                 if (this._map.options.zoomAnimation) {
 
13497                         this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
 
13499                         this._map._resetView(this._center, this._map._limitZoom(this._zoom));
 
13504 // @section Handlers
 
13505 // @property touchZoom: Handler
 
13506 // Touch zoom handler.
 
13507 Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
 
13509 Map.BoxZoom = BoxZoom;
 
13510 Map.DoubleClickZoom = DoubleClickZoom;
 
13512 Map.Keyboard = Keyboard;
 
13513 Map.ScrollWheelZoom = ScrollWheelZoom;
 
13515 Map.TouchZoom = TouchZoom;
 
13519 var oldL = window.L;
 
13520 function noConflict() {
 
13525 // Always export us to window global (see #2364)
 
13526 window.L = exports;
 
13528 Object.freeze = freeze;
 
13530 exports.version = version;
 
13531 exports.noConflict = noConflict;
 
13532 exports.Control = Control;
 
13533 exports.control = control;
 
13534 exports.Browser = Browser;
 
13535 exports.Evented = Evented;
 
13536 exports.Mixin = Mixin;
 
13537 exports.Util = Util;
 
13538 exports.Class = Class;
 
13539 exports.Handler = Handler;
 
13540 exports.extend = extend;
 
13541 exports.bind = bind;
 
13542 exports.stamp = stamp;
 
13543 exports.setOptions = setOptions;
 
13544 exports.DomEvent = DomEvent;
 
13545 exports.DomUtil = DomUtil;
 
13546 exports.PosAnimation = PosAnimation;
 
13547 exports.Draggable = Draggable;
 
13548 exports.LineUtil = LineUtil;
 
13549 exports.PolyUtil = PolyUtil;
 
13550 exports.Point = Point;
 
13551 exports.point = toPoint;
 
13552 exports.Bounds = Bounds;
 
13553 exports.bounds = toBounds;
 
13554 exports.Transformation = Transformation;
 
13555 exports.transformation = toTransformation;
 
13556 exports.Projection = index;
 
13557 exports.LatLng = LatLng;
 
13558 exports.latLng = toLatLng;
 
13559 exports.LatLngBounds = LatLngBounds;
 
13560 exports.latLngBounds = toLatLngBounds;
 
13562 exports.GeoJSON = GeoJSON;
 
13563 exports.geoJSON = geoJSON;
 
13564 exports.geoJson = geoJson;
 
13565 exports.Layer = Layer;
 
13566 exports.LayerGroup = LayerGroup;
 
13567 exports.layerGroup = layerGroup;
 
13568 exports.FeatureGroup = FeatureGroup;
 
13569 exports.featureGroup = featureGroup;
 
13570 exports.ImageOverlay = ImageOverlay;
 
13571 exports.imageOverlay = imageOverlay;
 
13572 exports.VideoOverlay = VideoOverlay;
 
13573 exports.videoOverlay = videoOverlay;
 
13574 exports.DivOverlay = DivOverlay;
 
13575 exports.Popup = Popup;
 
13576 exports.popup = popup;
 
13577 exports.Tooltip = Tooltip;
 
13578 exports.tooltip = tooltip;
 
13579 exports.Icon = Icon;
 
13580 exports.icon = icon;
 
13581 exports.DivIcon = DivIcon;
 
13582 exports.divIcon = divIcon;
 
13583 exports.Marker = Marker;
 
13584 exports.marker = marker;
 
13585 exports.TileLayer = TileLayer;
 
13586 exports.tileLayer = tileLayer;
 
13587 exports.GridLayer = GridLayer;
 
13588 exports.gridLayer = gridLayer;
 
13590 exports.svg = svg$1;
 
13591 exports.Renderer = Renderer;
 
13592 exports.Canvas = Canvas;
 
13593 exports.canvas = canvas$1;
 
13594 exports.Path = Path;
 
13595 exports.CircleMarker = CircleMarker;
 
13596 exports.circleMarker = circleMarker;
 
13597 exports.Circle = Circle;
 
13598 exports.circle = circle;
 
13599 exports.Polyline = Polyline;
 
13600 exports.polyline = polyline;
 
13601 exports.Polygon = Polygon;
 
13602 exports.polygon = polygon;
 
13603 exports.Rectangle = Rectangle;
 
13604 exports.rectangle = rectangle;
 
13606 exports.map = createMap;
 
13609 //# sourceMappingURL=leaflet-src.js.map