2  * Leaflet 1.3.4, a JS library for interactive maps. http://leafletjs.com
 
   3  * (c) 2010-2018 Vladimir Agafonkin, (c) 2010-2011 CloudMade
 
   6 (function (global, factory) {
 
   7         typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
 
   8         typeof define === 'function' && define.amd ? define(['exports'], factory) :
 
   9         (factory((global.L = {})));
 
  10 }(this, (function (exports) { 'use strict';
 
  12 var version = "1.3.4";
 
  17  * Various utility functions, used by Leaflet internally.
 
  20 var freeze = Object.freeze;
 
  21 Object.freeze = function (obj) { return obj; };
 
  23 // @function extend(dest: Object, src?: Object): Object
 
  24 // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
 
  25 function extend(dest) {
 
  28         for (j = 1, len = arguments.length; j < len; j++) {
 
  37 // @function create(proto: Object, properties?: Object): Object
 
  38 // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
 
  39 var create = Object.create || (function () {
 
  41         return function (proto) {
 
  47 // @function bind(fn: Function, …): Function
 
  48 // Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
 
  49 // Has a `L.bind()` shortcut.
 
  50 function bind(fn, obj) {
 
  51         var slice = Array.prototype.slice;
 
  54                 return fn.bind.apply(fn, slice.call(arguments, 1));
 
  57         var args = slice.call(arguments, 2);
 
  60                 return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
 
  64 // @property lastId: Number
 
  65 // Last unique ID used by [`stamp()`](#util-stamp)
 
  68 // @function stamp(obj: Object): Number
 
  69 // Returns the unique ID of an object, assigning it one if it doesn't have it.
 
  72         obj._leaflet_id = obj._leaflet_id || ++lastId;
 
  73         return obj._leaflet_id;
 
  77 // @function throttle(fn: Function, time: Number, context: Object): Function
 
  78 // Returns a function which executes function `fn` with the given scope `context`
 
  79 // (so that the `this` keyword refers to `context` inside `fn`'s code). The function
 
  80 // `fn` will be called no more than one time per given amount of `time`. The arguments
 
  81 // received by the bound function will be any arguments passed when binding the
 
  82 // function, followed by any arguments passed when invoking the bound function.
 
  83 // Has an `L.throttle` shortcut.
 
  84 function throttle(fn, time, context) {
 
  85         var lock, args, wrapperFn, later;
 
  88                 // reset lock and call if queued
 
  91                         wrapperFn.apply(context, args);
 
  96         wrapperFn = function () {
 
  98                         // called too soon, queue to call later
 
 102                         // call and lock until later
 
 103                         fn.apply(context, arguments);
 
 104                         setTimeout(later, time);
 
 112 // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
 
 113 // Returns the number `num` modulo `range` in such a way so it lies within
 
 114 // `range[0]` and `range[1]`. The returned value will be always smaller than
 
 115 // `range[1]` unless `includeMax` is set to `true`.
 
 116 function wrapNum(x, range, includeMax) {
 
 120         return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
 
 123 // @function falseFn(): Function
 
 124 // Returns a function which always returns `false`.
 
 125 function falseFn() { return false; }
 
 127 // @function formatNum(num: Number, digits?: Number): Number
 
 128 // Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default.
 
 129 function formatNum(num, digits) {
 
 130         var pow = Math.pow(10, (digits === undefined ? 6 : digits));
 
 131         return Math.round(num * pow) / pow;
 
 134 // @function trim(str: String): String
 
 135 // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
 
 137         return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
 
 140 // @function splitWords(str: String): String[]
 
 141 // Trims and splits the string on whitespace and returns the array of parts.
 
 142 function splitWords(str) {
 
 143         return trim(str).split(/\s+/);
 
 146 // @function setOptions(obj: Object, options: Object): Object
 
 147 // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
 
 148 function setOptions(obj, options) {
 
 149         if (!obj.hasOwnProperty('options')) {
 
 150                 obj.options = obj.options ? create(obj.options) : {};
 
 152         for (var i in options) {
 
 153                 obj.options[i] = options[i];
 
 158 // @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
 
 159 // Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
 
 160 // translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
 
 161 // be appended at the end. If `uppercase` is `true`, the parameter names will
 
 162 // be uppercased (e.g. `'?A=foo&B=bar'`)
 
 163 function getParamString(obj, existingUrl, uppercase) {
 
 166                 params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
 
 168         return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
 
 171 var templateRe = /\{ *([\w_-]+) *\}/g;
 
 173 // @function template(str: String, data: Object): String
 
 174 // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
 
 175 // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
 
 176 // `('Hello foo, bar')`. You can also specify functions instead of strings for
 
 177 // data values — they will be evaluated passing `data` as an argument.
 
 178 function template(str, data) {
 
 179         return str.replace(templateRe, function (str, key) {
 
 180                 var value = data[key];
 
 182                 if (value === undefined) {
 
 183                         throw new Error('No value provided for variable ' + str);
 
 185                 } else if (typeof value === 'function') {
 
 192 // @function isArray(obj): Boolean
 
 193 // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
 
 194 var isArray = Array.isArray || function (obj) {
 
 195         return (Object.prototype.toString.call(obj) === '[object Array]');
 
 198 // @function indexOf(array: Array, el: Object): Number
 
 199 // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
 
 200 function indexOf(array, el) {
 
 201         for (var i = 0; i < array.length; i++) {
 
 202                 if (array[i] === el) { return i; }
 
 207 // @property emptyImageUrl: String
 
 208 // Data URI string containing a base64-encoded empty GIF image.
 
 209 // Used as a hack to free memory from unused images on WebKit-powered
 
 210 // mobile devices (by setting image `src` to this string).
 
 211 var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
 
 213 // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
 
 215 function getPrefixed(name) {
 
 216         return window['webkit' + name] || window['moz' + name] || window['ms' + name];
 
 221 // fallback for IE 7-8
 
 222 function timeoutDefer(fn) {
 
 223         var time = +new Date(),
 
 224             timeToCall = Math.max(0, 16 - (time - lastTime));
 
 226         lastTime = time + timeToCall;
 
 227         return window.setTimeout(fn, timeToCall);
 
 230 var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer;
 
 231 var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
 
 232                 getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
 
 234 // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
 
 235 // Schedules `fn` to be executed when the browser repaints. `fn` is bound to
 
 236 // `context` if given. When `immediate` is set, `fn` is called immediately if
 
 237 // the browser doesn't have native support for
 
 238 // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
 
 239 // otherwise it's delayed. Returns a request ID that can be used to cancel the request.
 
 240 function requestAnimFrame(fn, context, immediate) {
 
 241         if (immediate && requestFn === timeoutDefer) {
 
 244                 return requestFn.call(window, bind(fn, context));
 
 248 // @function cancelAnimFrame(id: Number): undefined
 
 249 // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
 
 250 function cancelAnimFrame(id) {
 
 252                 cancelFn.call(window, id);
 
 257 var Util = (Object.freeze || Object)({
 
 267         formatNum: formatNum,
 
 269         splitWords: splitWords,
 
 270         setOptions: setOptions,
 
 271         getParamString: getParamString,
 
 275         emptyImageUrl: emptyImageUrl,
 
 276         requestFn: requestFn,
 
 278         requestAnimFrame: requestAnimFrame,
 
 279         cancelAnimFrame: cancelAnimFrame
 
 288 // Thanks to John Resig and Dean Edwards for inspiration!
 
 292 Class.extend = function (props) {
 
 294         // @function extend(props: Object): Function
 
 295         // [Extends the current class](#class-inheritance) given the properties to be included.
 
 296         // Returns a Javascript function that is a class constructor (to be called with `new`).
 
 297         var NewClass = function () {
 
 299                 // call the constructor
 
 300                 if (this.initialize) {
 
 301                         this.initialize.apply(this, arguments);
 
 304                 // call all constructor hooks
 
 305                 this.callInitHooks();
 
 308         var parentProto = NewClass.__super__ = this.prototype;
 
 310         var proto = create(parentProto);
 
 311         proto.constructor = NewClass;
 
 313         NewClass.prototype = proto;
 
 315         // inherit parent's statics
 
 316         for (var i in this) {
 
 317                 if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') {
 
 318                         NewClass[i] = this[i];
 
 322         // mix static properties into the class
 
 324                 extend(NewClass, props.statics);
 
 325                 delete props.statics;
 
 328         // mix includes into the prototype
 
 329         if (props.includes) {
 
 330                 checkDeprecatedMixinEvents(props.includes);
 
 331                 extend.apply(null, [proto].concat(props.includes));
 
 332                 delete props.includes;
 
 337                 props.options = extend(create(proto.options), props.options);
 
 340         // mix given properties into the prototype
 
 341         extend(proto, props);
 
 343         proto._initHooks = [];
 
 345         // add method for calling all hooks
 
 346         proto.callInitHooks = function () {
 
 348                 if (this._initHooksCalled) { return; }
 
 350                 if (parentProto.callInitHooks) {
 
 351                         parentProto.callInitHooks.call(this);
 
 354                 this._initHooksCalled = true;
 
 356                 for (var i = 0, len = proto._initHooks.length; i < len; i++) {
 
 357                         proto._initHooks[i].call(this);
 
 365 // @function include(properties: Object): this
 
 366 // [Includes a mixin](#class-includes) into the current class.
 
 367 Class.include = function (props) {
 
 368         extend(this.prototype, props);
 
 372 // @function mergeOptions(options: Object): this
 
 373 // [Merges `options`](#class-options) into the defaults of the class.
 
 374 Class.mergeOptions = function (options) {
 
 375         extend(this.prototype.options, options);
 
 379 // @function addInitHook(fn: Function): this
 
 380 // Adds a [constructor hook](#class-constructor-hooks) to the class.
 
 381 Class.addInitHook = function (fn) { // (Function) || (String, args...)
 
 382         var args = Array.prototype.slice.call(arguments, 1);
 
 384         var init = typeof fn === 'function' ? fn : function () {
 
 385                 this[fn].apply(this, args);
 
 388         this.prototype._initHooks = this.prototype._initHooks || [];
 
 389         this.prototype._initHooks.push(init);
 
 393 function checkDeprecatedMixinEvents(includes) {
 
 394         if (typeof L === 'undefined' || !L || !L.Mixin) { return; }
 
 396         includes = isArray(includes) ? includes : [includes];
 
 398         for (var i = 0; i < includes.length; i++) {
 
 399                 if (includes[i] === L.Mixin.Events) {
 
 400                         console.warn('Deprecated include of L.Mixin.Events: ' +
 
 401                                 'this property will be removed in future releases, ' +
 
 402                                 'please inherit from L.Evented instead.', new Error().stack);
 
 412  * A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event).
 
 417  * map.on('click', function(e) {
 
 422  * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
 
 425  * function onClick(e) { ... }
 
 427  * map.on('click', onClick);
 
 428  * map.off('click', onClick);
 
 433         /* @method on(type: String, fn: Function, context?: Object): this
 
 434          * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`).
 
 437          * @method on(eventMap: Object): this
 
 438          * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
 440         on: function (types, fn, context) {
 
 442                 // types can be a map of types/handlers
 
 443                 if (typeof types === 'object') {
 
 444                         for (var type in types) {
 
 445                                 // we don't process space-separated events here for performance;
 
 446                                 // it's a hot path since Layer uses the on(obj) syntax
 
 447                                 this._on(type, types[type], fn);
 
 451                         // types can be a string of space-separated words
 
 452                         types = splitWords(types);
 
 454                         for (var i = 0, len = types.length; i < len; i++) {
 
 455                                 this._on(types[i], fn, context);
 
 462         /* @method off(type: String, fn?: Function, context?: Object): this
 
 463          * Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener.
 
 466          * @method off(eventMap: Object): this
 
 467          * Removes a set of type/listener pairs.
 
 471          * Removes all listeners to all events on the object.
 
 473         off: function (types, fn, context) {
 
 476                         // clear all listeners if called without arguments
 
 479                 } else if (typeof types === 'object') {
 
 480                         for (var type in types) {
 
 481                                 this._off(type, types[type], fn);
 
 485                         types = splitWords(types);
 
 487                         for (var i = 0, len = types.length; i < len; i++) {
 
 488                                 this._off(types[i], fn, context);
 
 495         // attach listener (without syntactic sugar now)
 
 496         _on: function (type, fn, context) {
 
 497                 this._events = this._events || {};
 
 499                 /* get/init listeners for type */
 
 500                 var typeListeners = this._events[type];
 
 501                 if (!typeListeners) {
 
 503                         this._events[type] = typeListeners;
 
 506                 if (context === this) {
 
 507                         // Less memory footprint.
 
 510                 var newListener = {fn: fn, ctx: context},
 
 511                     listeners = typeListeners;
 
 513                 // check if fn already there
 
 514                 for (var i = 0, len = listeners.length; i < len; i++) {
 
 515                         if (listeners[i].fn === fn && listeners[i].ctx === context) {
 
 520                 listeners.push(newListener);
 
 523         _off: function (type, fn, context) {
 
 528                 if (!this._events) { return; }
 
 530                 listeners = this._events[type];
 
 537                         // Set all removed listeners to noop so they are not called if remove happens in fire
 
 538                         for (i = 0, len = listeners.length; i < len; i++) {
 
 539                                 listeners[i].fn = falseFn;
 
 541                         // clear all listeners for a type if function isn't specified
 
 542                         delete this._events[type];
 
 546                 if (context === this) {
 
 552                         // find fn and remove it
 
 553                         for (i = 0, len = listeners.length; i < len; i++) {
 
 554                                 var l = listeners[i];
 
 555                                 if (l.ctx !== context) { continue; }
 
 558                                         // set the removed listener to noop so that's not called if remove happens in fire
 
 561                                         if (this._firingCount) {
 
 562                                                 /* copy array in case events are being fired */
 
 563                                                 this._events[type] = listeners = listeners.slice();
 
 565                                         listeners.splice(i, 1);
 
 573         // @method fire(type: String, data?: Object, propagate?: Boolean): this
 
 574         // Fires an event of the specified type. You can optionally provide an data
 
 575         // object — the first argument of the listener function will contain its
 
 576         // properties. The event can optionally be propagated to event parents.
 
 577         fire: function (type, data, propagate) {
 
 578                 if (!this.listens(type, propagate)) { return this; }
 
 580                 var event = extend({}, data, {
 
 583                         sourceTarget: data && data.sourceTarget || this
 
 587                         var listeners = this._events[type];
 
 590                                 this._firingCount = (this._firingCount + 1) || 1;
 
 591                                 for (var i = 0, len = listeners.length; i < len; i++) {
 
 592                                         var l = listeners[i];
 
 593                                         l.fn.call(l.ctx || this, event);
 
 601                         // propagate the event to parents (set with addEventParent)
 
 602                         this._propagateEvent(event);
 
 608         // @method listens(type: String): Boolean
 
 609         // Returns `true` if a particular event type has any listeners attached to it.
 
 610         listens: function (type, propagate) {
 
 611                 var listeners = this._events && this._events[type];
 
 612                 if (listeners && listeners.length) { return true; }
 
 615                         // also check parents for listeners if event propagates
 
 616                         for (var id in this._eventParents) {
 
 617                                 if (this._eventParents[id].listens(type, propagate)) { return true; }
 
 623         // @method once(…): this
 
 624         // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
 
 625         once: function (types, fn, context) {
 
 627                 if (typeof types === 'object') {
 
 628                         for (var type in types) {
 
 629                                 this.once(type, types[type], fn);
 
 634                 var handler = bind(function () {
 
 636                             .off(types, fn, context)
 
 637                             .off(types, handler, context);
 
 640                 // add a listener that's executed once and removed after that
 
 642                     .on(types, fn, context)
 
 643                     .on(types, handler, context);
 
 646         // @method addEventParent(obj: Evented): this
 
 647         // Adds an event parent - an `Evented` that will receive propagated events
 
 648         addEventParent: function (obj) {
 
 649                 this._eventParents = this._eventParents || {};
 
 650                 this._eventParents[stamp(obj)] = obj;
 
 654         // @method removeEventParent(obj: Evented): this
 
 655         // Removes an event parent, so it will stop receiving propagated events
 
 656         removeEventParent: function (obj) {
 
 657                 if (this._eventParents) {
 
 658                         delete this._eventParents[stamp(obj)];
 
 663         _propagateEvent: function (e) {
 
 664                 for (var id in this._eventParents) {
 
 665                         this._eventParents[id].fire(e.type, extend({
 
 667                                 propagatedFrom: e.target
 
 673 // aliases; we should ditch those eventually
 
 675 // @method addEventListener(…): this
 
 676 // Alias to [`on(…)`](#evented-on)
 
 677 Events.addEventListener = Events.on;
 
 679 // @method removeEventListener(…): this
 
 680 // Alias to [`off(…)`](#evented-off)
 
 682 // @method clearAllEventListeners(…): this
 
 683 // Alias to [`off()`](#evented-off)
 
 684 Events.removeEventListener = Events.clearAllEventListeners = Events.off;
 
 686 // @method addOneTimeEventListener(…): this
 
 687 // Alias to [`once(…)`](#evented-once)
 
 688 Events.addOneTimeEventListener = Events.once;
 
 690 // @method fireEvent(…): this
 
 691 // Alias to [`fire(…)`](#evented-fire)
 
 692 Events.fireEvent = Events.fire;
 
 694 // @method hasEventListeners(…): Boolean
 
 695 // Alias to [`listens(…)`](#evented-listens)
 
 696 Events.hasEventListeners = Events.listens;
 
 698 var Evented = Class.extend(Events);
 
 704  * Represents a point with `x` and `y` coordinates in pixels.
 
 709  * var point = L.point(200, 300);
 
 712  * All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent:
 
 715  * map.panBy([200, 300]);
 
 716  * map.panBy(L.point(200, 300));
 
 719  * Note that `Point` does not inherit from Leafet's `Class` object,
 
 720  * which means new classes can't inherit from it, and new methods
 
 721  * can't be added to it with the `include` function.
 
 724 function Point(x, y, round) {
 
 725         // @property x: Number; The `x` coordinate of the point
 
 726         this.x = (round ? Math.round(x) : x);
 
 727         // @property y: Number; The `y` coordinate of the point
 
 728         this.y = (round ? Math.round(y) : y);
 
 731 var trunc = Math.trunc || function (v) {
 
 732         return v > 0 ? Math.floor(v) : Math.ceil(v);
 
 737         // @method clone(): Point
 
 738         // Returns a copy of the current point.
 
 740                 return new Point(this.x, this.y);
 
 743         // @method add(otherPoint: Point): Point
 
 744         // Returns the result of addition of the current and the given points.
 
 745         add: function (point) {
 
 746                 // non-destructive, returns a new point
 
 747                 return this.clone()._add(toPoint(point));
 
 750         _add: function (point) {
 
 751                 // destructive, used directly for performance in situations where it's safe to modify existing point
 
 757         // @method subtract(otherPoint: Point): Point
 
 758         // Returns the result of subtraction of the given point from the current.
 
 759         subtract: function (point) {
 
 760                 return this.clone()._subtract(toPoint(point));
 
 763         _subtract: function (point) {
 
 769         // @method divideBy(num: Number): Point
 
 770         // Returns the result of division of the current point by the given number.
 
 771         divideBy: function (num) {
 
 772                 return this.clone()._divideBy(num);
 
 775         _divideBy: function (num) {
 
 781         // @method multiplyBy(num: Number): Point
 
 782         // Returns the result of multiplication of the current point by the given number.
 
 783         multiplyBy: function (num) {
 
 784                 return this.clone()._multiplyBy(num);
 
 787         _multiplyBy: function (num) {
 
 793         // @method scaleBy(scale: Point): Point
 
 794         // Multiply each coordinate of the current point by each coordinate of
 
 795         // `scale`. In linear algebra terms, multiply the point by the
 
 796         // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
 
 797         // defined by `scale`.
 
 798         scaleBy: function (point) {
 
 799                 return new Point(this.x * point.x, this.y * point.y);
 
 802         // @method unscaleBy(scale: Point): Point
 
 803         // Inverse of `scaleBy`. Divide each coordinate of the current point by
 
 804         // each coordinate of `scale`.
 
 805         unscaleBy: function (point) {
 
 806                 return new Point(this.x / point.x, this.y / point.y);
 
 809         // @method round(): Point
 
 810         // Returns a copy of the current point with rounded coordinates.
 
 812                 return this.clone()._round();
 
 815         _round: function () {
 
 816                 this.x = Math.round(this.x);
 
 817                 this.y = Math.round(this.y);
 
 821         // @method floor(): Point
 
 822         // Returns a copy of the current point with floored coordinates (rounded down).
 
 824                 return this.clone()._floor();
 
 827         _floor: function () {
 
 828                 this.x = Math.floor(this.x);
 
 829                 this.y = Math.floor(this.y);
 
 833         // @method ceil(): Point
 
 834         // Returns a copy of the current point with ceiled coordinates (rounded up).
 
 836                 return this.clone()._ceil();
 
 840                 this.x = Math.ceil(this.x);
 
 841                 this.y = Math.ceil(this.y);
 
 845         // @method trunc(): Point
 
 846         // Returns a copy of the current point with truncated coordinates (rounded towards zero).
 
 848                 return this.clone()._trunc();
 
 851         _trunc: function () {
 
 852                 this.x = trunc(this.x);
 
 853                 this.y = trunc(this.y);
 
 857         // @method distanceTo(otherPoint: Point): Number
 
 858         // Returns the cartesian distance between the current and the given points.
 
 859         distanceTo: function (point) {
 
 860                 point = toPoint(point);
 
 862                 var x = point.x - this.x,
 
 863                     y = point.y - this.y;
 
 865                 return Math.sqrt(x * x + y * y);
 
 868         // @method equals(otherPoint: Point): Boolean
 
 869         // Returns `true` if the given point has the same coordinates.
 
 870         equals: function (point) {
 
 871                 point = toPoint(point);
 
 873                 return point.x === this.x &&
 
 877         // @method contains(otherPoint: Point): Boolean
 
 878         // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
 
 879         contains: function (point) {
 
 880                 point = toPoint(point);
 
 882                 return Math.abs(point.x) <= Math.abs(this.x) &&
 
 883                        Math.abs(point.y) <= Math.abs(this.y);
 
 886         // @method toString(): String
 
 887         // Returns a string representation of the point for debugging purposes.
 
 888         toString: function () {
 
 890                         formatNum(this.x) + ', ' +
 
 891                         formatNum(this.y) + ')';
 
 895 // @factory L.point(x: Number, y: Number, round?: Boolean)
 
 896 // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
 
 899 // @factory L.point(coords: Number[])
 
 900 // Expects an array of the form `[x, y]` instead.
 
 903 // @factory L.point(coords: Object)
 
 904 // Expects a plain object of the form `{x: Number, y: Number}` instead.
 
 905 function toPoint(x, y, round) {
 
 906         if (x instanceof Point) {
 
 910                 return new Point(x[0], x[1]);
 
 912         if (x === undefined || x === null) {
 
 915         if (typeof x === 'object' && 'x' in x && 'y' in x) {
 
 916                 return new Point(x.x, x.y);
 
 918         return new Point(x, y, round);
 
 925  * Represents a rectangular area in pixel coordinates.
 
 930  * var p1 = L.point(10, 10),
 
 931  * p2 = L.point(40, 60),
 
 932  * bounds = L.bounds(p1, p2);
 
 935  * All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
 
 938  * otherBounds.intersects([[10, 10], [40, 60]]);
 
 941  * Note that `Bounds` does not inherit from Leafet's `Class` object,
 
 942  * which means new classes can't inherit from it, and new methods
 
 943  * can't be added to it with the `include` function.
 
 946 function Bounds(a, b) {
 
 949         var points = b ? [a, b] : a;
 
 951         for (var i = 0, len = points.length; i < len; i++) {
 
 952                 this.extend(points[i]);
 
 957         // @method extend(point: Point): this
 
 958         // Extends the bounds to contain the given point.
 
 959         extend: function (point) { // (Point)
 
 960                 point = toPoint(point);
 
 962                 // @property min: Point
 
 963                 // The top left corner of the rectangle.
 
 964                 // @property max: Point
 
 965                 // The bottom right corner of the rectangle.
 
 966                 if (!this.min && !this.max) {
 
 967                         this.min = point.clone();
 
 968                         this.max = point.clone();
 
 970                         this.min.x = Math.min(point.x, this.min.x);
 
 971                         this.max.x = Math.max(point.x, this.max.x);
 
 972                         this.min.y = Math.min(point.y, this.min.y);
 
 973                         this.max.y = Math.max(point.y, this.max.y);
 
 978         // @method getCenter(round?: Boolean): Point
 
 979         // Returns the center point of the bounds.
 
 980         getCenter: function (round) {
 
 982                         (this.min.x + this.max.x) / 2,
 
 983                         (this.min.y + this.max.y) / 2, round);
 
 986         // @method getBottomLeft(): Point
 
 987         // Returns the bottom-left point of the bounds.
 
 988         getBottomLeft: function () {
 
 989                 return new Point(this.min.x, this.max.y);
 
 992         // @method getTopRight(): Point
 
 993         // Returns the top-right point of the bounds.
 
 994         getTopRight: function () { // -> Point
 
 995                 return new Point(this.max.x, this.min.y);
 
 998         // @method getTopLeft(): Point
 
 999         // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)).
 
1000         getTopLeft: function () {
 
1001                 return this.min; // left, top
 
1004         // @method getBottomRight(): Point
 
1005         // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)).
 
1006         getBottomRight: function () {
 
1007                 return this.max; // right, bottom
 
1010         // @method getSize(): Point
 
1011         // Returns the size of the given bounds
 
1012         getSize: function () {
 
1013                 return this.max.subtract(this.min);
 
1016         // @method contains(otherBounds: Bounds): Boolean
 
1017         // Returns `true` if the rectangle contains the given one.
 
1019         // @method contains(point: Point): Boolean
 
1020         // Returns `true` if the rectangle contains the given point.
 
1021         contains: function (obj) {
 
1024                 if (typeof obj[0] === 'number' || obj instanceof Point) {
 
1027                         obj = toBounds(obj);
 
1030                 if (obj instanceof Bounds) {
 
1037                 return (min.x >= this.min.x) &&
 
1038                        (max.x <= this.max.x) &&
 
1039                        (min.y >= this.min.y) &&
 
1040                        (max.y <= this.max.y);
 
1043         // @method intersects(otherBounds: Bounds): Boolean
 
1044         // Returns `true` if the rectangle intersects the given bounds. Two bounds
 
1045         // intersect if they have at least one point in common.
 
1046         intersects: function (bounds) { // (Bounds) -> Boolean
 
1047                 bounds = toBounds(bounds);
 
1053                     xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
 
1054                     yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
 
1056                 return xIntersects && yIntersects;
 
1059         // @method overlaps(otherBounds: Bounds): Boolean
 
1060         // Returns `true` if the rectangle overlaps the given bounds. Two bounds
 
1061         // overlap if their intersection is an area.
 
1062         overlaps: function (bounds) { // (Bounds) -> Boolean
 
1063                 bounds = toBounds(bounds);
 
1069                     xOverlaps = (max2.x > min.x) && (min2.x < max.x),
 
1070                     yOverlaps = (max2.y > min.y) && (min2.y < max.y);
 
1072                 return xOverlaps && yOverlaps;
 
1075         isValid: function () {
 
1076                 return !!(this.min && this.max);
 
1081 // @factory L.bounds(corner1: Point, corner2: Point)
 
1082 // Creates a Bounds object from two corners coordinate pairs.
 
1084 // @factory L.bounds(points: Point[])
 
1085 // Creates a Bounds object from the given array of points.
 
1086 function toBounds(a, b) {
 
1087         if (!a || a instanceof Bounds) {
 
1090         return new Bounds(a, b);
 
1094  * @class LatLngBounds
 
1095  * @aka L.LatLngBounds
 
1097  * Represents a rectangular geographical area on a map.
 
1102  * var corner1 = L.latLng(40.712, -74.227),
 
1103  * corner2 = L.latLng(40.774, -74.125),
 
1104  * bounds = L.latLngBounds(corner1, corner2);
 
1107  * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
 
1111  *      [40.712, -74.227],
 
1116  * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range.
 
1118  * Note that `LatLngBounds` does not inherit from Leafet's `Class` object,
 
1119  * which means new classes can't inherit from it, and new methods
 
1120  * can't be added to it with the `include` function.
 
1123 function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
 
1124         if (!corner1) { return; }
 
1126         var latlngs = corner2 ? [corner1, corner2] : corner1;
 
1128         for (var i = 0, len = latlngs.length; i < len; i++) {
 
1129                 this.extend(latlngs[i]);
 
1133 LatLngBounds.prototype = {
 
1135         // @method extend(latlng: LatLng): this
 
1136         // Extend the bounds to contain the given point
 
1139         // @method extend(otherBounds: LatLngBounds): this
 
1140         // Extend the bounds to contain the given bounds
 
1141         extend: function (obj) {
 
1142                 var sw = this._southWest,
 
1143                     ne = this._northEast,
 
1146                 if (obj instanceof LatLng) {
 
1150                 } else if (obj instanceof LatLngBounds) {
 
1151                         sw2 = obj._southWest;
 
1152                         ne2 = obj._northEast;
 
1154                         if (!sw2 || !ne2) { return this; }
 
1157                         return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this;
 
1161                         this._southWest = new LatLng(sw2.lat, sw2.lng);
 
1162                         this._northEast = new LatLng(ne2.lat, ne2.lng);
 
1164                         sw.lat = Math.min(sw2.lat, sw.lat);
 
1165                         sw.lng = Math.min(sw2.lng, sw.lng);
 
1166                         ne.lat = Math.max(ne2.lat, ne.lat);
 
1167                         ne.lng = Math.max(ne2.lng, ne.lng);
 
1173         // @method pad(bufferRatio: Number): LatLngBounds
 
1174         // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
 
1175         // For example, a ratio of 0.5 extends the bounds by 50% in each direction.
 
1176         // Negative values will retract the bounds.
 
1177         pad: function (bufferRatio) {
 
1178                 var sw = this._southWest,
 
1179                     ne = this._northEast,
 
1180                     heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
 
1181                     widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
 
1183                 return new LatLngBounds(
 
1184                         new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
 
1185                         new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
 
1188         // @method getCenter(): LatLng
 
1189         // Returns the center point of the bounds.
 
1190         getCenter: function () {
 
1192                         (this._southWest.lat + this._northEast.lat) / 2,
 
1193                         (this._southWest.lng + this._northEast.lng) / 2);
 
1196         // @method getSouthWest(): LatLng
 
1197         // Returns the south-west point of the bounds.
 
1198         getSouthWest: function () {
 
1199                 return this._southWest;
 
1202         // @method getNorthEast(): LatLng
 
1203         // Returns the north-east point of the bounds.
 
1204         getNorthEast: function () {
 
1205                 return this._northEast;
 
1208         // @method getNorthWest(): LatLng
 
1209         // Returns the north-west point of the bounds.
 
1210         getNorthWest: function () {
 
1211                 return new LatLng(this.getNorth(), this.getWest());
 
1214         // @method getSouthEast(): LatLng
 
1215         // Returns the south-east point of the bounds.
 
1216         getSouthEast: function () {
 
1217                 return new LatLng(this.getSouth(), this.getEast());
 
1220         // @method getWest(): Number
 
1221         // Returns the west longitude of the bounds
 
1222         getWest: function () {
 
1223                 return this._southWest.lng;
 
1226         // @method getSouth(): Number
 
1227         // Returns the south latitude of the bounds
 
1228         getSouth: function () {
 
1229                 return this._southWest.lat;
 
1232         // @method getEast(): Number
 
1233         // Returns the east longitude of the bounds
 
1234         getEast: function () {
 
1235                 return this._northEast.lng;
 
1238         // @method getNorth(): Number
 
1239         // Returns the north latitude of the bounds
 
1240         getNorth: function () {
 
1241                 return this._northEast.lat;
 
1244         // @method contains(otherBounds: LatLngBounds): Boolean
 
1245         // Returns `true` if the rectangle contains the given one.
 
1248         // @method contains (latlng: LatLng): Boolean
 
1249         // Returns `true` if the rectangle contains the given point.
 
1250         contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
 
1251                 if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) {
 
1252                         obj = toLatLng(obj);
 
1254                         obj = toLatLngBounds(obj);
 
1257                 var sw = this._southWest,
 
1258                     ne = this._northEast,
 
1261                 if (obj instanceof LatLngBounds) {
 
1262                         sw2 = obj.getSouthWest();
 
1263                         ne2 = obj.getNorthEast();
 
1268                 return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
 
1269                        (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
 
1272         // @method intersects(otherBounds: LatLngBounds): Boolean
 
1273         // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
 
1274         intersects: function (bounds) {
 
1275                 bounds = toLatLngBounds(bounds);
 
1277                 var sw = this._southWest,
 
1278                     ne = this._northEast,
 
1279                     sw2 = bounds.getSouthWest(),
 
1280                     ne2 = bounds.getNorthEast(),
 
1282                     latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
 
1283                     lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
 
1285                 return latIntersects && lngIntersects;
 
1288         // @method overlaps(otherBounds: Bounds): Boolean
 
1289         // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
 
1290         overlaps: function (bounds) {
 
1291                 bounds = toLatLngBounds(bounds);
 
1293                 var sw = this._southWest,
 
1294                     ne = this._northEast,
 
1295                     sw2 = bounds.getSouthWest(),
 
1296                     ne2 = bounds.getNorthEast(),
 
1298                     latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
 
1299                     lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
 
1301                 return latOverlaps && lngOverlaps;
 
1304         // @method toBBoxString(): String
 
1305         // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data.
 
1306         toBBoxString: function () {
 
1307                 return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
 
1310         // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean
 
1311         // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overridden by setting `maxMargin` to a small number.
 
1312         equals: function (bounds, maxMargin) {
 
1313                 if (!bounds) { return false; }
 
1315                 bounds = toLatLngBounds(bounds);
 
1317                 return this._southWest.equals(bounds.getSouthWest(), maxMargin) &&
 
1318                        this._northEast.equals(bounds.getNorthEast(), maxMargin);
 
1321         // @method isValid(): Boolean
 
1322         // Returns `true` if the bounds are properly initialized.
 
1323         isValid: function () {
 
1324                 return !!(this._southWest && this._northEast);
 
1328 // TODO International date line?
 
1330 // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
 
1331 // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
 
1334 // @factory L.latLngBounds(latlngs: LatLng[])
 
1335 // Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds).
 
1336 function toLatLngBounds(a, b) {
 
1337         if (a instanceof LatLngBounds) {
 
1340         return new LatLngBounds(a, b);
 
1346  * Represents a geographical point with a certain latitude and longitude.
 
1351  * var latlng = L.latLng(50.5, 30.5);
 
1354  * All Leaflet methods that accept LatLng objects also accept them in a simple Array form and simple object form (unless noted otherwise), so these lines are equivalent:
 
1357  * map.panTo([50, 30]);
 
1358  * map.panTo({lon: 30, lat: 50});
 
1359  * map.panTo({lat: 50, lng: 30});
 
1360  * map.panTo(L.latLng(50, 30));
 
1363  * Note that `LatLng` does not inherit from Leaflet's `Class` object,
 
1364  * which means new classes can't inherit from it, and new methods
 
1365  * can't be added to it with the `include` function.
 
1368 function LatLng(lat, lng, alt) {
 
1369         if (isNaN(lat) || isNaN(lng)) {
 
1370                 throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
 
1373         // @property lat: Number
 
1374         // Latitude in degrees
 
1377         // @property lng: Number
 
1378         // Longitude in degrees
 
1381         // @property alt: Number
 
1382         // Altitude in meters (optional)
 
1383         if (alt !== undefined) {
 
1388 LatLng.prototype = {
 
1389         // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
 
1390         // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overridden by setting `maxMargin` to a small number.
 
1391         equals: function (obj, maxMargin) {
 
1392                 if (!obj) { return false; }
 
1394                 obj = toLatLng(obj);
 
1396                 var margin = Math.max(
 
1397                         Math.abs(this.lat - obj.lat),
 
1398                         Math.abs(this.lng - obj.lng));
 
1400                 return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
 
1403         // @method toString(): String
 
1404         // Returns a string representation of the point (for debugging purposes).
 
1405         toString: function (precision) {
 
1407                         formatNum(this.lat, precision) + ', ' +
 
1408                         formatNum(this.lng, precision) + ')';
 
1411         // @method distanceTo(otherLatLng: LatLng): Number
 
1412         // Returns the distance (in meters) to the given `LatLng` calculated using the [Spherical Law of Cosines](https://en.wikipedia.org/wiki/Spherical_law_of_cosines).
 
1413         distanceTo: function (other) {
 
1414                 return Earth.distance(this, toLatLng(other));
 
1417         // @method wrap(): LatLng
 
1418         // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
 
1420                 return Earth.wrapLatLng(this);
 
1423         // @method toBounds(sizeInMeters: Number): LatLngBounds
 
1424         // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
 
1425         toBounds: function (sizeInMeters) {
 
1426                 var latAccuracy = 180 * sizeInMeters / 40075017,
 
1427                     lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
 
1429                 return toLatLngBounds(
 
1430                         [this.lat - latAccuracy, this.lng - lngAccuracy],
 
1431                         [this.lat + latAccuracy, this.lng + lngAccuracy]);
 
1434         clone: function () {
 
1435                 return new LatLng(this.lat, this.lng, this.alt);
 
1441 // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
 
1442 // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
 
1445 // @factory L.latLng(coords: Array): LatLng
 
1446 // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
 
1449 // @factory L.latLng(coords: Object): LatLng
 
1450 // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
 
1452 function toLatLng(a, b, c) {
 
1453         if (a instanceof LatLng) {
 
1456         if (isArray(a) && typeof a[0] !== 'object') {
 
1457                 if (a.length === 3) {
 
1458                         return new LatLng(a[0], a[1], a[2]);
 
1460                 if (a.length === 2) {
 
1461                         return new LatLng(a[0], a[1]);
 
1465         if (a === undefined || a === null) {
 
1468         if (typeof a === 'object' && 'lat' in a) {
 
1469                 return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
 
1471         if (b === undefined) {
 
1474         return new LatLng(a, b, c);
 
1480  * Object that defines coordinate reference systems for projecting
 
1481  * geographical points into pixel (screen) coordinates and back (and to
 
1482  * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
 
1483  * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
 
1485  * Leaflet defines the most usual CRSs by default. If you want to use a
 
1486  * CRS not defined by default, take a look at the
 
1487  * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
 
1489  * Note that the CRS instances do not inherit from Leafet's `Class` object,
 
1490  * and can't be instantiated. Also, new classes can't inherit from them,
 
1491  * and methods can't be added to them with the `include` function.
 
1495         // @method latLngToPoint(latlng: LatLng, zoom: Number): Point
 
1496         // Projects geographical coordinates into pixel coordinates for a given zoom.
 
1497         latLngToPoint: function (latlng, zoom) {
 
1498                 var projectedPoint = this.projection.project(latlng),
 
1499                     scale = this.scale(zoom);
 
1501                 return this.transformation._transform(projectedPoint, scale);
 
1504         // @method pointToLatLng(point: Point, zoom: Number): LatLng
 
1505         // The inverse of `latLngToPoint`. Projects pixel coordinates on a given
 
1506         // zoom into geographical coordinates.
 
1507         pointToLatLng: function (point, zoom) {
 
1508                 var scale = this.scale(zoom),
 
1509                     untransformedPoint = this.transformation.untransform(point, scale);
 
1511                 return this.projection.unproject(untransformedPoint);
 
1514         // @method project(latlng: LatLng): Point
 
1515         // Projects geographical coordinates into coordinates in units accepted for
 
1516         // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
 
1517         project: function (latlng) {
 
1518                 return this.projection.project(latlng);
 
1521         // @method unproject(point: Point): LatLng
 
1522         // Given a projected coordinate returns the corresponding LatLng.
 
1523         // The inverse of `project`.
 
1524         unproject: function (point) {
 
1525                 return this.projection.unproject(point);
 
1528         // @method scale(zoom: Number): Number
 
1529         // Returns the scale used when transforming projected coordinates into
 
1530         // pixel coordinates for a particular zoom. For example, it returns
 
1531         // `256 * 2^zoom` for Mercator-based CRS.
 
1532         scale: function (zoom) {
 
1533                 return 256 * Math.pow(2, zoom);
 
1536         // @method zoom(scale: Number): Number
 
1537         // Inverse of `scale()`, returns the zoom level corresponding to a scale
 
1538         // factor of `scale`.
 
1539         zoom: function (scale) {
 
1540                 return Math.log(scale / 256) / Math.LN2;
 
1543         // @method getProjectedBounds(zoom: Number): Bounds
 
1544         // Returns the projection's bounds scaled and transformed for the provided `zoom`.
 
1545         getProjectedBounds: function (zoom) {
 
1546                 if (this.infinite) { return null; }
 
1548                 var b = this.projection.bounds,
 
1549                     s = this.scale(zoom),
 
1550                     min = this.transformation.transform(b.min, s),
 
1551                     max = this.transformation.transform(b.max, s);
 
1553                 return new Bounds(min, max);
 
1556         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
 
1557         // Returns the distance between two geographical coordinates.
 
1559         // @property code: String
 
1560         // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
 
1562         // @property wrapLng: Number[]
 
1563         // An array of two numbers defining whether the longitude (horizontal) coordinate
 
1564         // axis wraps around a given range and how. Defaults to `[-180, 180]` in most
 
1565         // geographical CRSs. If `undefined`, the longitude axis does not wrap around.
 
1567         // @property wrapLat: Number[]
 
1568         // Like `wrapLng`, but for the latitude (vertical) axis.
 
1570         // wrapLng: [min, max],
 
1571         // wrapLat: [min, max],
 
1573         // @property infinite: Boolean
 
1574         // If true, the coordinate space will be unbounded (infinite in both axes)
 
1577         // @method wrapLatLng(latlng: LatLng): LatLng
 
1578         // Returns a `LatLng` where lat and lng has been wrapped according to the
 
1579         // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
 
1580         wrapLatLng: function (latlng) {
 
1581                 var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
 
1582                     lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
 
1585                 return new LatLng(lat, lng, alt);
 
1588         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
 
1589         // Returns a `LatLngBounds` with the same size as the given one, ensuring
 
1590         // that its center is within the CRS's bounds.
 
1591         // Only accepts actual `L.LatLngBounds` instances, not arrays.
 
1592         wrapLatLngBounds: function (bounds) {
 
1593                 var center = bounds.getCenter(),
 
1594                     newCenter = this.wrapLatLng(center),
 
1595                     latShift = center.lat - newCenter.lat,
 
1596                     lngShift = center.lng - newCenter.lng;
 
1598                 if (latShift === 0 && lngShift === 0) {
 
1602                 var sw = bounds.getSouthWest(),
 
1603                     ne = bounds.getNorthEast(),
 
1604                     newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift),
 
1605                     newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift);
 
1607                 return new LatLngBounds(newSw, newNe);
 
1615  * Serves as the base for CRS that are global such that they cover the earth.
 
1616  * Can only be used as the base for other CRS and cannot be used directly,
 
1617  * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
 
1621 var Earth = extend({}, CRS, {
 
1622         wrapLng: [-180, 180],
 
1624         // Mean Earth Radius, as recommended for use by
 
1625         // the International Union of Geodesy and Geophysics,
 
1626         // see http://rosettacode.org/wiki/Haversine_formula
 
1629         // distance between two geographical points using spherical law of cosines approximation
 
1630         distance: function (latlng1, latlng2) {
 
1631                 var rad = Math.PI / 180,
 
1632                     lat1 = latlng1.lat * rad,
 
1633                     lat2 = latlng2.lat * rad,
 
1634                     sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2),
 
1635                     sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2),
 
1636                     a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon,
 
1637                     c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
 
1643  * @namespace Projection
 
1644  * @projection L.Projection.SphericalMercator
 
1646  * Spherical Mercator projection — the most common projection for online maps,
 
1647  * used by almost all free and commercial tile providers. Assumes that Earth is
 
1648  * a sphere. Used by the `EPSG:3857` CRS.
 
1651 var SphericalMercator = {
 
1654         MAX_LATITUDE: 85.0511287798,
 
1656         project: function (latlng) {
 
1657                 var d = Math.PI / 180,
 
1658                     max = this.MAX_LATITUDE,
 
1659                     lat = Math.max(Math.min(max, latlng.lat), -max),
 
1660                     sin = Math.sin(lat * d);
 
1663                         this.R * latlng.lng * d,
 
1664                         this.R * Math.log((1 + sin) / (1 - sin)) / 2);
 
1667         unproject: function (point) {
 
1668                 var d = 180 / Math.PI;
 
1671                         (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
 
1672                         point.x * d / this.R);
 
1675         bounds: (function () {
 
1676                 var d = 6378137 * Math.PI;
 
1677                 return new Bounds([-d, -d], [d, d]);
 
1682  * @class Transformation
 
1683  * @aka L.Transformation
 
1685  * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
 
1686  * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
 
1687  * the reverse. Used by Leaflet in its projections code.
 
1692  * var transformation = L.transformation(2, 5, -1, 10),
 
1693  *      p = L.point(1, 2),
 
1694  *      p2 = transformation.transform(p), //  L.point(7, 8)
 
1695  *      p3 = transformation.untransform(p2); //  L.point(1, 2)
 
1700 // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
 
1701 // Creates a `Transformation` object with the given coefficients.
 
1702 function Transformation(a, b, c, d) {
 
1704                 // use array properties
 
1717 Transformation.prototype = {
 
1718         // @method transform(point: Point, scale?: Number): Point
 
1719         // Returns a transformed point, optionally multiplied by the given scale.
 
1720         // Only accepts actual `L.Point` instances, not arrays.
 
1721         transform: function (point, scale) { // (Point, Number) -> Point
 
1722                 return this._transform(point.clone(), scale);
 
1725         // destructive transform (faster)
 
1726         _transform: function (point, scale) {
 
1728                 point.x = scale * (this._a * point.x + this._b);
 
1729                 point.y = scale * (this._c * point.y + this._d);
 
1733         // @method untransform(point: Point, scale?: Number): Point
 
1734         // Returns the reverse transformation of the given point, optionally divided
 
1735         // by the given scale. Only accepts actual `L.Point` instances, not arrays.
 
1736         untransform: function (point, scale) {
 
1739                         (point.x / scale - this._b) / this._a,
 
1740                         (point.y / scale - this._d) / this._c);
 
1744 // factory L.transformation(a: Number, b: Number, c: Number, d: Number)
 
1746 // @factory L.transformation(a: Number, b: Number, c: Number, d: Number)
 
1747 // Instantiates a Transformation object with the given coefficients.
 
1750 // @factory L.transformation(coefficients: Array): Transformation
 
1751 // Expects an coefficients array of the form
 
1752 // `[a: Number, b: Number, c: Number, d: Number]`.
 
1754 function toTransformation(a, b, c, d) {
 
1755         return new Transformation(a, b, c, d);
 
1760  * @crs L.CRS.EPSG3857
 
1762  * The most common CRS for online maps, used by almost all free and commercial
 
1763  * tile providers. Uses Spherical Mercator projection. Set in by default in
 
1764  * Map's `crs` option.
 
1767 var EPSG3857 = extend({}, Earth, {
 
1769         projection: SphericalMercator,
 
1771         transformation: (function () {
 
1772                 var scale = 0.5 / (Math.PI * SphericalMercator.R);
 
1773                 return toTransformation(scale, 0.5, -scale, 0.5);
 
1777 var EPSG900913 = extend({}, EPSG3857, {
 
1781 // @namespace SVG; @section
 
1782 // There are several static functions which can be called without instantiating L.SVG:
 
1784 // @function create(name: String): SVGElement
 
1785 // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
 
1786 // corresponding to the class name passed. For example, using 'line' will return
 
1787 // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
 
1788 function svgCreate(name) {
 
1789         return document.createElementNS('http://www.w3.org/2000/svg', name);
 
1792 // @function pointsToPath(rings: Point[], closed: Boolean): String
 
1793 // Generates a SVG path string for multiple rings, with each ring turning
 
1794 // into "M..L..L.." instructions
 
1795 function pointsToPath(rings, closed) {
 
1797         i, j, len, len2, points, p;
 
1799         for (i = 0, len = rings.length; i < len; i++) {
 
1802                 for (j = 0, len2 = points.length; j < len2; j++) {
 
1804                         str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
 
1807                 // closes the ring for polygons; "x" is VML syntax
 
1808                 str += closed ? (svg ? 'z' : 'x') : '';
 
1811         // SVG complains about empty path strings
 
1812         return str || 'M0 0';
 
1816  * @namespace Browser
 
1819  * A namespace with static properties for browser/feature detection used by Leaflet internally.
 
1824  * if (L.Browser.ielt9) {
 
1825  *   alert('Upgrade your browser, dude!');
 
1830 var style$1 = document.documentElement.style;
 
1832 // @property ie: Boolean; `true` for all Internet Explorer versions (not Edge).
 
1833 var ie = 'ActiveXObject' in window;
 
1835 // @property ielt9: Boolean; `true` for Internet Explorer versions less than 9.
 
1836 var ielt9 = ie && !document.addEventListener;
 
1838 // @property edge: Boolean; `true` for the Edge web browser.
 
1839 var edge = 'msLaunchUri' in navigator && !('documentMode' in document);
 
1841 // @property webkit: Boolean;
 
1842 // `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
 
1843 var webkit = userAgentContains('webkit');
 
1845 // @property android: Boolean
 
1846 // `true` for any browser running on an Android platform.
 
1847 var android = userAgentContains('android');
 
1849 // @property android23: Boolean; `true` for browsers running on Android 2 or Android 3.
 
1850 var android23 = userAgentContains('android 2') || userAgentContains('android 3');
 
1852 /* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */
 
1853 var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit
 
1854 // @property androidStock: Boolean; `true` for the Android stock browser (i.e. not Chrome)
 
1855 var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window);
 
1857 // @property opera: Boolean; `true` for the Opera browser
 
1858 var opera = !!window.opera;
 
1860 // @property chrome: Boolean; `true` for the Chrome browser.
 
1861 var chrome = userAgentContains('chrome');
 
1863 // @property gecko: Boolean; `true` for gecko-based browsers like Firefox.
 
1864 var gecko = userAgentContains('gecko') && !webkit && !opera && !ie;
 
1866 // @property safari: Boolean; `true` for the Safari browser.
 
1867 var safari = !chrome && userAgentContains('safari');
 
1869 var phantom = userAgentContains('phantom');
 
1871 // @property opera12: Boolean
 
1872 // `true` for the Opera browser supporting CSS transforms (version 12 or later).
 
1873 var opera12 = 'OTransition' in style$1;
 
1875 // @property win: Boolean; `true` when the browser is running in a Windows platform
 
1876 var win = navigator.platform.indexOf('Win') === 0;
 
1878 // @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms.
 
1879 var ie3d = ie && ('transition' in style$1);
 
1881 // @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms.
 
1882 var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23;
 
1884 // @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms.
 
1885 var gecko3d = 'MozPerspective' in style$1;
 
1887 // @property any3d: Boolean
 
1888 // `true` for all browsers supporting CSS transforms.
 
1889 var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom;
 
1891 // @property mobile: Boolean; `true` for all browsers running in a mobile device.
 
1892 var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile');
 
1894 // @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device.
 
1895 var mobileWebkit = mobile && webkit;
 
1897 // @property mobileWebkit3d: Boolean
 
1898 // `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
 
1899 var mobileWebkit3d = mobile && webkit3d;
 
1901 // @property msPointer: Boolean
 
1902 // `true` for browsers implementing the Microsoft touch events model (notably IE10).
 
1903 var msPointer = !window.PointerEvent && window.MSPointerEvent;
 
1905 // @property pointer: Boolean
 
1906 // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
 
1907 var pointer = !!(window.PointerEvent || msPointer);
 
1909 // @property touch: Boolean
 
1910 // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
 
1911 // This does not necessarily mean that the browser is running in a computer with
 
1912 // a touchscreen, it only means that the browser is capable of understanding
 
1914 var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
 
1915                 (window.DocumentTouch && document instanceof window.DocumentTouch));
 
1917 // @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device.
 
1918 var mobileOpera = mobile && opera;
 
1920 // @property mobileGecko: Boolean
 
1921 // `true` for gecko-based browsers running in a mobile device.
 
1922 var mobileGecko = mobile && gecko;
 
1924 // @property retina: Boolean
 
1925 // `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%.
 
1926 var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1;
 
1929 // @property canvas: Boolean
 
1930 // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
 
1931 var canvas = (function () {
 
1932         return !!document.createElement('canvas').getContext;
 
1935 // @property svg: Boolean
 
1936 // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
 
1937 var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect);
 
1939 // @property vml: Boolean
 
1940 // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
 
1941 var vml = !svg && (function () {
 
1943                 var div = document.createElement('div');
 
1944                 div.innerHTML = '<v:shape adj="1"/>';
 
1946                 var shape = div.firstChild;
 
1947                 shape.style.behavior = 'url(#default#VML)';
 
1949                 return shape && (typeof shape.adj === 'object');
 
1957 function userAgentContains(str) {
 
1958         return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
 
1962 var Browser = (Object.freeze || Object)({
 
1968         android23: android23,
 
1969         androidStock: androidStock,
 
1982         mobileWebkit: mobileWebkit,
 
1983         mobileWebkit3d: mobileWebkit3d,
 
1984         msPointer: msPointer,
 
1987         mobileOpera: mobileOpera,
 
1988         mobileGecko: mobileGecko,
 
1996  * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
 
2000 var POINTER_DOWN =   msPointer ? 'MSPointerDown'   : 'pointerdown';
 
2001 var POINTER_MOVE =   msPointer ? 'MSPointerMove'   : 'pointermove';
 
2002 var POINTER_UP =     msPointer ? 'MSPointerUp'     : 'pointerup';
 
2003 var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel';
 
2004 var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION'];
 
2007 var _pointerDocListener = false;
 
2009 // DomEvent.DoubleTap needs to know about this
 
2010 var _pointersCount = 0;
 
2012 // Provides a touch events wrapper for (ms)pointer events.
 
2013 // ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
 
2015 function addPointerListener(obj, type, handler, id) {
 
2016         if (type === 'touchstart') {
 
2017                 _addPointerStart(obj, handler, id);
 
2019         } else if (type === 'touchmove') {
 
2020                 _addPointerMove(obj, handler, id);
 
2022         } else if (type === 'touchend') {
 
2023                 _addPointerEnd(obj, handler, id);
 
2029 function removePointerListener(obj, type, id) {
 
2030         var handler = obj['_leaflet_' + type + id];
 
2032         if (type === 'touchstart') {
 
2033                 obj.removeEventListener(POINTER_DOWN, handler, false);
 
2035         } else if (type === 'touchmove') {
 
2036                 obj.removeEventListener(POINTER_MOVE, handler, false);
 
2038         } else if (type === 'touchend') {
 
2039                 obj.removeEventListener(POINTER_UP, handler, false);
 
2040                 obj.removeEventListener(POINTER_CANCEL, handler, false);
 
2046 function _addPointerStart(obj, handler, id) {
 
2047         var onDown = bind(function (e) {
 
2048                 if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
 
2049                         // In IE11, some touch events needs to fire for form controls, or
 
2050                         // the controls will stop working. We keep a whitelist of tag names that
 
2051                         // need these events. For other target tags, we prevent default on the event.
 
2052                         if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
 
2059                 _handlePointer(e, handler);
 
2062         obj['_leaflet_touchstart' + id] = onDown;
 
2063         obj.addEventListener(POINTER_DOWN, onDown, false);
 
2065         // need to keep track of what pointers and how many are active to provide e.touches emulation
 
2066         if (!_pointerDocListener) {
 
2067                 // we listen documentElement as any drags that end by moving the touch off the screen get fired there
 
2068                 document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true);
 
2069                 document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true);
 
2070                 document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true);
 
2071                 document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
 
2073                 _pointerDocListener = true;
 
2077 function _globalPointerDown(e) {
 
2078         _pointers[e.pointerId] = e;
 
2082 function _globalPointerMove(e) {
 
2083         if (_pointers[e.pointerId]) {
 
2084                 _pointers[e.pointerId] = e;
 
2088 function _globalPointerUp(e) {
 
2089         delete _pointers[e.pointerId];
 
2093 function _handlePointer(e, handler) {
 
2095         for (var i in _pointers) {
 
2096                 e.touches.push(_pointers[i]);
 
2098         e.changedTouches = [e];
 
2103 function _addPointerMove(obj, handler, id) {
 
2104         var onMove = function (e) {
 
2105                 // don't fire touch moves when mouse isn't down
 
2106                 if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
 
2108                 _handlePointer(e, handler);
 
2111         obj['_leaflet_touchmove' + id] = onMove;
 
2112         obj.addEventListener(POINTER_MOVE, onMove, false);
 
2115 function _addPointerEnd(obj, handler, id) {
 
2116         var onUp = function (e) {
 
2117                 _handlePointer(e, handler);
 
2120         obj['_leaflet_touchend' + id] = onUp;
 
2121         obj.addEventListener(POINTER_UP, onUp, false);
 
2122         obj.addEventListener(POINTER_CANCEL, onUp, false);
 
2126  * Extends the event handling code with double tap support for mobile browsers.
 
2129 var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart';
 
2130 var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend';
 
2131 var _pre = '_leaflet_';
 
2133 // inspired by Zepto touch code by Thomas Fuchs
 
2134 function addDoubleTapListener(obj, handler, id) {
 
2139         function onTouchStart(e) {
 
2143                         if ((!edge) || e.pointerType === 'mouse') { return; }
 
2144                         count = _pointersCount;
 
2146                         count = e.touches.length;
 
2149                 if (count > 1) { return; }
 
2151                 var now = Date.now(),
 
2152                     delta = now - (last || now);
 
2154                 touch$$1 = e.touches ? e.touches[0] : e;
 
2155                 doubleTap = (delta > 0 && delta <= delay);
 
2159         function onTouchEnd(e) {
 
2160                 if (doubleTap && !touch$$1.cancelBubble) {
 
2162                                 if ((!edge) || e.pointerType === 'mouse') { return; }
 
2163                                 // work around .type being readonly with MSPointer* events
 
2167                                 for (i in touch$$1) {
 
2169                                         newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop;
 
2171                                 touch$$1 = newTouch;
 
2173                         touch$$1.type = 'dblclick';
 
2179         obj[_pre + _touchstart + id] = onTouchStart;
 
2180         obj[_pre + _touchend + id] = onTouchEnd;
 
2181         obj[_pre + 'dblclick' + id] = handler;
 
2183         obj.addEventListener(_touchstart, onTouchStart, false);
 
2184         obj.addEventListener(_touchend, onTouchEnd, false);
 
2186         // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse),
 
2187         // the browser doesn't fire touchend/pointerup events but does fire
 
2188         // native dblclicks. See #4127.
 
2189         // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180.
 
2190         obj.addEventListener('dblclick', handler, false);
 
2195 function removeDoubleTapListener(obj, id) {
 
2196         var touchstart = obj[_pre + _touchstart + id],
 
2197             touchend = obj[_pre + _touchend + id],
 
2198             dblclick = obj[_pre + 'dblclick' + id];
 
2200         obj.removeEventListener(_touchstart, touchstart, false);
 
2201         obj.removeEventListener(_touchend, touchend, false);
 
2203                 obj.removeEventListener('dblclick', dblclick, false);
 
2210  * @namespace DomUtil
 
2212  * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
 
2213  * tree, used by Leaflet internally.
 
2215  * Most functions expecting or returning a `HTMLElement` also work for
 
2216  * SVG elements. The only difference is that classes refer to CSS classes
 
2217  * in HTML and SVG classes in SVG.
 
2221 // @property TRANSFORM: String
 
2222 // Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
 
2223 var TRANSFORM = testProp(
 
2224         ['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
 
2226 // webkitTransition comes first because some browser versions that drop vendor prefix don't do
 
2227 // the same for the transitionend event, in particular the Android 4.1 stock browser
 
2229 // @property TRANSITION: String
 
2230 // Vendor-prefixed transition style name.
 
2231 var TRANSITION = testProp(
 
2232         ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
 
2234 // @property TRANSITION_END: String
 
2235 // Vendor-prefixed transitionend event name.
 
2236 var TRANSITION_END =
 
2237         TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
 
2240 // @function get(id: String|HTMLElement): HTMLElement
 
2241 // Returns an element given its DOM id, or returns the element itself
 
2242 // if it was passed directly.
 
2244         return typeof id === 'string' ? document.getElementById(id) : id;
 
2247 // @function getStyle(el: HTMLElement, styleAttrib: String): String
 
2248 // Returns the value for a certain style attribute on an element,
 
2249 // including computed values or values set through CSS.
 
2250 function getStyle(el, style) {
 
2251         var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
 
2253         if ((!value || value === 'auto') && document.defaultView) {
 
2254                 var css = document.defaultView.getComputedStyle(el, null);
 
2255                 value = css ? css[style] : null;
 
2257         return value === 'auto' ? null : value;
 
2260 // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
 
2261 // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
 
2262 function create$1(tagName, className, container) {
 
2263         var el = document.createElement(tagName);
 
2264         el.className = className || '';
 
2267                 container.appendChild(el);
 
2272 // @function remove(el: HTMLElement)
 
2273 // Removes `el` from its parent element
 
2274 function remove(el) {
 
2275         var parent = el.parentNode;
 
2277                 parent.removeChild(el);
 
2281 // @function empty(el: HTMLElement)
 
2282 // Removes all of `el`'s children elements from `el`
 
2283 function empty(el) {
 
2284         while (el.firstChild) {
 
2285                 el.removeChild(el.firstChild);
 
2289 // @function toFront(el: HTMLElement)
 
2290 // Makes `el` the last child of its parent, so it renders in front of the other children.
 
2291 function toFront(el) {
 
2292         var parent = el.parentNode;
 
2293         if (parent.lastChild !== el) {
 
2294                 parent.appendChild(el);
 
2298 // @function toBack(el: HTMLElement)
 
2299 // Makes `el` the first child of its parent, so it renders behind the other children.
 
2300 function toBack(el) {
 
2301         var parent = el.parentNode;
 
2302         if (parent.firstChild !== el) {
 
2303                 parent.insertBefore(el, parent.firstChild);
 
2307 // @function hasClass(el: HTMLElement, name: String): Boolean
 
2308 // Returns `true` if the element's class attribute contains `name`.
 
2309 function hasClass(el, name) {
 
2310         if (el.classList !== undefined) {
 
2311                 return el.classList.contains(name);
 
2313         var className = getClass(el);
 
2314         return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
 
2317 // @function addClass(el: HTMLElement, name: String)
 
2318 // Adds `name` to the element's class attribute.
 
2319 function addClass(el, name) {
 
2320         if (el.classList !== undefined) {
 
2321                 var classes = splitWords(name);
 
2322                 for (var i = 0, len = classes.length; i < len; i++) {
 
2323                         el.classList.add(classes[i]);
 
2325         } else if (!hasClass(el, name)) {
 
2326                 var className = getClass(el);
 
2327                 setClass(el, (className ? className + ' ' : '') + name);
 
2331 // @function removeClass(el: HTMLElement, name: String)
 
2332 // Removes `name` from the element's class attribute.
 
2333 function removeClass(el, name) {
 
2334         if (el.classList !== undefined) {
 
2335                 el.classList.remove(name);
 
2337                 setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
 
2341 // @function setClass(el: HTMLElement, name: String)
 
2342 // Sets the element's class.
 
2343 function setClass(el, name) {
 
2344         if (el.className.baseVal === undefined) {
 
2345                 el.className = name;
 
2347                 // in case of SVG element
 
2348                 el.className.baseVal = name;
 
2352 // @function getClass(el: HTMLElement): String
 
2353 // Returns the element's class.
 
2354 function getClass(el) {
 
2355         return el.className.baseVal === undefined ? el.className : el.className.baseVal;
 
2358 // @function setOpacity(el: HTMLElement, opacity: Number)
 
2359 // Set the opacity of an element (including old IE support).
 
2360 // `opacity` must be a number from `0` to `1`.
 
2361 function setOpacity(el, value) {
 
2362         if ('opacity' in el.style) {
 
2363                 el.style.opacity = value;
 
2364         } else if ('filter' in el.style) {
 
2365                 _setOpacityIE(el, value);
 
2369 function _setOpacityIE(el, value) {
 
2371             filterName = 'DXImageTransform.Microsoft.Alpha';
 
2373         // filters collection throws an error if we try to retrieve a filter that doesn't exist
 
2375                 filter = el.filters.item(filterName);
 
2377                 // don't set opacity to 1 if we haven't already set an opacity,
 
2378                 // it isn't needed and breaks transparent pngs.
 
2379                 if (value === 1) { return; }
 
2382         value = Math.round(value * 100);
 
2385                 filter.Enabled = (value !== 100);
 
2386                 filter.Opacity = value;
 
2388                 el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
 
2392 // @function testProp(props: String[]): String|false
 
2393 // Goes through the array of style names and returns the first name
 
2394 // that is a valid style name for an element. If no such name is found,
 
2395 // it returns false. Useful for vendor-prefixed styles like `transform`.
 
2396 function testProp(props) {
 
2397         var style = document.documentElement.style;
 
2399         for (var i = 0; i < props.length; i++) {
 
2400                 if (props[i] in style) {
 
2407 // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
 
2408 // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
 
2409 // and optionally scaled by `scale`. Does not have an effect if the
 
2410 // browser doesn't support 3D CSS transforms.
 
2411 function setTransform(el, offset, scale) {
 
2412         var pos = offset || new Point(0, 0);
 
2414         el.style[TRANSFORM] =
 
2416                         'translate(' + pos.x + 'px,' + pos.y + 'px)' :
 
2417                         'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
 
2418                 (scale ? ' scale(' + scale + ')' : '');
 
2421 // @function setPosition(el: HTMLElement, position: Point)
 
2422 // Sets the position of `el` to coordinates specified by `position`,
 
2423 // using CSS translate or top/left positioning depending on the browser
 
2424 // (used by Leaflet internally to position its layers).
 
2425 function setPosition(el, point) {
 
2428         el._leaflet_pos = point;
 
2432                 setTransform(el, point);
 
2434                 el.style.left = point.x + 'px';
 
2435                 el.style.top = point.y + 'px';
 
2439 // @function getPosition(el: HTMLElement): Point
 
2440 // Returns the coordinates of an element previously positioned with setPosition.
 
2441 function getPosition(el) {
 
2442         // this method is only used for elements previously positioned using setPosition,
 
2443         // so it's safe to cache the position for performance
 
2445         return el._leaflet_pos || new Point(0, 0);
 
2448 // @function disableTextSelection()
 
2449 // Prevents the user from generating `selectstart` DOM events, usually generated
 
2450 // when the user drags the mouse through a page with text. Used internally
 
2451 // by Leaflet to override the behaviour of any click-and-drag interaction on
 
2452 // the map. Affects drag interactions on the whole document.
 
2454 // @function enableTextSelection()
 
2455 // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
 
2456 var disableTextSelection;
 
2457 var enableTextSelection;
 
2459 if ('onselectstart' in document) {
 
2460         disableTextSelection = function () {
 
2461                 on(window, 'selectstart', preventDefault);
 
2463         enableTextSelection = function () {
 
2464                 off(window, 'selectstart', preventDefault);
 
2467         var userSelectProperty = testProp(
 
2468                 ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
 
2470         disableTextSelection = function () {
 
2471                 if (userSelectProperty) {
 
2472                         var style = document.documentElement.style;
 
2473                         _userSelect = style[userSelectProperty];
 
2474                         style[userSelectProperty] = 'none';
 
2477         enableTextSelection = function () {
 
2478                 if (userSelectProperty) {
 
2479                         document.documentElement.style[userSelectProperty] = _userSelect;
 
2480                         _userSelect = undefined;
 
2485 // @function disableImageDrag()
 
2486 // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
 
2487 // for `dragstart` DOM events, usually generated when the user drags an image.
 
2488 function disableImageDrag() {
 
2489         on(window, 'dragstart', preventDefault);
 
2492 // @function enableImageDrag()
 
2493 // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
 
2494 function enableImageDrag() {
 
2495         off(window, 'dragstart', preventDefault);
 
2498 var _outlineElement;
 
2500 // @function preventOutline(el: HTMLElement)
 
2501 // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
 
2502 // of the element `el` invisible. Used internally by Leaflet to prevent
 
2503 // focusable elements from displaying an outline when the user performs a
 
2504 // drag interaction on them.
 
2505 function preventOutline(element) {
 
2506         while (element.tabIndex === -1) {
 
2507                 element = element.parentNode;
 
2509         if (!element.style) { return; }
 
2511         _outlineElement = element;
 
2512         _outlineStyle = element.style.outline;
 
2513         element.style.outline = 'none';
 
2514         on(window, 'keydown', restoreOutline);
 
2517 // @function restoreOutline()
 
2518 // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
 
2519 function restoreOutline() {
 
2520         if (!_outlineElement) { return; }
 
2521         _outlineElement.style.outline = _outlineStyle;
 
2522         _outlineElement = undefined;
 
2523         _outlineStyle = undefined;
 
2524         off(window, 'keydown', restoreOutline);
 
2527 // @function getSizedParentNode(el: HTMLElement): HTMLElement
 
2528 // Finds the closest parent node which size (width and height) is not null.
 
2529 function getSizedParentNode(element) {
 
2531                 element = element.parentNode;
 
2532         } while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body);
 
2536 // @function getScale(el: HTMLElement): Object
 
2537 // Computes the CSS scale currently applied on the element.
 
2538 // Returns an object with `x` and `y` members as horizontal and vertical scales respectively,
 
2539 // and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
 
2540 function getScale(element) {
 
2541         var rect = element.getBoundingClientRect(); // Read-only in old browsers.
 
2544                 x: rect.width / element.offsetWidth || 1,
 
2545                 y: rect.height / element.offsetHeight || 1,
 
2546                 boundingClientRect: rect
 
2551 var DomUtil = (Object.freeze || Object)({
 
2552         TRANSFORM: TRANSFORM,
 
2553         TRANSITION: TRANSITION,
 
2554         TRANSITION_END: TRANSITION_END,
 
2564         removeClass: removeClass,
 
2567         setOpacity: setOpacity,
 
2569         setTransform: setTransform,
 
2570         setPosition: setPosition,
 
2571         getPosition: getPosition,
 
2572         disableTextSelection: disableTextSelection,
 
2573         enableTextSelection: enableTextSelection,
 
2574         disableImageDrag: disableImageDrag,
 
2575         enableImageDrag: enableImageDrag,
 
2576         preventOutline: preventOutline,
 
2577         restoreOutline: restoreOutline,
 
2578         getSizedParentNode: getSizedParentNode,
 
2583  * @namespace DomEvent
 
2584  * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
 
2587 // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
 
2589 // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
 
2590 // Adds a listener function (`fn`) to a particular DOM event type of the
 
2591 // element `el`. You can optionally specify the context of the listener
 
2592 // (object the `this` keyword will point to). You can also pass several
 
2593 // space-separated types (e.g. `'click dblclick'`).
 
2596 // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
 
2597 // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
2598 function on(obj, types, fn, context) {
 
2600         if (typeof types === 'object') {
 
2601                 for (var type in types) {
 
2602                         addOne(obj, type, types[type], fn);
 
2605                 types = splitWords(types);
 
2607                 for (var i = 0, len = types.length; i < len; i++) {
 
2608                         addOne(obj, types[i], fn, context);
 
2615 var eventsKey = '_leaflet_events';
 
2617 // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
 
2618 // Removes a previously added listener function.
 
2619 // Note that if you passed a custom context to on, you must pass the same
 
2620 // context to `off` in order to remove the listener.
 
2623 // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
 
2624 // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
2625 function off(obj, types, fn, context) {
 
2627         if (typeof types === 'object') {
 
2628                 for (var type in types) {
 
2629                         removeOne(obj, type, types[type], fn);
 
2632                 types = splitWords(types);
 
2634                 for (var i = 0, len = types.length; i < len; i++) {
 
2635                         removeOne(obj, types[i], fn, context);
 
2638                 for (var j in obj[eventsKey]) {
 
2639                         removeOne(obj, j, obj[eventsKey][j]);
 
2641                 delete obj[eventsKey];
 
2647 function addOne(obj, type, fn, context) {
 
2648         var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
 
2650         if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
 
2652         var handler = function (e) {
 
2653                 return fn.call(context || obj, e || window.event);
 
2656         var originalHandler = handler;
 
2658         if (pointer && type.indexOf('touch') === 0) {
 
2659                 // Needs DomEvent.Pointer.js
 
2660                 addPointerListener(obj, type, handler, id);
 
2662         } else if (touch && (type === 'dblclick') && addDoubleTapListener &&
 
2663                    !(pointer && chrome)) {
 
2664                 // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
 
2666                 addDoubleTapListener(obj, handler, id);
 
2668         } else if ('addEventListener' in obj) {
 
2670                 if (type === 'mousewheel') {
 
2671                         obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
 
2673                 } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
 
2674                         handler = function (e) {
 
2675                                 e = e || window.event;
 
2676                                 if (isExternalTarget(obj, e)) {
 
2680                         obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
 
2683                         if (type === 'click' && android) {
 
2684                                 handler = function (e) {
 
2685                                         filterClick(e, originalHandler);
 
2688                         obj.addEventListener(type, handler, false);
 
2691         } else if ('attachEvent' in obj) {
 
2692                 obj.attachEvent('on' + type, handler);
 
2695         obj[eventsKey] = obj[eventsKey] || {};
 
2696         obj[eventsKey][id] = handler;
 
2699 function removeOne(obj, type, fn, context) {
 
2701         var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''),
 
2702             handler = obj[eventsKey] && obj[eventsKey][id];
 
2704         if (!handler) { return this; }
 
2706         if (pointer && type.indexOf('touch') === 0) {
 
2707                 removePointerListener(obj, type, id);
 
2709         } else if (touch && (type === 'dblclick') && removeDoubleTapListener &&
 
2710                    !(pointer && chrome)) {
 
2711                 removeDoubleTapListener(obj, id);
 
2713         } else if ('removeEventListener' in obj) {
 
2715                 if (type === 'mousewheel') {
 
2716                         obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
 
2719                         obj.removeEventListener(
 
2720                                 type === 'mouseenter' ? 'mouseover' :
 
2721                                 type === 'mouseleave' ? 'mouseout' : type, handler, false);
 
2724         } else if ('detachEvent' in obj) {
 
2725                 obj.detachEvent('on' + type, handler);
 
2728         obj[eventsKey][id] = null;
 
2731 // @function stopPropagation(ev: DOMEvent): this
 
2732 // Stop the given event from propagation to parent elements. Used inside the listener functions:
 
2734 // L.DomEvent.on(div, 'click', function (ev) {
 
2735 //      L.DomEvent.stopPropagation(ev);
 
2738 function stopPropagation(e) {
 
2740         if (e.stopPropagation) {
 
2741                 e.stopPropagation();
 
2742         } else if (e.originalEvent) {  // In case of Leaflet event.
 
2743                 e.originalEvent._stopped = true;
 
2745                 e.cancelBubble = true;
 
2752 // @function disableScrollPropagation(el: HTMLElement): this
 
2753 // Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
 
2754 function disableScrollPropagation(el) {
 
2755         addOne(el, 'mousewheel', stopPropagation);
 
2759 // @function disableClickPropagation(el: HTMLElement): this
 
2760 // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
 
2761 // `'mousedown'` and `'touchstart'` events (plus browser variants).
 
2762 function disableClickPropagation(el) {
 
2763         on(el, 'mousedown touchstart dblclick', stopPropagation);
 
2764         addOne(el, 'click', fakeStop);
 
2768 // @function preventDefault(ev: DOMEvent): this
 
2769 // Prevents the default action of the DOM Event `ev` from happening (such as
 
2770 // following a link in the href of the a element, or doing a POST request
 
2771 // with page reload when a `<form>` is submitted).
 
2772 // Use it inside listener functions.
 
2773 function preventDefault(e) {
 
2774         if (e.preventDefault) {
 
2777                 e.returnValue = false;
 
2782 // @function stop(ev: DOMEvent): this
 
2783 // Does `stopPropagation` and `preventDefault` at the same time.
 
2790 // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
 
2791 // Gets normalized mouse position from a DOM event relative to the
 
2792 // `container` (border excluded) or to the whole page if not specified.
 
2793 function getMousePosition(e, container) {
 
2795                 return new Point(e.clientX, e.clientY);
 
2798         var scale = getScale(container),
 
2799             offset = scale.boundingClientRect; // left and top  values are in page scale (like the event clientX/Y)
 
2802                 // offset.left/top values are in page scale (like clientX/Y),
 
2803                 // whereas clientLeft/Top (border width) values are the original values (before CSS scale applies).
 
2804                 (e.clientX - offset.left) / scale.x - container.clientLeft,
 
2805                 (e.clientY - offset.top) / scale.y - container.clientTop
 
2809 // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
 
2810 // and Firefox scrolls device pixels, not CSS pixels
 
2812         (win && chrome) ? 2 * window.devicePixelRatio :
 
2813         gecko ? window.devicePixelRatio : 1;
 
2815 // @function getWheelDelta(ev: DOMEvent): Number
 
2816 // Gets normalized wheel delta from a mousewheel DOM event, in vertical
 
2817 // pixels scrolled (negative if scrolling down).
 
2818 // Events from pointing devices without precise scrolling are mapped to
 
2819 // a best guess of 60 pixels.
 
2820 function getWheelDelta(e) {
 
2821         return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
 
2822                (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
 
2823                (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
 
2824                (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
 
2825                (e.deltaX || e.deltaZ) ? 0 :     // Skip horizontal/depth wheel events
 
2826                e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
 
2827                (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
 
2828                e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
 
2832 var skipEvents = {};
 
2834 function fakeStop(e) {
 
2835         // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e)
 
2836         skipEvents[e.type] = true;
 
2839 function skipped(e) {
 
2840         var events = skipEvents[e.type];
 
2841         // reset when checking, as it's only used in map container and propagates outside of the map
 
2842         skipEvents[e.type] = false;
 
2846 // check if element really left/entered the event target (for mouseenter/mouseleave)
 
2847 function isExternalTarget(el, e) {
 
2849         var related = e.relatedTarget;
 
2851         if (!related) { return true; }
 
2854                 while (related && (related !== el)) {
 
2855                         related = related.parentNode;
 
2860         return (related !== el);
 
2865 // this is a horrible workaround for a bug in Android where a single touch triggers two click events
 
2866 function filterClick(e, handler) {
 
2867         var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
 
2868             elapsed = lastClick && (timeStamp - lastClick);
 
2870         // are they closer together than 500ms yet more than 100ms?
 
2871         // Android typically triggers them ~300ms apart while multiple listeners
 
2872         // on the same event should be triggered far faster;
 
2873         // or check if click is simulated on the element, and if it is, reject any non-simulated events
 
2875         if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
 
2879         lastClick = timeStamp;
 
2887 var DomEvent = (Object.freeze || Object)({
 
2890         stopPropagation: stopPropagation,
 
2891         disableScrollPropagation: disableScrollPropagation,
 
2892         disableClickPropagation: disableClickPropagation,
 
2893         preventDefault: preventDefault,
 
2895         getMousePosition: getMousePosition,
 
2896         getWheelDelta: getWheelDelta,
 
2899         isExternalTarget: isExternalTarget,
 
2905  * @class PosAnimation
 
2906  * @aka L.PosAnimation
 
2908  * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
 
2912  * var fx = new L.PosAnimation();
 
2913  * fx.run(el, [300, 500], 0.5);
 
2916  * @constructor L.PosAnimation()
 
2917  * Creates a `PosAnimation` object.
 
2921 var PosAnimation = Evented.extend({
 
2923         // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
 
2924         // Run an animation of a given element to a new position, optionally setting
 
2925         // duration in seconds (`0.25` by default) and easing linearity factor (3rd
 
2926         // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
 
2927         // `0.5` by default).
 
2928         run: function (el, newPos, duration, easeLinearity) {
 
2932                 this._inProgress = true;
 
2933                 this._duration = duration || 0.25;
 
2934                 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
 
2936                 this._startPos = getPosition(el);
 
2937                 this._offset = newPos.subtract(this._startPos);
 
2938                 this._startTime = +new Date();
 
2940                 // @event start: Event
 
2941                 // Fired when the animation starts
 
2948         // Stops the animation (if currently running).
 
2950                 if (!this._inProgress) { return; }
 
2956         _animate: function () {
 
2958                 this._animId = requestAnimFrame(this._animate, this);
 
2962         _step: function (round) {
 
2963                 var elapsed = (+new Date()) - this._startTime,
 
2964                     duration = this._duration * 1000;
 
2966                 if (elapsed < duration) {
 
2967                         this._runFrame(this._easeOut(elapsed / duration), round);
 
2974         _runFrame: function (progress, round) {
 
2975                 var pos = this._startPos.add(this._offset.multiplyBy(progress));
 
2979                 setPosition(this._el, pos);
 
2981                 // @event step: Event
 
2982                 // Fired continuously during the animation.
 
2986         _complete: function () {
 
2987                 cancelAnimFrame(this._animId);
 
2989                 this._inProgress = false;
 
2990                 // @event end: Event
 
2991                 // Fired when the animation ends.
 
2995         _easeOut: function (t) {
 
2996                 return 1 - Math.pow(1 - t, this._easeOutPower);
 
3005  * The central class of the API — it is used to create a map on a page and manipulate it.
 
3010  * // initialize the map on the "map" div with a given center and zoom
 
3011  * var map = L.map('map', {
 
3012  *      center: [51.505, -0.09],
 
3019 var Map = Evented.extend({
 
3022                 // @section Map State Options
 
3023                 // @option crs: CRS = L.CRS.EPSG3857
 
3024                 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
 
3025                 // sure what it means.
 
3028                 // @option center: LatLng = undefined
 
3029                 // Initial geographic center of the map
 
3032                 // @option zoom: Number = undefined
 
3033                 // Initial map zoom level
 
3036                 // @option minZoom: Number = *
 
3037                 // Minimum zoom level of the map.
 
3038                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
 
3039                 // the lowest of their `minZoom` options will be used instead.
 
3042                 // @option maxZoom: Number = *
 
3043                 // Maximum zoom level of the map.
 
3044                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
 
3045                 // the highest of their `maxZoom` options will be used instead.
 
3048                 // @option layers: Layer[] = []
 
3049                 // Array of layers that will be added to the map initially
 
3052                 // @option maxBounds: LatLngBounds = null
 
3053                 // When this option is set, the map restricts the view to the given
 
3054                 // geographical bounds, bouncing the user back if the user tries to pan
 
3055                 // outside the view. To set the restriction dynamically, use
 
3056                 // [`setMaxBounds`](#map-setmaxbounds) method.
 
3057                 maxBounds: undefined,
 
3059                 // @option renderer: Renderer = *
 
3060                 // The default method for drawing vector layers on the map. `L.SVG`
 
3061                 // or `L.Canvas` by default depending on browser support.
 
3062                 renderer: undefined,
 
3065                 // @section Animation Options
 
3066                 // @option zoomAnimation: Boolean = true
 
3067                 // Whether the map zoom animation is enabled. By default it's enabled
 
3068                 // in all browsers that support CSS3 Transitions except Android.
 
3069                 zoomAnimation: true,
 
3071                 // @option zoomAnimationThreshold: Number = 4
 
3072                 // Won't animate zoom if the zoom difference exceeds this value.
 
3073                 zoomAnimationThreshold: 4,
 
3075                 // @option fadeAnimation: Boolean = true
 
3076                 // Whether the tile fade animation is enabled. By default it's enabled
 
3077                 // in all browsers that support CSS3 Transitions except Android.
 
3078                 fadeAnimation: true,
 
3080                 // @option markerZoomAnimation: Boolean = true
 
3081                 // Whether markers animate their zoom with the zoom animation, if disabled
 
3082                 // they will disappear for the length of the animation. By default it's
 
3083                 // enabled in all browsers that support CSS3 Transitions except Android.
 
3084                 markerZoomAnimation: true,
 
3086                 // @option transform3DLimit: Number = 2^23
 
3087                 // Defines the maximum size of a CSS translation transform. The default
 
3088                 // value should not be changed unless a web browser positions layers in
 
3089                 // the wrong place after doing a large `panBy`.
 
3090                 transform3DLimit: 8388608, // Precision limit of a 32-bit float
 
3092                 // @section Interaction Options
 
3093                 // @option zoomSnap: Number = 1
 
3094                 // Forces the map's zoom level to always be a multiple of this, particularly
 
3095                 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
 
3096                 // By default, the zoom level snaps to the nearest integer; lower values
 
3097                 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
 
3098                 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
 
3101                 // @option zoomDelta: Number = 1
 
3102                 // Controls how much the map's zoom level will change after a
 
3103                 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
 
3104                 // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
 
3105                 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
 
3108                 // @option trackResize: Boolean = true
 
3109                 // Whether the map automatically handles browser window resize to update itself.
 
3113         initialize: function (id, options) { // (HTMLElement or String, Object)
 
3114                 options = setOptions(this, options);
 
3116                 this._initContainer(id);
 
3119                 // hack for https://github.com/Leaflet/Leaflet/issues/1980
 
3120                 this._onResize = bind(this._onResize, this);
 
3124                 if (options.maxBounds) {
 
3125                         this.setMaxBounds(options.maxBounds);
 
3128                 if (options.zoom !== undefined) {
 
3129                         this._zoom = this._limitZoom(options.zoom);
 
3132                 if (options.center && options.zoom !== undefined) {
 
3133                         this.setView(toLatLng(options.center), options.zoom, {reset: true});
 
3136                 this._handlers = [];
 
3138                 this._zoomBoundLayers = {};
 
3139                 this._sizeChanged = true;
 
3141                 this.callInitHooks();
 
3143                 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
 
3144                 this._zoomAnimated = TRANSITION && any3d && !mobileOpera &&
 
3145                                 this.options.zoomAnimation;
 
3147                 // zoom transitions run with the same duration for all layers, so if one of transitionend events
 
3148                 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
 
3149                 if (this._zoomAnimated) {
 
3150                         this._createAnimProxy();
 
3151                         on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
 
3154                 this._addLayers(this.options.layers);
 
3158         // @section Methods for modifying map state
 
3160         // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
 
3161         // Sets the view of the map (geographical center and zoom) with the given
 
3162         // animation options.
 
3163         setView: function (center, zoom, options) {
 
3165                 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
 
3166                 center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
 
3167                 options = options || {};
 
3171                 if (this._loaded && !options.reset && options !== true) {
 
3173                         if (options.animate !== undefined) {
 
3174                                 options.zoom = extend({animate: options.animate}, options.zoom);
 
3175                                 options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
 
3178                         // try animating pan or zoom
 
3179                         var moved = (this._zoom !== zoom) ?
 
3180                                 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
 
3181                                 this._tryAnimatedPan(center, options.pan);
 
3184                                 // prevent resize handler call, the view will refresh after animation anyway
 
3185                                 clearTimeout(this._sizeTimer);
 
3190                 // animation didn't start, just reset the map view
 
3191                 this._resetView(center, zoom);
 
3196         // @method setZoom(zoom: Number, options?: Zoom/pan options): this
 
3197         // Sets the zoom of the map.
 
3198         setZoom: function (zoom, options) {
 
3199                 if (!this._loaded) {
 
3203                 return this.setView(this.getCenter(), zoom, {zoom: options});
 
3206         // @method zoomIn(delta?: Number, options?: Zoom options): this
 
3207         // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
 
3208         zoomIn: function (delta, options) {
 
3209                 delta = delta || (any3d ? this.options.zoomDelta : 1);
 
3210                 return this.setZoom(this._zoom + delta, options);
 
3213         // @method zoomOut(delta?: Number, options?: Zoom options): this
 
3214         // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
 
3215         zoomOut: function (delta, options) {
 
3216                 delta = delta || (any3d ? this.options.zoomDelta : 1);
 
3217                 return this.setZoom(this._zoom - delta, options);
 
3220         // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
 
3221         // Zooms the map while keeping a specified geographical point on the map
 
3222         // stationary (e.g. used internally for scroll zoom and double-click zoom).
 
3224         // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
 
3225         // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
 
3226         setZoomAround: function (latlng, zoom, options) {
 
3227                 var scale = this.getZoomScale(zoom),
 
3228                     viewHalf = this.getSize().divideBy(2),
 
3229                     containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
 
3231                     centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
 
3232                     newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
 
3234                 return this.setView(newCenter, zoom, {zoom: options});
 
3237         _getBoundsCenterZoom: function (bounds, options) {
 
3239                 options = options || {};
 
3240                 bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
 
3242                 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
 
3243                     paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
 
3245                     zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
 
3247                 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
 
3249                 if (zoom === Infinity) {
 
3251                                 center: bounds.getCenter(),
 
3256                 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
 
3258                     swPoint = this.project(bounds.getSouthWest(), zoom),
 
3259                     nePoint = this.project(bounds.getNorthEast(), zoom),
 
3260                     center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
 
3268         // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
 
3269         // Sets a map view that contains the given geographical bounds with the
 
3270         // maximum zoom level possible.
 
3271         fitBounds: function (bounds, options) {
 
3273                 bounds = toLatLngBounds(bounds);
 
3275                 if (!bounds.isValid()) {
 
3276                         throw new Error('Bounds are not valid.');
 
3279                 var target = this._getBoundsCenterZoom(bounds, options);
 
3280                 return this.setView(target.center, target.zoom, options);
 
3283         // @method fitWorld(options?: fitBounds options): this
 
3284         // Sets a map view that mostly contains the whole world with the maximum
 
3285         // zoom level possible.
 
3286         fitWorld: function (options) {
 
3287                 return this.fitBounds([[-90, -180], [90, 180]], options);
 
3290         // @method panTo(latlng: LatLng, options?: Pan options): this
 
3291         // Pans the map to a given center.
 
3292         panTo: function (center, options) { // (LatLng)
 
3293                 return this.setView(center, this._zoom, {pan: options});
 
3296         // @method panBy(offset: Point, options?: Pan options): this
 
3297         // Pans the map by a given number of pixels (animated).
 
3298         panBy: function (offset, options) {
 
3299                 offset = toPoint(offset).round();
 
3300                 options = options || {};
 
3302                 if (!offset.x && !offset.y) {
 
3303                         return this.fire('moveend');
 
3305                 // If we pan too far, Chrome gets issues with tiles
 
3306                 // and makes them disappear or appear in the wrong place (slightly offset) #2602
 
3307                 if (options.animate !== true && !this.getSize().contains(offset)) {
 
3308                         this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
 
3312                 if (!this._panAnim) {
 
3313                         this._panAnim = new PosAnimation();
 
3316                                 'step': this._onPanTransitionStep,
 
3317                                 'end': this._onPanTransitionEnd
 
3321                 // don't fire movestart if animating inertia
 
3322                 if (!options.noMoveStart) {
 
3323                         this.fire('movestart');
 
3326                 // animate pan unless animate: false specified
 
3327                 if (options.animate !== false) {
 
3328                         addClass(this._mapPane, 'leaflet-pan-anim');
 
3330                         var newPos = this._getMapPanePos().subtract(offset).round();
 
3331                         this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
 
3333                         this._rawPanBy(offset);
 
3334                         this.fire('move').fire('moveend');
 
3340         // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
 
3341         // Sets the view of the map (geographical center and zoom) performing a smooth
 
3342         // pan-zoom animation.
 
3343         flyTo: function (targetCenter, targetZoom, options) {
 
3345                 options = options || {};
 
3346                 if (options.animate === false || !any3d) {
 
3347                         return this.setView(targetCenter, targetZoom, options);
 
3352                 var from = this.project(this.getCenter()),
 
3353                     to = this.project(targetCenter),
 
3354                     size = this.getSize(),
 
3355                     startZoom = this._zoom;
 
3357                 targetCenter = toLatLng(targetCenter);
 
3358                 targetZoom = targetZoom === undefined ? startZoom : targetZoom;
 
3360                 var w0 = Math.max(size.x, size.y),
 
3361                     w1 = w0 * this.getZoomScale(startZoom, targetZoom),
 
3362                     u1 = (to.distanceTo(from)) || 1,
 
3367                         var s1 = i ? -1 : 1,
 
3369                             t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
 
3370                             b1 = 2 * s2 * rho2 * u1,
 
3372                             sq = Math.sqrt(b * b + 1) - b;
 
3374                             // workaround for floating point precision bug when sq = 0, log = -Infinite,
 
3375                             // thus triggering an infinite loop in flyTo
 
3376                             var log = sq < 0.000000001 ? -18 : Math.log(sq);
 
3381                 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
 
3382                 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
 
3383                 function tanh(n) { return sinh(n) / cosh(n); }
 
3387                 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
 
3388                 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
 
3390                 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
 
3392                 var start = Date.now(),
 
3393                     S = (r(1) - r0) / rho,
 
3394                     duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
 
3397                         var t = (Date.now() - start) / duration,
 
3401                                 this._flyToFrame = requestAnimFrame(frame, this);
 
3404                                         this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
 
3405                                         this.getScaleZoom(w0 / w(s), startZoom),
 
3410                                         ._move(targetCenter, targetZoom)
 
3415                 this._moveStart(true, options.noMoveStart);
 
3421         // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
 
3422         // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
 
3423         // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
 
3424         flyToBounds: function (bounds, options) {
 
3425                 var target = this._getBoundsCenterZoom(bounds, options);
 
3426                 return this.flyTo(target.center, target.zoom, options);
 
3429         // @method setMaxBounds(bounds: Bounds): this
 
3430         // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
 
3431         setMaxBounds: function (bounds) {
 
3432                 bounds = toLatLngBounds(bounds);
 
3434                 if (!bounds.isValid()) {
 
3435                         this.options.maxBounds = null;
 
3436                         return this.off('moveend', this._panInsideMaxBounds);
 
3437                 } else if (this.options.maxBounds) {
 
3438                         this.off('moveend', this._panInsideMaxBounds);
 
3441                 this.options.maxBounds = bounds;
 
3444                         this._panInsideMaxBounds();
 
3447                 return this.on('moveend', this._panInsideMaxBounds);
 
3450         // @method setMinZoom(zoom: Number): this
 
3451         // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
 
3452         setMinZoom: function (zoom) {
 
3453                 var oldZoom = this.options.minZoom;
 
3454                 this.options.minZoom = zoom;
 
3456                 if (this._loaded && oldZoom !== zoom) {
 
3457                         this.fire('zoomlevelschange');
 
3459                         if (this.getZoom() < this.options.minZoom) {
 
3460                                 return this.setZoom(zoom);
 
3467         // @method setMaxZoom(zoom: Number): this
 
3468         // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
 
3469         setMaxZoom: function (zoom) {
 
3470                 var oldZoom = this.options.maxZoom;
 
3471                 this.options.maxZoom = zoom;
 
3473                 if (this._loaded && oldZoom !== zoom) {
 
3474                         this.fire('zoomlevelschange');
 
3476                         if (this.getZoom() > this.options.maxZoom) {
 
3477                                 return this.setZoom(zoom);
 
3484         // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
 
3485         // 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.
 
3486         panInsideBounds: function (bounds, options) {
 
3487                 this._enforcingBounds = true;
 
3488                 var center = this.getCenter(),
 
3489                     newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
 
3491                 if (!center.equals(newCenter)) {
 
3492                         this.panTo(newCenter, options);
 
3495                 this._enforcingBounds = false;
 
3499         // @method invalidateSize(options: Zoom/pan options): this
 
3500         // Checks if the map container size changed and updates the map if so —
 
3501         // call it after you've changed the map size dynamically, also animating
 
3502         // pan by default. If `options.pan` is `false`, panning will not occur.
 
3503         // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
 
3504         // that it doesn't happen often even if the method is called many
 
3508         // @method invalidateSize(animate: Boolean): this
 
3509         // Checks if the map container size changed and updates the map if so —
 
3510         // call it after you've changed the map size dynamically, also animating
 
3512         invalidateSize: function (options) {
 
3513                 if (!this._loaded) { return this; }
 
3518                 }, options === true ? {animate: true} : options);
 
3520                 var oldSize = this.getSize();
 
3521                 this._sizeChanged = true;
 
3522                 this._lastCenter = null;
 
3524                 var newSize = this.getSize(),
 
3525                     oldCenter = oldSize.divideBy(2).round(),
 
3526                     newCenter = newSize.divideBy(2).round(),
 
3527                     offset = oldCenter.subtract(newCenter);
 
3529                 if (!offset.x && !offset.y) { return this; }
 
3531                 if (options.animate && options.pan) {
 
3536                                 this._rawPanBy(offset);
 
3541                         if (options.debounceMoveend) {
 
3542                                 clearTimeout(this._sizeTimer);
 
3543                                 this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
 
3545                                 this.fire('moveend');
 
3549                 // @section Map state change events
 
3550                 // @event resize: ResizeEvent
 
3551                 // Fired when the map is resized.
 
3552                 return this.fire('resize', {
 
3558         // @section Methods for modifying map state
 
3559         // @method stop(): this
 
3560         // Stops the currently running `panTo` or `flyTo` animation, if any.
 
3562                 this.setZoom(this._limitZoom(this._zoom));
 
3563                 if (!this.options.zoomSnap) {
 
3564                         this.fire('viewreset');
 
3566                 return this._stop();
 
3569         // @section Geolocation methods
 
3570         // @method locate(options?: Locate options): this
 
3571         // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
 
3572         // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
 
3573         // and optionally sets the map view to the user's location with respect to
 
3574         // detection accuracy (or to the world view if geolocation failed).
 
3575         // Note that, if your page doesn't use HTTPS, this method will fail in
 
3576         // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
 
3577         // See `Locate options` for more details.
 
3578         locate: function (options) {
 
3580                 options = this._locateOptions = extend({
 
3584                         // maxZoom: <Number>
 
3586                         // enableHighAccuracy: false
 
3589                 if (!('geolocation' in navigator)) {
 
3590                         this._handleGeolocationError({
 
3592                                 message: 'Geolocation not supported.'
 
3597                 var onResponse = bind(this._handleGeolocationResponse, this),
 
3598                     onError = bind(this._handleGeolocationError, this);
 
3600                 if (options.watch) {
 
3601                         this._locationWatchId =
 
3602                                 navigator.geolocation.watchPosition(onResponse, onError, options);
 
3604                         navigator.geolocation.getCurrentPosition(onResponse, onError, options);
 
3609         // @method stopLocate(): this
 
3610         // Stops watching location previously initiated by `map.locate({watch: true})`
 
3611         // and aborts resetting the map view if map.locate was called with
 
3612         // `{setView: true}`.
 
3613         stopLocate: function () {
 
3614                 if (navigator.geolocation && navigator.geolocation.clearWatch) {
 
3615                         navigator.geolocation.clearWatch(this._locationWatchId);
 
3617                 if (this._locateOptions) {
 
3618                         this._locateOptions.setView = false;
 
3623         _handleGeolocationError: function (error) {
 
3625                     message = error.message ||
 
3626                             (c === 1 ? 'permission denied' :
 
3627                             (c === 2 ? 'position unavailable' : 'timeout'));
 
3629                 if (this._locateOptions.setView && !this._loaded) {
 
3633                 // @section Location events
 
3634                 // @event locationerror: ErrorEvent
 
3635                 // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
 
3636                 this.fire('locationerror', {
 
3638                         message: 'Geolocation error: ' + message + '.'
 
3642         _handleGeolocationResponse: function (pos) {
 
3643                 var lat = pos.coords.latitude,
 
3644                     lng = pos.coords.longitude,
 
3645                     latlng = new LatLng(lat, lng),
 
3646                     bounds = latlng.toBounds(pos.coords.accuracy * 2),
 
3647                     options = this._locateOptions;
 
3649                 if (options.setView) {
 
3650                         var zoom = this.getBoundsZoom(bounds);
 
3651                         this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
 
3657                         timestamp: pos.timestamp
 
3660                 for (var i in pos.coords) {
 
3661                         if (typeof pos.coords[i] === 'number') {
 
3662                                 data[i] = pos.coords[i];
 
3666                 // @event locationfound: LocationEvent
 
3667                 // Fired when geolocation (using the [`locate`](#map-locate) method)
 
3668                 // went successfully.
 
3669                 this.fire('locationfound', data);
 
3672         // TODO Appropriate docs section?
 
3673         // @section Other Methods
 
3674         // @method addHandler(name: String, HandlerClass: Function): this
 
3675         // Adds a new `Handler` to the map, given its name and constructor function.
 
3676         addHandler: function (name, HandlerClass) {
 
3677                 if (!HandlerClass) { return this; }
 
3679                 var handler = this[name] = new HandlerClass(this);
 
3681                 this._handlers.push(handler);
 
3683                 if (this.options[name]) {
 
3690         // @method remove(): this
 
3691         // Destroys the map and clears all related event listeners.
 
3692         remove: function () {
 
3694                 this._initEvents(true);
 
3696                 if (this._containerId !== this._container._leaflet_id) {
 
3697                         throw new Error('Map container is being reused by another instance');
 
3701                         // throws error in IE6-8
 
3702                         delete this._container._leaflet_id;
 
3703                         delete this._containerId;
 
3706                         this._container._leaflet_id = undefined;
 
3708                         this._containerId = undefined;
 
3711                 if (this._locationWatchId !== undefined) {
 
3717                 remove(this._mapPane);
 
3719                 if (this._clearControlPos) {
 
3720                         this._clearControlPos();
 
3722                 if (this._resizeRequest) {
 
3723                         cancelAnimFrame(this._resizeRequest);
 
3724                         this._resizeRequest = null;
 
3727                 this._clearHandlers();
 
3730                         // @section Map state change events
 
3731                         // @event unload: Event
 
3732                         // Fired when the map is destroyed with [remove](#map-remove) method.
 
3733                         this.fire('unload');
 
3737                 for (i in this._layers) {
 
3738                         this._layers[i].remove();
 
3740                 for (i in this._panes) {
 
3741                         remove(this._panes[i]);
 
3746                 delete this._mapPane;
 
3747                 delete this._renderer;
 
3752         // @section Other Methods
 
3753         // @method createPane(name: String, container?: HTMLElement): HTMLElement
 
3754         // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
 
3755         // then returns it. The pane is created as a child of `container`, or
 
3756         // as a child of the main map pane if not set.
 
3757         createPane: function (name, container) {
 
3758                 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
 
3759                     pane = create$1('div', className, container || this._mapPane);
 
3762                         this._panes[name] = pane;
 
3767         // @section Methods for Getting Map State
 
3769         // @method getCenter(): LatLng
 
3770         // Returns the geographical center of the map view
 
3771         getCenter: function () {
 
3772                 this._checkIfLoaded();
 
3774                 if (this._lastCenter && !this._moved()) {
 
3775                         return this._lastCenter;
 
3777                 return this.layerPointToLatLng(this._getCenterLayerPoint());
 
3780         // @method getZoom(): Number
 
3781         // Returns the current zoom level of the map view
 
3782         getZoom: function () {
 
3786         // @method getBounds(): LatLngBounds
 
3787         // Returns the geographical bounds visible in the current map view
 
3788         getBounds: function () {
 
3789                 var bounds = this.getPixelBounds(),
 
3790                     sw = this.unproject(bounds.getBottomLeft()),
 
3791                     ne = this.unproject(bounds.getTopRight());
 
3793                 return new LatLngBounds(sw, ne);
 
3796         // @method getMinZoom(): Number
 
3797         // 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.
 
3798         getMinZoom: function () {
 
3799                 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
 
3802         // @method getMaxZoom(): Number
 
3803         // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
 
3804         getMaxZoom: function () {
 
3805                 return this.options.maxZoom === undefined ?
 
3806                         (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
 
3807                         this.options.maxZoom;
 
3810         // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number
 
3811         // Returns the maximum zoom level on which the given bounds fit to the map
 
3812         // view in its entirety. If `inside` (optional) is set to `true`, the method
 
3813         // instead returns the minimum zoom level on which the map view fits into
 
3814         // the given bounds in its entirety.
 
3815         getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
 
3816                 bounds = toLatLngBounds(bounds);
 
3817                 padding = toPoint(padding || [0, 0]);
 
3819                 var zoom = this.getZoom() || 0,
 
3820                     min = this.getMinZoom(),
 
3821                     max = this.getMaxZoom(),
 
3822                     nw = bounds.getNorthWest(),
 
3823                     se = bounds.getSouthEast(),
 
3824                     size = this.getSize().subtract(padding),
 
3825                     boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
 
3826                     snap = any3d ? this.options.zoomSnap : 1,
 
3827                     scalex = size.x / boundsSize.x,
 
3828                     scaley = size.y / boundsSize.y,
 
3829                     scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
 
3831                 zoom = this.getScaleZoom(scale, zoom);
 
3834                         zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
 
3835                         zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
 
3838                 return Math.max(min, Math.min(max, zoom));
 
3841         // @method getSize(): Point
 
3842         // Returns the current size of the map container (in pixels).
 
3843         getSize: function () {
 
3844                 if (!this._size || this._sizeChanged) {
 
3845                         this._size = new Point(
 
3846                                 this._container.clientWidth || 0,
 
3847                                 this._container.clientHeight || 0);
 
3849                         this._sizeChanged = false;
 
3851                 return this._size.clone();
 
3854         // @method getPixelBounds(): Bounds
 
3855         // Returns the bounds of the current map view in projected pixel
 
3856         // coordinates (sometimes useful in layer and overlay implementations).
 
3857         getPixelBounds: function (center, zoom) {
 
3858                 var topLeftPoint = this._getTopLeftPoint(center, zoom);
 
3859                 return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
 
3862         // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
 
3863         // the map pane? "left point of the map layer" can be confusing, specially
 
3864         // since there can be negative offsets.
 
3865         // @method getPixelOrigin(): Point
 
3866         // Returns the projected pixel coordinates of the top left point of
 
3867         // the map layer (useful in custom layer and overlay implementations).
 
3868         getPixelOrigin: function () {
 
3869                 this._checkIfLoaded();
 
3870                 return this._pixelOrigin;
 
3873         // @method getPixelWorldBounds(zoom?: Number): Bounds
 
3874         // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
 
3875         // If `zoom` is omitted, the map's current zoom level is used.
 
3876         getPixelWorldBounds: function (zoom) {
 
3877                 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
 
3880         // @section Other Methods
 
3882         // @method getPane(pane: String|HTMLElement): HTMLElement
 
3883         // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
 
3884         getPane: function (pane) {
 
3885                 return typeof pane === 'string' ? this._panes[pane] : pane;
 
3888         // @method getPanes(): Object
 
3889         // Returns a plain object containing the names of all [panes](#map-pane) as keys and
 
3890         // the panes as values.
 
3891         getPanes: function () {
 
3895         // @method getContainer: HTMLElement
 
3896         // Returns the HTML element that contains the map.
 
3897         getContainer: function () {
 
3898                 return this._container;
 
3902         // @section Conversion Methods
 
3904         // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
 
3905         // Returns the scale factor to be applied to a map transition from zoom level
 
3906         // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
 
3907         getZoomScale: function (toZoom, fromZoom) {
 
3908                 // TODO replace with universal implementation after refactoring projections
 
3909                 var crs = this.options.crs;
 
3910                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
 
3911                 return crs.scale(toZoom) / crs.scale(fromZoom);
 
3914         // @method getScaleZoom(scale: Number, fromZoom: Number): Number
 
3915         // Returns the zoom level that the map would end up at, if it is at `fromZoom`
 
3916         // level and everything is scaled by a factor of `scale`. Inverse of
 
3917         // [`getZoomScale`](#map-getZoomScale).
 
3918         getScaleZoom: function (scale, fromZoom) {
 
3919                 var crs = this.options.crs;
 
3920                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
 
3921                 var zoom = crs.zoom(scale * crs.scale(fromZoom));
 
3922                 return isNaN(zoom) ? Infinity : zoom;
 
3925         // @method project(latlng: LatLng, zoom: Number): Point
 
3926         // Projects a geographical coordinate `LatLng` according to the projection
 
3927         // of the map's CRS, then scales it according to `zoom` and the CRS's
 
3928         // `Transformation`. The result is pixel coordinate relative to
 
3930         project: function (latlng, zoom) {
 
3931                 zoom = zoom === undefined ? this._zoom : zoom;
 
3932                 return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
 
3935         // @method unproject(point: Point, zoom: Number): LatLng
 
3936         // Inverse of [`project`](#map-project).
 
3937         unproject: function (point, zoom) {
 
3938                 zoom = zoom === undefined ? this._zoom : zoom;
 
3939                 return this.options.crs.pointToLatLng(toPoint(point), zoom);
 
3942         // @method layerPointToLatLng(point: Point): LatLng
 
3943         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
 
3944         // returns the corresponding geographical coordinate (for the current zoom level).
 
3945         layerPointToLatLng: function (point) {
 
3946                 var projectedPoint = toPoint(point).add(this.getPixelOrigin());
 
3947                 return this.unproject(projectedPoint);
 
3950         // @method latLngToLayerPoint(latlng: LatLng): Point
 
3951         // Given a geographical coordinate, returns the corresponding pixel coordinate
 
3952         // relative to the [origin pixel](#map-getpixelorigin).
 
3953         latLngToLayerPoint: function (latlng) {
 
3954                 var projectedPoint = this.project(toLatLng(latlng))._round();
 
3955                 return projectedPoint._subtract(this.getPixelOrigin());
 
3958         // @method wrapLatLng(latlng: LatLng): LatLng
 
3959         // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
 
3960         // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
 
3962         // By default this means longitude is wrapped around the dateline so its
 
3963         // value is between -180 and +180 degrees.
 
3964         wrapLatLng: function (latlng) {
 
3965                 return this.options.crs.wrapLatLng(toLatLng(latlng));
 
3968         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
 
3969         // Returns a `LatLngBounds` with the same size as the given one, ensuring that
 
3970         // its center is within the CRS's bounds.
 
3971         // By default this means the center longitude is wrapped around the dateline so its
 
3972         // value is between -180 and +180 degrees, and the majority of the bounds
 
3973         // overlaps the CRS's bounds.
 
3974         wrapLatLngBounds: function (latlng) {
 
3975                 return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
 
3978         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
 
3979         // Returns the distance between two geographical coordinates according to
 
3980         // the map's CRS. By default this measures distance in meters.
 
3981         distance: function (latlng1, latlng2) {
 
3982                 return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
 
3985         // @method containerPointToLayerPoint(point: Point): Point
 
3986         // Given a pixel coordinate relative to the map container, returns the corresponding
 
3987         // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
 
3988         containerPointToLayerPoint: function (point) { // (Point)
 
3989                 return toPoint(point).subtract(this._getMapPanePos());
 
3992         // @method layerPointToContainerPoint(point: Point): Point
 
3993         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
 
3994         // returns the corresponding pixel coordinate relative to the map container.
 
3995         layerPointToContainerPoint: function (point) { // (Point)
 
3996                 return toPoint(point).add(this._getMapPanePos());
 
3999         // @method containerPointToLatLng(point: Point): LatLng
 
4000         // Given a pixel coordinate relative to the map container, returns
 
4001         // the corresponding geographical coordinate (for the current zoom level).
 
4002         containerPointToLatLng: function (point) {
 
4003                 var layerPoint = this.containerPointToLayerPoint(toPoint(point));
 
4004                 return this.layerPointToLatLng(layerPoint);
 
4007         // @method latLngToContainerPoint(latlng: LatLng): Point
 
4008         // Given a geographical coordinate, returns the corresponding pixel coordinate
 
4009         // relative to the map container.
 
4010         latLngToContainerPoint: function (latlng) {
 
4011                 return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
 
4014         // @method mouseEventToContainerPoint(ev: MouseEvent): Point
 
4015         // Given a MouseEvent object, returns the pixel coordinate relative to the
 
4016         // map container where the event took place.
 
4017         mouseEventToContainerPoint: function (e) {
 
4018                 return getMousePosition(e, this._container);
 
4021         // @method mouseEventToLayerPoint(ev: MouseEvent): Point
 
4022         // Given a MouseEvent object, returns the pixel coordinate relative to
 
4023         // the [origin pixel](#map-getpixelorigin) where the event took place.
 
4024         mouseEventToLayerPoint: function (e) {
 
4025                 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
 
4028         // @method mouseEventToLatLng(ev: MouseEvent): LatLng
 
4029         // Given a MouseEvent object, returns geographical coordinate where the
 
4030         // event took place.
 
4031         mouseEventToLatLng: function (e) { // (MouseEvent)
 
4032                 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
 
4036         // map initialization methods
 
4038         _initContainer: function (id) {
 
4039                 var container = this._container = get(id);
 
4042                         throw new Error('Map container not found.');
 
4043                 } else if (container._leaflet_id) {
 
4044                         throw new Error('Map container is already initialized.');
 
4047                 on(container, 'scroll', this._onScroll, this);
 
4048                 this._containerId = stamp(container);
 
4051         _initLayout: function () {
 
4052                 var container = this._container;
 
4054                 this._fadeAnimated = this.options.fadeAnimation && any3d;
 
4056                 addClass(container, 'leaflet-container' +
 
4057                         (touch ? ' leaflet-touch' : '') +
 
4058                         (retina ? ' leaflet-retina' : '') +
 
4059                         (ielt9 ? ' leaflet-oldie' : '') +
 
4060                         (safari ? ' leaflet-safari' : '') +
 
4061                         (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
 
4063                 var position = getStyle(container, 'position');
 
4065                 if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
 
4066                         container.style.position = 'relative';
 
4071                 if (this._initControlPos) {
 
4072                         this._initControlPos();
 
4076         _initPanes: function () {
 
4077                 var panes = this._panes = {};
 
4078                 this._paneRenderers = {};
 
4082                 // Panes are DOM elements used to control the ordering of layers on the map. You
 
4083                 // can access panes with [`map.getPane`](#map-getpane) or
 
4084                 // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
 
4085                 // [`map.createPane`](#map-createpane) method.
 
4087                 // Every map has the following default panes that differ only in zIndex.
 
4089                 // @pane mapPane: HTMLElement = 'auto'
 
4090                 // Pane that contains all other map panes
 
4092                 this._mapPane = this.createPane('mapPane', this._container);
 
4093                 setPosition(this._mapPane, new Point(0, 0));
 
4095                 // @pane tilePane: HTMLElement = 200
 
4096                 // Pane for `GridLayer`s and `TileLayer`s
 
4097                 this.createPane('tilePane');
 
4098                 // @pane overlayPane: HTMLElement = 400
 
4099                 // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s
 
4100                 this.createPane('shadowPane');
 
4101                 // @pane shadowPane: HTMLElement = 500
 
4102                 // Pane for overlay shadows (e.g. `Marker` shadows)
 
4103                 this.createPane('overlayPane');
 
4104                 // @pane markerPane: HTMLElement = 600
 
4105                 // Pane for `Icon`s of `Marker`s
 
4106                 this.createPane('markerPane');
 
4107                 // @pane tooltipPane: HTMLElement = 650
 
4108                 // Pane for `Tooltip`s.
 
4109                 this.createPane('tooltipPane');
 
4110                 // @pane popupPane: HTMLElement = 700
 
4111                 // Pane for `Popup`s.
 
4112                 this.createPane('popupPane');
 
4114                 if (!this.options.markerZoomAnimation) {
 
4115                         addClass(panes.markerPane, 'leaflet-zoom-hide');
 
4116                         addClass(panes.shadowPane, 'leaflet-zoom-hide');
 
4121         // private methods that modify map state
 
4123         // @section Map state change events
 
4124         _resetView: function (center, zoom) {
 
4125                 setPosition(this._mapPane, new Point(0, 0));
 
4127                 var loading = !this._loaded;
 
4128                 this._loaded = true;
 
4129                 zoom = this._limitZoom(zoom);
 
4131                 this.fire('viewprereset');
 
4133                 var zoomChanged = this._zoom !== zoom;
 
4135                         ._moveStart(zoomChanged, false)
 
4136                         ._move(center, zoom)
 
4137                         ._moveEnd(zoomChanged);
 
4139                 // @event viewreset: Event
 
4140                 // Fired when the map needs to redraw its content (this usually happens
 
4141                 // on map zoom or load). Very useful for creating custom overlays.
 
4142                 this.fire('viewreset');
 
4144                 // @event load: Event
 
4145                 // Fired when the map is initialized (when its center and zoom are set
 
4146                 // for the first time).
 
4152         _moveStart: function (zoomChanged, noMoveStart) {
 
4153                 // @event zoomstart: Event
 
4154                 // Fired when the map zoom is about to change (e.g. before zoom animation).
 
4155                 // @event movestart: Event
 
4156                 // Fired when the view of the map starts changing (e.g. user starts dragging the map).
 
4158                         this.fire('zoomstart');
 
4161                         this.fire('movestart');
 
4166         _move: function (center, zoom, data) {
 
4167                 if (zoom === undefined) {
 
4170                 var zoomChanged = this._zoom !== zoom;
 
4173                 this._lastCenter = center;
 
4174                 this._pixelOrigin = this._getNewPixelOrigin(center);
 
4176                 // @event zoom: Event
 
4177                 // Fired repeatedly during any change in zoom level, including zoom
 
4178                 // and fly animations.
 
4179                 if (zoomChanged || (data && data.pinch)) {      // Always fire 'zoom' if pinching because #3530
 
4180                         this.fire('zoom', data);
 
4183                 // @event move: Event
 
4184                 // Fired repeatedly during any movement of the map, including pan and
 
4186                 return this.fire('move', data);
 
4189         _moveEnd: function (zoomChanged) {
 
4190                 // @event zoomend: Event
 
4191                 // Fired when the map has changed, after any animations.
 
4193                         this.fire('zoomend');
 
4196                 // @event moveend: Event
 
4197                 // Fired when the center of the map stops changing (e.g. user stopped
 
4198                 // dragging the map).
 
4199                 return this.fire('moveend');
 
4202         _stop: function () {
 
4203                 cancelAnimFrame(this._flyToFrame);
 
4204                 if (this._panAnim) {
 
4205                         this._panAnim.stop();
 
4210         _rawPanBy: function (offset) {
 
4211                 setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
 
4214         _getZoomSpan: function () {
 
4215                 return this.getMaxZoom() - this.getMinZoom();
 
4218         _panInsideMaxBounds: function () {
 
4219                 if (!this._enforcingBounds) {
 
4220                         this.panInsideBounds(this.options.maxBounds);
 
4224         _checkIfLoaded: function () {
 
4225                 if (!this._loaded) {
 
4226                         throw new Error('Set map center and zoom first.');
 
4230         // DOM event handling
 
4232         // @section Interaction events
 
4233         _initEvents: function (remove$$1) {
 
4235                 this._targets[stamp(this._container)] = this;
 
4237                 var onOff = remove$$1 ? off : on;
 
4239                 // @event click: MouseEvent
 
4240                 // Fired when the user clicks (or taps) the map.
 
4241                 // @event dblclick: MouseEvent
 
4242                 // Fired when the user double-clicks (or double-taps) the map.
 
4243                 // @event mousedown: MouseEvent
 
4244                 // Fired when the user pushes the mouse button on the map.
 
4245                 // @event mouseup: MouseEvent
 
4246                 // Fired when the user releases the mouse button on the map.
 
4247                 // @event mouseover: MouseEvent
 
4248                 // Fired when the mouse enters the map.
 
4249                 // @event mouseout: MouseEvent
 
4250                 // Fired when the mouse leaves the map.
 
4251                 // @event mousemove: MouseEvent
 
4252                 // Fired while the mouse moves over the map.
 
4253                 // @event contextmenu: MouseEvent
 
4254                 // Fired when the user pushes the right mouse button on the map, prevents
 
4255                 // default browser context menu from showing if there are listeners on
 
4256                 // this event. Also fired on mobile when the user holds a single touch
 
4257                 // for a second (also called long press).
 
4258                 // @event keypress: KeyboardEvent
 
4259                 // Fired when the user presses a key from the keyboard while the map is focused.
 
4260                 onOff(this._container, 'click dblclick mousedown mouseup ' +
 
4261                         'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
 
4263                 if (this.options.trackResize) {
 
4264                         onOff(window, 'resize', this._onResize, this);
 
4267                 if (any3d && this.options.transform3DLimit) {
 
4268                         (remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
 
4272         _onResize: function () {
 
4273                 cancelAnimFrame(this._resizeRequest);
 
4274                 this._resizeRequest = requestAnimFrame(
 
4275                         function () { this.invalidateSize({debounceMoveend: true}); }, this);
 
4278         _onScroll: function () {
 
4279                 this._container.scrollTop  = 0;
 
4280                 this._container.scrollLeft = 0;
 
4283         _onMoveEnd: function () {
 
4284                 var pos = this._getMapPanePos();
 
4285                 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
 
4286                         // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
 
4287                         // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
 
4288                         this._resetView(this.getCenter(), this.getZoom());
 
4292         _findEventTargets: function (e, type) {
 
4295                     isHover = type === 'mouseout' || type === 'mouseover',
 
4296                     src = e.target || e.srcElement,
 
4300                         target = this._targets[stamp(src)];
 
4301                         if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
 
4302                                 // Prevent firing click after you just dragged an object.
 
4306                         if (target && target.listens(type, true)) {
 
4307                                 if (isHover && !isExternalTarget(src, e)) { break; }
 
4308                                 targets.push(target);
 
4309                                 if (isHover) { break; }
 
4311                         if (src === this._container) { break; }
 
4312                         src = src.parentNode;
 
4314                 if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) {
 
4320         _handleDOMEvent: function (e) {
 
4321                 if (!this._loaded || skipped(e)) { return; }
 
4325                 if (type === 'mousedown' || type === 'keypress') {
 
4326                         // prevents outline when clicking on keyboard-focusable element
 
4327                         preventOutline(e.target || e.srcElement);
 
4330                 this._fireDOMEvent(e, type);
 
4333         _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
 
4335         _fireDOMEvent: function (e, type, targets) {
 
4337                 if (e.type === 'click') {
 
4338                         // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
 
4339                         // @event preclick: MouseEvent
 
4340                         // Fired before mouse click on the map (sometimes useful when you
 
4341                         // want something to happen on click before any existing click
 
4342                         // handlers start running).
 
4343                         var synth = extend({}, e);
 
4344                         synth.type = 'preclick';
 
4345                         this._fireDOMEvent(synth, synth.type, targets);
 
4348                 if (e._stopped) { return; }
 
4350                 // Find the layer the event is propagating from and its parents.
 
4351                 targets = (targets || []).concat(this._findEventTargets(e, type));
 
4353                 if (!targets.length) { return; }
 
4355                 var target = targets[0];
 
4356                 if (type === 'contextmenu' && target.listens(type, true)) {
 
4364                 if (e.type !== 'keypress') {
 
4365                         var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
 
4366                         data.containerPoint = isMarker ?
 
4367                                 this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
 
4368                         data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
 
4369                         data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
 
4372                 for (var i = 0; i < targets.length; i++) {
 
4373                         targets[i].fire(type, data, true);
 
4374                         if (data.originalEvent._stopped ||
 
4375                                 (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
 
4379         _draggableMoved: function (obj) {
 
4380                 obj = obj.dragging && obj.dragging.enabled() ? obj : this;
 
4381                 return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
 
4384         _clearHandlers: function () {
 
4385                 for (var i = 0, len = this._handlers.length; i < len; i++) {
 
4386                         this._handlers[i].disable();
 
4390         // @section Other Methods
 
4392         // @method whenReady(fn: Function, context?: Object): this
 
4393         // Runs the given function `fn` when the map gets initialized with
 
4394         // a view (center and zoom) and at least one layer, or immediately
 
4395         // if it's already initialized, optionally passing a function context.
 
4396         whenReady: function (callback, context) {
 
4398                         callback.call(context || this, {target: this});
 
4400                         this.on('load', callback, context);
 
4406         // private methods for getting map state
 
4408         _getMapPanePos: function () {
 
4409                 return getPosition(this._mapPane) || new Point(0, 0);
 
4412         _moved: function () {
 
4413                 var pos = this._getMapPanePos();
 
4414                 return pos && !pos.equals([0, 0]);
 
4417         _getTopLeftPoint: function (center, zoom) {
 
4418                 var pixelOrigin = center && zoom !== undefined ?
 
4419                         this._getNewPixelOrigin(center, zoom) :
 
4420                         this.getPixelOrigin();
 
4421                 return pixelOrigin.subtract(this._getMapPanePos());
 
4424         _getNewPixelOrigin: function (center, zoom) {
 
4425                 var viewHalf = this.getSize()._divideBy(2);
 
4426                 return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
 
4429         _latLngToNewLayerPoint: function (latlng, zoom, center) {
 
4430                 var topLeft = this._getNewPixelOrigin(center, zoom);
 
4431                 return this.project(latlng, zoom)._subtract(topLeft);
 
4434         _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
 
4435                 var topLeft = this._getNewPixelOrigin(center, zoom);
 
4437                         this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
 
4438                         this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
 
4439                         this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
 
4440                         this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
 
4444         // layer point of the current center
 
4445         _getCenterLayerPoint: function () {
 
4446                 return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
 
4449         // offset of the specified place to the current center in pixels
 
4450         _getCenterOffset: function (latlng) {
 
4451                 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
 
4454         // adjust center for view to get inside bounds
 
4455         _limitCenter: function (center, zoom, bounds) {
 
4457                 if (!bounds) { return center; }
 
4459                 var centerPoint = this.project(center, zoom),
 
4460                     viewHalf = this.getSize().divideBy(2),
 
4461                     viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
 
4462                     offset = this._getBoundsOffset(viewBounds, bounds, zoom);
 
4464                 // If offset is less than a pixel, ignore.
 
4465                 // This prevents unstable projections from getting into
 
4466                 // an infinite loop of tiny offsets.
 
4467                 if (offset.round().equals([0, 0])) {
 
4471                 return this.unproject(centerPoint.add(offset), zoom);
 
4474         // adjust offset for view to get inside bounds
 
4475         _limitOffset: function (offset, bounds) {
 
4476                 if (!bounds) { return offset; }
 
4478                 var viewBounds = this.getPixelBounds(),
 
4479                     newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
 
4481                 return offset.add(this._getBoundsOffset(newBounds, bounds));
 
4484         // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
 
4485         _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
 
4486                 var projectedMaxBounds = toBounds(
 
4487                         this.project(maxBounds.getNorthEast(), zoom),
 
4488                         this.project(maxBounds.getSouthWest(), zoom)
 
4490                     minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
 
4491                     maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
 
4493                     dx = this._rebound(minOffset.x, -maxOffset.x),
 
4494                     dy = this._rebound(minOffset.y, -maxOffset.y);
 
4496                 return new Point(dx, dy);
 
4499         _rebound: function (left, right) {
 
4500                 return left + right > 0 ?
 
4501                         Math.round(left - right) / 2 :
 
4502                         Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
 
4505         _limitZoom: function (zoom) {
 
4506                 var min = this.getMinZoom(),
 
4507                     max = this.getMaxZoom(),
 
4508                     snap = any3d ? this.options.zoomSnap : 1;
 
4510                         zoom = Math.round(zoom / snap) * snap;
 
4512                 return Math.max(min, Math.min(max, zoom));
 
4515         _onPanTransitionStep: function () {
 
4519         _onPanTransitionEnd: function () {
 
4520                 removeClass(this._mapPane, 'leaflet-pan-anim');
 
4521                 this.fire('moveend');
 
4524         _tryAnimatedPan: function (center, options) {
 
4525                 // difference between the new and current centers in pixels
 
4526                 var offset = this._getCenterOffset(center)._trunc();
 
4528                 // don't animate too far unless animate: true specified in options
 
4529                 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
 
4531                 this.panBy(offset, options);
 
4536         _createAnimProxy: function () {
 
4538                 var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
 
4539                 this._panes.mapPane.appendChild(proxy);
 
4541                 this.on('zoomanim', function (e) {
 
4542                         var prop = TRANSFORM,
 
4543                             transform = this._proxy.style[prop];
 
4545                         setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
 
4547                         // workaround for case when transform is the same and so transitionend event is not fired
 
4548                         if (transform === this._proxy.style[prop] && this._animatingZoom) {
 
4549                                 this._onZoomTransitionEnd();
 
4553                 this.on('load moveend', function () {
 
4554                         var c = this.getCenter(),
 
4556                         setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
 
4559                 this._on('unload', this._destroyAnimProxy, this);
 
4562         _destroyAnimProxy: function () {
 
4563                 remove(this._proxy);
 
4567         _catchTransitionEnd: function (e) {
 
4568                 if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
 
4569                         this._onZoomTransitionEnd();
 
4573         _nothingToAnimate: function () {
 
4574                 return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
 
4577         _tryAnimatedZoom: function (center, zoom, options) {
 
4579                 if (this._animatingZoom) { return true; }
 
4581                 options = options || {};
 
4583                 // don't animate if disabled, not supported or zoom difference is too large
 
4584                 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
 
4585                         Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
 
4587                 // offset is the pixel coords of the zoom origin relative to the current center
 
4588                 var scale = this.getZoomScale(zoom),
 
4589                     offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
 
4591                 // don't animate if the zoom origin isn't within one screen from the current center, unless forced
 
4592                 if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
 
4594                 requestAnimFrame(function () {
 
4596                             ._moveStart(true, false)
 
4597                             ._animateZoom(center, zoom, true);
 
4603         _animateZoom: function (center, zoom, startAnim, noUpdate) {
 
4604                 if (!this._mapPane) { return; }
 
4607                         this._animatingZoom = true;
 
4609                         // remember what center/zoom to set after animation
 
4610                         this._animateToCenter = center;
 
4611                         this._animateToZoom = zoom;
 
4613                         addClass(this._mapPane, 'leaflet-zoom-anim');
 
4616                 // @event zoomanim: ZoomAnimEvent
 
4617                 // Fired on every frame of a zoom animation
 
4618                 this.fire('zoomanim', {
 
4624                 // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
 
4625                 setTimeout(bind(this._onZoomTransitionEnd, this), 250);
 
4628         _onZoomTransitionEnd: function () {
 
4629                 if (!this._animatingZoom) { return; }
 
4631                 if (this._mapPane) {
 
4632                         removeClass(this._mapPane, 'leaflet-zoom-anim');
 
4635                 this._animatingZoom = false;
 
4637                 this._move(this._animateToCenter, this._animateToZoom);
 
4639                 // This anim frame should prevent an obscure iOS webkit tile loading race condition.
 
4640                 requestAnimFrame(function () {
 
4641                         this._moveEnd(true);
 
4648 // @factory L.map(id: String, options?: Map options)
 
4649 // Instantiates a map object given the DOM ID of a `<div>` element
 
4650 // and optionally an object literal with `Map options`.
 
4653 // @factory L.map(el: HTMLElement, options?: Map options)
 
4654 // Instantiates a map object given an instance of a `<div>` HTML element
 
4655 // and optionally an object literal with `Map options`.
 
4656 function createMap(id, options) {
 
4657         return new Map(id, options);
 
4665  * L.Control is a base class for implementing map controls. Handles positioning.
 
4666  * All other controls extend from this class.
 
4669 var Control = Class.extend({
 
4671         // @aka Control options
 
4673                 // @option position: String = 'topright'
 
4674                 // The position of the control (one of the map corners). Possible values are `'topleft'`,
 
4675                 // `'topright'`, `'bottomleft'` or `'bottomright'`
 
4676                 position: 'topright'
 
4679         initialize: function (options) {
 
4680                 setOptions(this, options);
 
4684          * Classes extending L.Control will inherit the following methods:
 
4686          * @method getPosition: string
 
4687          * Returns the position of the control.
 
4689         getPosition: function () {
 
4690                 return this.options.position;
 
4693         // @method setPosition(position: string): this
 
4694         // Sets the position of the control.
 
4695         setPosition: function (position) {
 
4696                 var map = this._map;
 
4699                         map.removeControl(this);
 
4702                 this.options.position = position;
 
4705                         map.addControl(this);
 
4711         // @method getContainer: HTMLElement
 
4712         // Returns the HTMLElement that contains the control.
 
4713         getContainer: function () {
 
4714                 return this._container;
 
4717         // @method addTo(map: Map): this
 
4718         // Adds the control to the given map.
 
4719         addTo: function (map) {
 
4723                 var container = this._container = this.onAdd(map),
 
4724                     pos = this.getPosition(),
 
4725                     corner = map._controlCorners[pos];
 
4727                 addClass(container, 'leaflet-control');
 
4729                 if (pos.indexOf('bottom') !== -1) {
 
4730                         corner.insertBefore(container, corner.firstChild);
 
4732                         corner.appendChild(container);
 
4738         // @method remove: this
 
4739         // Removes the control from the map it is currently active on.
 
4740         remove: function () {
 
4745                 remove(this._container);
 
4747                 if (this.onRemove) {
 
4748                         this.onRemove(this._map);
 
4756         _refocusOnMap: function (e) {
 
4757                 // if map exists and event is not a keyboard event
 
4758                 if (this._map && e && e.screenX > 0 && e.screenY > 0) {
 
4759                         this._map.getContainer().focus();
 
4764 var control = function (options) {
 
4765         return new Control(options);
 
4768 /* @section Extension methods
 
4771  * Every control should extend from `L.Control` and (re-)implement the following methods.
 
4773  * @method onAdd(map: Map): HTMLElement
 
4774  * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
 
4776  * @method onRemove(map: Map)
 
4777  * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
 
4781  * @section Methods for Layers and Controls
 
4784         // @method addControl(control: Control): this
 
4785         // Adds the given control to the map
 
4786         addControl: function (control) {
 
4787                 control.addTo(this);
 
4791         // @method removeControl(control: Control): this
 
4792         // Removes the given control from the map
 
4793         removeControl: function (control) {
 
4798         _initControlPos: function () {
 
4799                 var corners = this._controlCorners = {},
 
4801                     container = this._controlContainer =
 
4802                             create$1('div', l + 'control-container', this._container);
 
4804                 function createCorner(vSide, hSide) {
 
4805                         var className = l + vSide + ' ' + l + hSide;
 
4807                         corners[vSide + hSide] = create$1('div', className, container);
 
4810                 createCorner('top', 'left');
 
4811                 createCorner('top', 'right');
 
4812                 createCorner('bottom', 'left');
 
4813                 createCorner('bottom', 'right');
 
4816         _clearControlPos: function () {
 
4817                 for (var i in this._controlCorners) {
 
4818                         remove(this._controlCorners[i]);
 
4820                 remove(this._controlContainer);
 
4821                 delete this._controlCorners;
 
4822                 delete this._controlContainer;
 
4827  * @class Control.Layers
 
4828  * @aka L.Control.Layers
 
4831  * 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`.
 
4836  * var baseLayers = {
 
4838  *      "OpenStreetMap": osm
 
4843  *      "Roads": roadsLayer
 
4846  * L.control.layers(baseLayers, overlays).addTo(map);
 
4849  * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
 
4853  *     "<someName1>": layer1,
 
4854  *     "<someName2>": layer2
 
4858  * The layer names can contain HTML, which allows you to add additional styling to the items:
 
4861  * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
 
4865 var Layers = Control.extend({
 
4867         // @aka Control.Layers options
 
4869                 // @option collapsed: Boolean = true
 
4870                 // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
 
4872                 position: 'topright',
 
4874                 // @option autoZIndex: Boolean = true
 
4875                 // 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.
 
4878                 // @option hideSingleBase: Boolean = false
 
4879                 // If `true`, the base layers in the control will be hidden when there is only one.
 
4880                 hideSingleBase: false,
 
4882                 // @option sortLayers: Boolean = false
 
4883                 // Whether to sort the layers. When `false`, layers will keep the order
 
4884                 // in which they were added to the control.
 
4887                 // @option sortFunction: Function = *
 
4888                 // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
 
4889                 // that will be used for sorting the layers, when `sortLayers` is `true`.
 
4890                 // The function receives both the `L.Layer` instances and their names, as in
 
4891                 // `sortFunction(layerA, layerB, nameA, nameB)`.
 
4892                 // By default, it sorts layers alphabetically by their name.
 
4893                 sortFunction: function (layerA, layerB, nameA, nameB) {
 
4894                         return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
 
4898         initialize: function (baseLayers, overlays, options) {
 
4899                 setOptions(this, options);
 
4901                 this._layerControlInputs = [];
 
4903                 this._lastZIndex = 0;
 
4904                 this._handlingClick = false;
 
4906                 for (var i in baseLayers) {
 
4907                         this._addLayer(baseLayers[i], i);
 
4910                 for (i in overlays) {
 
4911                         this._addLayer(overlays[i], i, true);
 
4915         onAdd: function (map) {
 
4920                 map.on('zoomend', this._checkDisabledLayers, this);
 
4922                 for (var i = 0; i < this._layers.length; i++) {
 
4923                         this._layers[i].layer.on('add remove', this._onLayerChange, this);
 
4926                 return this._container;
 
4929         addTo: function (map) {
 
4930                 Control.prototype.addTo.call(this, map);
 
4931                 // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
 
4932                 return this._expandIfNotCollapsed();
 
4935         onRemove: function () {
 
4936                 this._map.off('zoomend', this._checkDisabledLayers, this);
 
4938                 for (var i = 0; i < this._layers.length; i++) {
 
4939                         this._layers[i].layer.off('add remove', this._onLayerChange, this);
 
4943         // @method addBaseLayer(layer: Layer, name: String): this
 
4944         // Adds a base layer (radio button entry) with the given name to the control.
 
4945         addBaseLayer: function (layer, name) {
 
4946                 this._addLayer(layer, name);
 
4947                 return (this._map) ? this._update() : this;
 
4950         // @method addOverlay(layer: Layer, name: String): this
 
4951         // Adds an overlay (checkbox entry) with the given name to the control.
 
4952         addOverlay: function (layer, name) {
 
4953                 this._addLayer(layer, name, true);
 
4954                 return (this._map) ? this._update() : this;
 
4957         // @method removeLayer(layer: Layer): this
 
4958         // Remove the given layer from the control.
 
4959         removeLayer: function (layer) {
 
4960                 layer.off('add remove', this._onLayerChange, this);
 
4962                 var obj = this._getLayer(stamp(layer));
 
4964                         this._layers.splice(this._layers.indexOf(obj), 1);
 
4966                 return (this._map) ? this._update() : this;
 
4969         // @method expand(): this
 
4970         // Expand the control container if collapsed.
 
4971         expand: function () {
 
4972                 addClass(this._container, 'leaflet-control-layers-expanded');
 
4973                 this._form.style.height = null;
 
4974                 var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
 
4975                 if (acceptableHeight < this._form.clientHeight) {
 
4976                         addClass(this._form, 'leaflet-control-layers-scrollbar');
 
4977                         this._form.style.height = acceptableHeight + 'px';
 
4979                         removeClass(this._form, 'leaflet-control-layers-scrollbar');
 
4981                 this._checkDisabledLayers();
 
4985         // @method collapse(): this
 
4986         // Collapse the control container if expanded.
 
4987         collapse: function () {
 
4988                 removeClass(this._container, 'leaflet-control-layers-expanded');
 
4992         _initLayout: function () {
 
4993                 var className = 'leaflet-control-layers',
 
4994                     container = this._container = create$1('div', className),
 
4995                     collapsed = this.options.collapsed;
 
4997                 // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
 
4998                 container.setAttribute('aria-haspopup', true);
 
5000                 disableClickPropagation(container);
 
5001                 disableScrollPropagation(container);
 
5003                 var form = this._form = create$1('form', className + '-list');
 
5006                         this._map.on('click', this.collapse, this);
 
5010                                         mouseenter: this.expand,
 
5011                                         mouseleave: this.collapse
 
5016                 var link = this._layersLink = create$1('a', className + '-toggle', container);
 
5018                 link.title = 'Layers';
 
5021                         on(link, 'click', stop);
 
5022                         on(link, 'click', this.expand, this);
 
5024                         on(link, 'focus', this.expand, this);
 
5031                 this._baseLayersList = create$1('div', className + '-base', form);
 
5032                 this._separator = create$1('div', className + '-separator', form);
 
5033                 this._overlaysList = create$1('div', className + '-overlays', form);
 
5035                 container.appendChild(form);
 
5038         _getLayer: function (id) {
 
5039                 for (var i = 0; i < this._layers.length; i++) {
 
5041                         if (this._layers[i] && stamp(this._layers[i].layer) === id) {
 
5042                                 return this._layers[i];
 
5047         _addLayer: function (layer, name, overlay) {
 
5049                         layer.on('add remove', this._onLayerChange, this);
 
5058                 if (this.options.sortLayers) {
 
5059                         this._layers.sort(bind(function (a, b) {
 
5060                                 return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
 
5064                 if (this.options.autoZIndex && layer.setZIndex) {
 
5066                         layer.setZIndex(this._lastZIndex);
 
5069                 this._expandIfNotCollapsed();
 
5072         _update: function () {
 
5073                 if (!this._container) { return this; }
 
5075                 empty(this._baseLayersList);
 
5076                 empty(this._overlaysList);
 
5078                 this._layerControlInputs = [];
 
5079                 var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
 
5081                 for (i = 0; i < this._layers.length; i++) {
 
5082                         obj = this._layers[i];
 
5084                         overlaysPresent = overlaysPresent || obj.overlay;
 
5085                         baseLayersPresent = baseLayersPresent || !obj.overlay;
 
5086                         baseLayersCount += !obj.overlay ? 1 : 0;
 
5089                 // Hide base layers section if there's only one layer.
 
5090                 if (this.options.hideSingleBase) {
 
5091                         baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
 
5092                         this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
 
5095                 this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
 
5100         _onLayerChange: function (e) {
 
5101                 if (!this._handlingClick) {
 
5105                 var obj = this._getLayer(stamp(e.target));
 
5108                 // @section Layer events
 
5109                 // @event baselayerchange: LayersControlEvent
 
5110                 // Fired when the base layer is changed through the [layer control](#control-layers).
 
5111                 // @event overlayadd: LayersControlEvent
 
5112                 // Fired when an overlay is selected through the [layer control](#control-layers).
 
5113                 // @event overlayremove: LayersControlEvent
 
5114                 // Fired when an overlay is deselected through the [layer control](#control-layers).
 
5115                 // @namespace Control.Layers
 
5116                 var type = obj.overlay ?
 
5117                         (e.type === 'add' ? 'overlayadd' : 'overlayremove') :
 
5118                         (e.type === 'add' ? 'baselayerchange' : null);
 
5121                         this._map.fire(type, obj);
 
5125         // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
 
5126         _createRadioElement: function (name, checked) {
 
5128                 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
 
5129                                 name + '"' + (checked ? ' checked="checked"' : '') + '/>';
 
5131                 var radioFragment = document.createElement('div');
 
5132                 radioFragment.innerHTML = radioHtml;
 
5134                 return radioFragment.firstChild;
 
5137         _addItem: function (obj) {
 
5138                 var label = document.createElement('label'),
 
5139                     checked = this._map.hasLayer(obj.layer),
 
5143                         input = document.createElement('input');
 
5144                         input.type = 'checkbox';
 
5145                         input.className = 'leaflet-control-layers-selector';
 
5146                         input.defaultChecked = checked;
 
5148                         input = this._createRadioElement('leaflet-base-layers', checked);
 
5151                 this._layerControlInputs.push(input);
 
5152                 input.layerId = stamp(obj.layer);
 
5154                 on(input, 'click', this._onInputClick, this);
 
5156                 var name = document.createElement('span');
 
5157                 name.innerHTML = ' ' + obj.name;
 
5159                 // Helps from preventing layer control flicker when checkboxes are disabled
 
5160                 // https://github.com/Leaflet/Leaflet/issues/2771
 
5161                 var holder = document.createElement('div');
 
5163                 label.appendChild(holder);
 
5164                 holder.appendChild(input);
 
5165                 holder.appendChild(name);
 
5167                 var container = obj.overlay ? this._overlaysList : this._baseLayersList;
 
5168                 container.appendChild(label);
 
5170                 this._checkDisabledLayers();
 
5174         _onInputClick: function () {
 
5175                 var inputs = this._layerControlInputs,
 
5177                 var addedLayers = [],
 
5180                 this._handlingClick = true;
 
5182                 for (var i = inputs.length - 1; i >= 0; i--) {
 
5184                         layer = this._getLayer(input.layerId).layer;
 
5186                         if (input.checked) {
 
5187                                 addedLayers.push(layer);
 
5188                         } else if (!input.checked) {
 
5189                                 removedLayers.push(layer);
 
5193                 // Bugfix issue 2318: Should remove all old layers before readding new ones
 
5194                 for (i = 0; i < removedLayers.length; i++) {
 
5195                         if (this._map.hasLayer(removedLayers[i])) {
 
5196                                 this._map.removeLayer(removedLayers[i]);
 
5199                 for (i = 0; i < addedLayers.length; i++) {
 
5200                         if (!this._map.hasLayer(addedLayers[i])) {
 
5201                                 this._map.addLayer(addedLayers[i]);
 
5205                 this._handlingClick = false;
 
5207                 this._refocusOnMap();
 
5210         _checkDisabledLayers: function () {
 
5211                 var inputs = this._layerControlInputs,
 
5214                     zoom = this._map.getZoom();
 
5216                 for (var i = inputs.length - 1; i >= 0; i--) {
 
5218                         layer = this._getLayer(input.layerId).layer;
 
5219                         input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
 
5220                                          (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
 
5225         _expandIfNotCollapsed: function () {
 
5226                 if (this._map && !this.options.collapsed) {
 
5232         _expand: function () {
 
5233                 // Backward compatibility, remove me in 1.1.
 
5234                 return this.expand();
 
5237         _collapse: function () {
 
5238                 // Backward compatibility, remove me in 1.1.
 
5239                 return this.collapse();
 
5245 // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
 
5246 // 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.
 
5247 var layers = function (baseLayers, overlays, options) {
 
5248         return new Layers(baseLayers, overlays, options);
 
5252  * @class Control.Zoom
 
5253  * @aka L.Control.Zoom
 
5256  * 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`.
 
5259 var Zoom = Control.extend({
 
5261         // @aka Control.Zoom options
 
5263                 position: 'topleft',
 
5265                 // @option zoomInText: String = '+'
 
5266                 // The text set on the 'zoom in' button.
 
5269                 // @option zoomInTitle: String = 'Zoom in'
 
5270                 // The title set on the 'zoom in' button.
 
5271                 zoomInTitle: 'Zoom in',
 
5273                 // @option zoomOutText: String = '−'
 
5274                 // The text set on the 'zoom out' button.
 
5275                 zoomOutText: '−',
 
5277                 // @option zoomOutTitle: String = 'Zoom out'
 
5278                 // The title set on the 'zoom out' button.
 
5279                 zoomOutTitle: 'Zoom out'
 
5282         onAdd: function (map) {
 
5283                 var zoomName = 'leaflet-control-zoom',
 
5284                     container = create$1('div', zoomName + ' leaflet-bar'),
 
5285                     options = this.options;
 
5287                 this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
 
5288                         zoomName + '-in',  container, this._zoomIn);
 
5289                 this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
 
5290                         zoomName + '-out', container, this._zoomOut);
 
5292                 this._updateDisabled();
 
5293                 map.on('zoomend zoomlevelschange', this._updateDisabled, this);
 
5298         onRemove: function (map) {
 
5299                 map.off('zoomend zoomlevelschange', this._updateDisabled, this);
 
5302         disable: function () {
 
5303                 this._disabled = true;
 
5304                 this._updateDisabled();
 
5308         enable: function () {
 
5309                 this._disabled = false;
 
5310                 this._updateDisabled();
 
5314         _zoomIn: function (e) {
 
5315                 if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
 
5316                         this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
 
5320         _zoomOut: function (e) {
 
5321                 if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
 
5322                         this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
 
5326         _createButton: function (html, title, className, container, fn) {
 
5327                 var link = create$1('a', className, container);
 
5328                 link.innerHTML = html;
 
5333                  * Will force screen readers like VoiceOver to read this as "Zoom in - button"
 
5335                 link.setAttribute('role', 'button');
 
5336                 link.setAttribute('aria-label', title);
 
5338                 disableClickPropagation(link);
 
5339                 on(link, 'click', stop);
 
5340                 on(link, 'click', fn, this);
 
5341                 on(link, 'click', this._refocusOnMap, this);
 
5346         _updateDisabled: function () {
 
5347                 var map = this._map,
 
5348                     className = 'leaflet-disabled';
 
5350                 removeClass(this._zoomInButton, className);
 
5351                 removeClass(this._zoomOutButton, className);
 
5353                 if (this._disabled || map._zoom === map.getMinZoom()) {
 
5354                         addClass(this._zoomOutButton, className);
 
5356                 if (this._disabled || map._zoom === map.getMaxZoom()) {
 
5357                         addClass(this._zoomInButton, className);
 
5363 // @section Control options
 
5364 // @option zoomControl: Boolean = true
 
5365 // Whether a [zoom control](#control-zoom) is added to the map by default.
 
5370 Map.addInitHook(function () {
 
5371         if (this.options.zoomControl) {
 
5372                 // @section Controls
 
5373                 // @property zoomControl: Control.Zoom
 
5374                 // The default zoom control (only available if the
 
5375                 // [`zoomControl` option](#map-zoomcontrol) was `true` when creating the map).
 
5376                 this.zoomControl = new Zoom();
 
5377                 this.addControl(this.zoomControl);
 
5381 // @namespace Control.Zoom
 
5382 // @factory L.control.zoom(options: Control.Zoom options)
 
5383 // Creates a zoom control
 
5384 var zoom = function (options) {
 
5385         return new Zoom(options);
 
5389  * @class Control.Scale
 
5390  * @aka L.Control.Scale
 
5393  * 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`.
 
5398  * L.control.scale().addTo(map);
 
5402 var Scale = Control.extend({
 
5404         // @aka Control.Scale options
 
5406                 position: 'bottomleft',
 
5408                 // @option maxWidth: Number = 100
 
5409                 // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
 
5412                 // @option metric: Boolean = True
 
5413                 // Whether to show the metric scale line (m/km).
 
5416                 // @option imperial: Boolean = True
 
5417                 // Whether to show the imperial scale line (mi/ft).
 
5420                 // @option updateWhenIdle: Boolean = false
 
5421                 // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
 
5424         onAdd: function (map) {
 
5425                 var className = 'leaflet-control-scale',
 
5426                     container = create$1('div', className),
 
5427                     options = this.options;
 
5429                 this._addScales(options, className + '-line', container);
 
5431                 map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
5432                 map.whenReady(this._update, this);
 
5437         onRemove: function (map) {
 
5438                 map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
5441         _addScales: function (options, className, container) {
 
5442                 if (options.metric) {
 
5443                         this._mScale = create$1('div', className, container);
 
5445                 if (options.imperial) {
 
5446                         this._iScale = create$1('div', className, container);
 
5450         _update: function () {
 
5451                 var map = this._map,
 
5452                     y = map.getSize().y / 2;
 
5454                 var maxMeters = map.distance(
 
5455                         map.containerPointToLatLng([0, y]),
 
5456                         map.containerPointToLatLng([this.options.maxWidth, y]));
 
5458                 this._updateScales(maxMeters);
 
5461         _updateScales: function (maxMeters) {
 
5462                 if (this.options.metric && maxMeters) {
 
5463                         this._updateMetric(maxMeters);
 
5465                 if (this.options.imperial && maxMeters) {
 
5466                         this._updateImperial(maxMeters);
 
5470         _updateMetric: function (maxMeters) {
 
5471                 var meters = this._getRoundNum(maxMeters),
 
5472                     label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
 
5474                 this._updateScale(this._mScale, label, meters / maxMeters);
 
5477         _updateImperial: function (maxMeters) {
 
5478                 var maxFeet = maxMeters * 3.2808399,
 
5479                     maxMiles, miles, feet;
 
5481                 if (maxFeet > 5280) {
 
5482                         maxMiles = maxFeet / 5280;
 
5483                         miles = this._getRoundNum(maxMiles);
 
5484                         this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
 
5487                         feet = this._getRoundNum(maxFeet);
 
5488                         this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
 
5492         _updateScale: function (scale, text, ratio) {
 
5493                 scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
 
5494                 scale.innerHTML = text;
 
5497         _getRoundNum: function (num) {
 
5498                 var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
 
5511 // @factory L.control.scale(options?: Control.Scale options)
 
5512 // Creates an scale control with the given options.
 
5513 var scale = function (options) {
 
5514         return new Scale(options);
 
5518  * @class Control.Attribution
 
5519  * @aka L.Control.Attribution
 
5522  * 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.
 
5525 var Attribution = Control.extend({
 
5527         // @aka Control.Attribution options
 
5529                 position: 'bottomright',
 
5531                 // @option prefix: String = 'Leaflet'
 
5532                 // The HTML text shown before the attributions. Pass `false` to disable.
 
5533                 prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
 
5536         initialize: function (options) {
 
5537                 setOptions(this, options);
 
5539                 this._attributions = {};
 
5542         onAdd: function (map) {
 
5543                 map.attributionControl = this;
 
5544                 this._container = create$1('div', 'leaflet-control-attribution');
 
5545                 disableClickPropagation(this._container);
 
5547                 // TODO ugly, refactor
 
5548                 for (var i in map._layers) {
 
5549                         if (map._layers[i].getAttribution) {
 
5550                                 this.addAttribution(map._layers[i].getAttribution());
 
5556                 return this._container;
 
5559         // @method setPrefix(prefix: String): this
 
5560         // Sets the text before the attributions.
 
5561         setPrefix: function (prefix) {
 
5562                 this.options.prefix = prefix;
 
5567         // @method addAttribution(text: String): this
 
5568         // Adds an attribution text (e.g. `'Vector data © Mapbox'`).
 
5569         addAttribution: function (text) {
 
5570                 if (!text) { return this; }
 
5572                 if (!this._attributions[text]) {
 
5573                         this._attributions[text] = 0;
 
5575                 this._attributions[text]++;
 
5582         // @method removeAttribution(text: String): this
 
5583         // Removes an attribution text.
 
5584         removeAttribution: function (text) {
 
5585                 if (!text) { return this; }
 
5587                 if (this._attributions[text]) {
 
5588                         this._attributions[text]--;
 
5595         _update: function () {
 
5596                 if (!this._map) { return; }
 
5600                 for (var i in this._attributions) {
 
5601                         if (this._attributions[i]) {
 
5606                 var prefixAndAttribs = [];
 
5608                 if (this.options.prefix) {
 
5609                         prefixAndAttribs.push(this.options.prefix);
 
5611                 if (attribs.length) {
 
5612                         prefixAndAttribs.push(attribs.join(', '));
 
5615                 this._container.innerHTML = prefixAndAttribs.join(' | ');
 
5620 // @section Control options
 
5621 // @option attributionControl: Boolean = true
 
5622 // Whether a [attribution control](#control-attribution) is added to the map by default.
 
5624         attributionControl: true
 
5627 Map.addInitHook(function () {
 
5628         if (this.options.attributionControl) {
 
5629                 new Attribution().addTo(this);
 
5633 // @namespace Control.Attribution
 
5634 // @factory L.control.attribution(options: Control.Attribution options)
 
5635 // Creates an attribution control.
 
5636 var attribution = function (options) {
 
5637         return new Attribution(options);
 
5640 Control.Layers = Layers;
 
5641 Control.Zoom = Zoom;
 
5642 Control.Scale = Scale;
 
5643 Control.Attribution = Attribution;
 
5645 control.layers = layers;
 
5646 control.zoom = zoom;
 
5647 control.scale = scale;
 
5648 control.attribution = attribution;
 
5651         L.Handler is a base class for handler classes that are used internally to inject
 
5652         interaction features like dragging to classes like Map and Marker.
 
5657 // Abstract class for map interaction handlers
 
5659 var Handler = Class.extend({
 
5660         initialize: function (map) {
 
5664         // @method enable(): this
 
5665         // Enables the handler
 
5666         enable: function () {
 
5667                 if (this._enabled) { return this; }
 
5669                 this._enabled = true;
 
5674         // @method disable(): this
 
5675         // Disables the handler
 
5676         disable: function () {
 
5677                 if (!this._enabled) { return this; }
 
5679                 this._enabled = false;
 
5684         // @method enabled(): Boolean
 
5685         // Returns `true` if the handler is enabled
 
5686         enabled: function () {
 
5687                 return !!this._enabled;
 
5690         // @section Extension methods
 
5691         // Classes inheriting from `Handler` must implement the two following methods:
 
5692         // @method addHooks()
 
5693         // Called when the handler is enabled, should add event hooks.
 
5694         // @method removeHooks()
 
5695         // Called when the handler is disabled, should remove the event hooks added previously.
 
5698 // @section There is static function which can be called without instantiating L.Handler:
 
5699 // @function addTo(map: Map, name: String): this
 
5700 // Adds a new Handler to the given map with the given name.
 
5701 Handler.addTo = function (map, name) {
 
5702         map.addHandler(name, this);
 
5706 var Mixin = {Events: Events};
 
5713  * A class for making DOM elements draggable (including touch support).
 
5714  * Used internally for map and marker dragging. Only works for elements
 
5715  * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
 
5719  * var draggable = new L.Draggable(elementToDrag);
 
5720  * draggable.enable();
 
5724 var START = touch ? 'touchstart mousedown' : 'mousedown';
 
5726         mousedown: 'mouseup',
 
5727         touchstart: 'touchend',
 
5728         pointerdown: 'touchend',
 
5729         MSPointerDown: 'touchend'
 
5732         mousedown: 'mousemove',
 
5733         touchstart: 'touchmove',
 
5734         pointerdown: 'touchmove',
 
5735         MSPointerDown: 'touchmove'
 
5739 var Draggable = Evented.extend({
 
5743                 // @aka Draggable options
 
5744                 // @option clickTolerance: Number = 3
 
5745                 // The max number of pixels a user can shift the mouse pointer during a click
 
5746                 // for it to be considered a valid click (as opposed to a mouse drag).
 
5750         // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
 
5751         // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
 
5752         initialize: function (element, dragStartTarget, preventOutline$$1, options) {
 
5753                 setOptions(this, options);
 
5755                 this._element = element;
 
5756                 this._dragStartTarget = dragStartTarget || element;
 
5757                 this._preventOutline = preventOutline$$1;
 
5761         // Enables the dragging ability
 
5762         enable: function () {
 
5763                 if (this._enabled) { return; }
 
5765                 on(this._dragStartTarget, START, this._onDown, this);
 
5767                 this._enabled = true;
 
5770         // @method disable()
 
5771         // Disables the dragging ability
 
5772         disable: function () {
 
5773                 if (!this._enabled) { return; }
 
5775                 // If we're currently dragging this draggable,
 
5776                 // disabling it counts as first ending the drag.
 
5777                 if (Draggable._dragging === this) {
 
5781                 off(this._dragStartTarget, START, this._onDown, this);
 
5783                 this._enabled = false;
 
5784                 this._moved = false;
 
5787         _onDown: function (e) {
 
5788                 // Ignore simulated events, since we handle both touch and
 
5789                 // mouse explicitly; otherwise we risk getting duplicates of
 
5790                 // touch events, see #4315.
 
5791                 // Also ignore the event if disabled; this happens in IE11
 
5792                 // under some circumstances, see #3666.
 
5793                 if (e._simulated || !this._enabled) { return; }
 
5795                 this._moved = false;
 
5797                 if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
 
5799                 if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
 
5800                 Draggable._dragging = this;  // Prevent dragging multiple objects at once.
 
5802                 if (this._preventOutline) {
 
5803                         preventOutline(this._element);
 
5807                 disableTextSelection();
 
5809                 if (this._moving) { return; }
 
5811                 // @event down: Event
 
5812                 // Fired when a drag is about to start.
 
5815                 var first = e.touches ? e.touches[0] : e,
 
5816                     sizedParent = getSizedParentNode(this._element);
 
5818                 this._startPoint = new Point(first.clientX, first.clientY);
 
5820                 // Cache the scale, so that we can continuously compensate for it during drag (_onMove).
 
5821                 this._parentScale = getScale(sizedParent);
 
5823                 on(document, MOVE[e.type], this._onMove, this);
 
5824                 on(document, END[e.type], this._onUp, this);
 
5827         _onMove: function (e) {
 
5828                 // Ignore simulated events, since we handle both touch and
 
5829                 // mouse explicitly; otherwise we risk getting duplicates of
 
5830                 // touch events, see #4315.
 
5831                 // Also ignore the event if disabled; this happens in IE11
 
5832                 // under some circumstances, see #3666.
 
5833                 if (e._simulated || !this._enabled) { return; }
 
5835                 if (e.touches && e.touches.length > 1) {
 
5840                 var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
 
5841                     offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint);
 
5843                 if (!offset.x && !offset.y) { return; }
 
5844                 if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
 
5846                 // We assume that the parent container's position, border and scale do not change for the duration of the drag.
 
5847                 // Therefore there is no need to account for the position and border (they are eliminated by the subtraction)
 
5848                 // and we can use the cached value for the scale.
 
5849                 offset.x /= this._parentScale.x;
 
5850                 offset.y /= this._parentScale.y;
 
5855                         // @event dragstart: Event
 
5856                         // Fired when a drag starts
 
5857                         this.fire('dragstart');
 
5860                         this._startPos = getPosition(this._element).subtract(offset);
 
5862                         addClass(document.body, 'leaflet-dragging');
 
5864                         this._lastTarget = e.target || e.srcElement;
 
5865                         // IE and Edge do not give the <use> element, so fetch it
 
5867                         if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
 
5868                                 this._lastTarget = this._lastTarget.correspondingUseElement;
 
5870                         addClass(this._lastTarget, 'leaflet-drag-target');
 
5873                 this._newPos = this._startPos.add(offset);
 
5874                 this._moving = true;
 
5876                 cancelAnimFrame(this._animRequest);
 
5877                 this._lastEvent = e;
 
5878                 this._animRequest = requestAnimFrame(this._updatePosition, this, true);
 
5881         _updatePosition: function () {
 
5882                 var e = {originalEvent: this._lastEvent};
 
5884                 // @event predrag: Event
 
5885                 // Fired continuously during dragging *before* each corresponding
 
5886                 // update of the element's position.
 
5887                 this.fire('predrag', e);
 
5888                 setPosition(this._element, this._newPos);
 
5890                 // @event drag: Event
 
5891                 // Fired continuously during dragging.
 
5892                 this.fire('drag', e);
 
5895         _onUp: function (e) {
 
5896                 // Ignore simulated events, since we handle both touch and
 
5897                 // mouse explicitly; otherwise we risk getting duplicates of
 
5898                 // touch events, see #4315.
 
5899                 // Also ignore the event if disabled; this happens in IE11
 
5900                 // under some circumstances, see #3666.
 
5901                 if (e._simulated || !this._enabled) { return; }
 
5905         finishDrag: function () {
 
5906                 removeClass(document.body, 'leaflet-dragging');
 
5908                 if (this._lastTarget) {
 
5909                         removeClass(this._lastTarget, 'leaflet-drag-target');
 
5910                         this._lastTarget = null;
 
5913                 for (var i in MOVE) {
 
5914                         off(document, MOVE[i], this._onMove, this);
 
5915                         off(document, END[i], this._onUp, this);
 
5919                 enableTextSelection();
 
5921                 if (this._moved && this._moving) {
 
5922                         // ensure drag is not fired after dragend
 
5923                         cancelAnimFrame(this._animRequest);
 
5925                         // @event dragend: DragEndEvent
 
5926                         // Fired when the drag ends.
 
5927                         this.fire('dragend', {
 
5928                                 distance: this._newPos.distanceTo(this._startPos)
 
5932                 this._moving = false;
 
5933                 Draggable._dragging = false;
 
5939  * @namespace LineUtil
 
5941  * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast.
 
5944 // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
 
5945 // Improves rendering performance dramatically by lessening the number of points to draw.
 
5947 // @function simplify(points: Point[], tolerance: Number): Point[]
 
5948 // Dramatically reduces the number of points in a polyline while retaining
 
5949 // its shape and returns a new array of simplified points, using the
 
5950 // [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
 
5951 // Used for a huge performance boost when processing/displaying Leaflet polylines for
 
5952 // each zoom level and also reducing visual noise. tolerance affects the amount of
 
5953 // simplification (lesser value means higher quality but slower and with more points).
 
5954 // Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
 
5955 function simplify(points, tolerance) {
 
5956         if (!tolerance || !points.length) {
 
5957                 return points.slice();
 
5960         var sqTolerance = tolerance * tolerance;
 
5962             // stage 1: vertex reduction
 
5963             points = _reducePoints(points, sqTolerance);
 
5965             // stage 2: Douglas-Peucker simplification
 
5966             points = _simplifyDP(points, sqTolerance);
 
5971 // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
 
5972 // Returns the distance between point `p` and segment `p1` to `p2`.
 
5973 function pointToSegmentDistance(p, p1, p2) {
 
5974         return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
 
5977 // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
 
5978 // Returns the closest point from a point `p` on a segment `p1` to `p2`.
 
5979 function closestPointOnSegment(p, p1, p2) {
 
5980         return _sqClosestPointOnSegment(p, p1, p2);
 
5983 // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
 
5984 function _simplifyDP(points, sqTolerance) {
 
5986         var len = points.length,
 
5987             ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
 
5988             markers = new ArrayConstructor(len);
 
5990             markers[0] = markers[len - 1] = 1;
 
5992         _simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
 
5997         for (i = 0; i < len; i++) {
 
5999                         newPoints.push(points[i]);
 
6006 function _simplifyDPStep(points, markers, sqTolerance, first, last) {
 
6011         for (i = first + 1; i <= last - 1; i++) {
 
6012                 sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
 
6014                 if (sqDist > maxSqDist) {
 
6020         if (maxSqDist > sqTolerance) {
 
6023                 _simplifyDPStep(points, markers, sqTolerance, first, index);
 
6024                 _simplifyDPStep(points, markers, sqTolerance, index, last);
 
6028 // reduce points that are too close to each other to a single point
 
6029 function _reducePoints(points, sqTolerance) {
 
6030         var reducedPoints = [points[0]];
 
6032         for (var i = 1, prev = 0, len = points.length; i < len; i++) {
 
6033                 if (_sqDist(points[i], points[prev]) > sqTolerance) {
 
6034                         reducedPoints.push(points[i]);
 
6038         if (prev < len - 1) {
 
6039                 reducedPoints.push(points[len - 1]);
 
6041         return reducedPoints;
 
6046 // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
 
6047 // Clips the segment a to b by rectangular bounds with the
 
6048 // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
 
6049 // (modifying the segment points directly!). Used by Leaflet to only show polyline
 
6050 // points that are on the screen or near, increasing performance.
 
6051 function clipSegment(a, b, bounds, useLastCode, round) {
 
6052         var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
 
6053             codeB = _getBitCode(b, bounds),
 
6055             codeOut, p, newCode;
 
6057             // save 2nd code to avoid calculating it on the next segment
 
6061                 // if a,b is inside the clip window (trivial accept)
 
6062                 if (!(codeA | codeB)) {
 
6066                 // if a,b is outside the clip window (trivial reject)
 
6067                 if (codeA & codeB) {
 
6072                 codeOut = codeA || codeB;
 
6073                 p = _getEdgeIntersection(a, b, codeOut, bounds, round);
 
6074                 newCode = _getBitCode(p, bounds);
 
6076                 if (codeOut === codeA) {
 
6086 function _getEdgeIntersection(a, b, code, bounds, round) {
 
6093         if (code & 8) { // top
 
6094                 x = a.x + dx * (max.y - a.y) / dy;
 
6097         } else if (code & 4) { // bottom
 
6098                 x = a.x + dx * (min.y - a.y) / dy;
 
6101         } else if (code & 2) { // right
 
6103                 y = a.y + dy * (max.x - a.x) / dx;
 
6105         } else if (code & 1) { // left
 
6107                 y = a.y + dy * (min.x - a.x) / dx;
 
6110         return new Point(x, y, round);
 
6113 function _getBitCode(p, bounds) {
 
6116         if (p.x < bounds.min.x) { // left
 
6118         } else if (p.x > bounds.max.x) { // right
 
6122         if (p.y < bounds.min.y) { // bottom
 
6124         } else if (p.y > bounds.max.y) { // top
 
6131 // square distance (to avoid unnecessary Math.sqrt calls)
 
6132 function _sqDist(p1, p2) {
 
6133         var dx = p2.x - p1.x,
 
6135         return dx * dx + dy * dy;
 
6138 // return closest point on segment or distance to that point
 
6139 function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
 
6144             dot = dx * dx + dy * dy,
 
6148                 t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
 
6162         return sqDist ? dx * dx + dy * dy : new Point(x, y);
 
6166 // @function isFlat(latlngs: LatLng[]): Boolean
 
6167 // Returns true if `latlngs` is a flat array, false is nested.
 
6168 function isFlat(latlngs) {
 
6169         return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
 
6172 function _flat(latlngs) {
 
6173         console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
 
6174         return isFlat(latlngs);
 
6178 var LineUtil = (Object.freeze || Object)({
 
6180         pointToSegmentDistance: pointToSegmentDistance,
 
6181         closestPointOnSegment: closestPointOnSegment,
 
6182         clipSegment: clipSegment,
 
6183         _getEdgeIntersection: _getEdgeIntersection,
 
6184         _getBitCode: _getBitCode,
 
6185         _sqClosestPointOnSegment: _sqClosestPointOnSegment,
 
6191  * @namespace PolyUtil
 
6192  * Various utility functions for polygon geometries.
 
6195 /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
 
6196  * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)).
 
6197  * Used by Leaflet to only show polygon points that are on the screen or near, increasing
 
6198  * performance. Note that polygon points needs different algorithm for clipping
 
6199  * than polyline, so there's a separate method for it.
 
6201 function clipPolygon(points, bounds, round) {
 
6203             edges = [1, 4, 2, 8],
 
6208         for (i = 0, len = points.length; i < len; i++) {
 
6209                 points[i]._code = _getBitCode(points[i], bounds);
 
6212         // for each edge (left, bottom, right, top)
 
6213         for (k = 0; k < 4; k++) {
 
6217                 for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
 
6221                         // if a is inside the clip window
 
6222                         if (!(a._code & edge)) {
 
6223                                 // if b is outside the clip window (a->b goes out of screen)
 
6224                                 if (b._code & edge) {
 
6225                                         p = _getEdgeIntersection(b, a, edge, bounds, round);
 
6226                                         p._code = _getBitCode(p, bounds);
 
6227                                         clippedPoints.push(p);
 
6229                                 clippedPoints.push(a);
 
6231                         // else if b is inside the clip window (a->b enters the screen)
 
6232                         } else if (!(b._code & edge)) {
 
6233                                 p = _getEdgeIntersection(b, a, edge, bounds, round);
 
6234                                 p._code = _getBitCode(p, bounds);
 
6235                                 clippedPoints.push(p);
 
6238                 points = clippedPoints;
 
6245 var PolyUtil = (Object.freeze || Object)({
 
6246         clipPolygon: clipPolygon
 
6250  * @namespace Projection
 
6252  * Leaflet comes with a set of already defined Projections out of the box:
 
6254  * @projection L.Projection.LonLat
 
6256  * Equirectangular, or Plate Carree projection — the most simple projection,
 
6257  * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
 
6258  * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
 
6259  * `EPSG:4326` and `Simple` CRS.
 
6263         project: function (latlng) {
 
6264                 return new Point(latlng.lng, latlng.lat);
 
6267         unproject: function (point) {
 
6268                 return new LatLng(point.y, point.x);
 
6271         bounds: new Bounds([-180, -90], [180, 90])
 
6275  * @namespace Projection
 
6276  * @projection L.Projection.Mercator
 
6278  * 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.
 
6283         R_MINOR: 6356752.314245179,
 
6285         bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
 
6287         project: function (latlng) {
 
6288                 var d = Math.PI / 180,
 
6291                     tmp = this.R_MINOR / r,
 
6292                     e = Math.sqrt(1 - tmp * tmp),
 
6293                     con = e * Math.sin(y);
 
6295                 var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
 
6296                 y = -r * Math.log(Math.max(ts, 1E-10));
 
6298                 return new Point(latlng.lng * d * r, y);
 
6301         unproject: function (point) {
 
6302                 var d = 180 / Math.PI,
 
6304                     tmp = this.R_MINOR / r,
 
6305                     e = Math.sqrt(1 - tmp * tmp),
 
6306                     ts = Math.exp(-point.y / r),
 
6307                     phi = Math.PI / 2 - 2 * Math.atan(ts);
 
6309                 for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
 
6310                         con = e * Math.sin(phi);
 
6311                         con = Math.pow((1 - con) / (1 + con), e / 2);
 
6312                         dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
 
6316                 return new LatLng(phi * d, point.x * d / r);
 
6323  * An object with methods for projecting geographical coordinates of the world onto
 
6324  * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection).
 
6326  * @property bounds: Bounds
 
6327  * The bounds (specified in CRS units) where the projection is valid
 
6329  * @method project(latlng: LatLng): Point
 
6330  * Projects geographical coordinates into a 2D point.
 
6331  * Only accepts actual `L.LatLng` instances, not arrays.
 
6333  * @method unproject(point: Point): LatLng
 
6334  * The inverse of `project`. Projects a 2D point into a geographical location.
 
6335  * Only accepts actual `L.Point` instances, not arrays.
 
6337  * Note that the projection instances do not inherit from Leafet's `Class` object,
 
6338  * and can't be instantiated. Also, new classes can't inherit from them,
 
6339  * and methods can't be added to them with the `include` function.
 
6346 var index = (Object.freeze || Object)({
 
6349         SphericalMercator: SphericalMercator
 
6354  * @crs L.CRS.EPSG3395
 
6356  * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
 
6358 var EPSG3395 = extend({}, Earth, {
 
6360         projection: Mercator,
 
6362         transformation: (function () {
 
6363                 var scale = 0.5 / (Math.PI * Mercator.R);
 
6364                 return toTransformation(scale, 0.5, -scale, 0.5);
 
6370  * @crs L.CRS.EPSG4326
 
6372  * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
 
6374  * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
 
6375  * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
 
6376  * with this CRS, ensure that there are two 256x256 pixel tiles covering the
 
6377  * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
 
6378  * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
 
6381 var EPSG4326 = extend({}, Earth, {
 
6384         transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
 
6391  * A simple CRS that maps longitude and latitude into `x` and `y` directly.
 
6392  * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
 
6393  * axis should still be inverted (going from bottom to top). `distance()` returns
 
6394  * simple euclidean distance.
 
6397 var Simple = extend({}, CRS, {
 
6399         transformation: toTransformation(1, 0, -1, 0),
 
6401         scale: function (zoom) {
 
6402                 return Math.pow(2, zoom);
 
6405         zoom: function (scale) {
 
6406                 return Math.log(scale) / Math.LN2;
 
6409         distance: function (latlng1, latlng2) {
 
6410                 var dx = latlng2.lng - latlng1.lng,
 
6411                     dy = latlng2.lat - latlng1.lat;
 
6413                 return Math.sqrt(dx * dx + dy * dy);
 
6420 CRS.EPSG3395 = EPSG3395;
 
6421 CRS.EPSG3857 = EPSG3857;
 
6422 CRS.EPSG900913 = EPSG900913;
 
6423 CRS.EPSG4326 = EPSG4326;
 
6424 CRS.Simple = Simple;
 
6432  * A set of methods from the Layer base class that all Leaflet layers use.
 
6433  * Inherits all methods, options and events from `L.Evented`.
 
6438  * var layer = L.Marker(latlng).addTo(map);
 
6444  * Fired after the layer is added to a map
 
6446  * @event remove: Event
 
6447  * Fired after the layer is removed from a map
 
6451 var Layer = Evented.extend({
 
6453         // Classes extending `L.Layer` will inherit the following options:
 
6455                 // @option pane: String = 'overlayPane'
 
6456                 // 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.
 
6457                 pane: 'overlayPane',
 
6459                 // @option attribution: String = null
 
6460                 // String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox".
 
6463                 bubblingMouseEvents: true
 
6467          * Classes extending `L.Layer` will inherit the following methods:
 
6469          * @method addTo(map: Map|LayerGroup): this
 
6470          * Adds the layer to the given map or layer group.
 
6472         addTo: function (map) {
 
6477         // @method remove: this
 
6478         // Removes the layer from the map it is currently active on.
 
6479         remove: function () {
 
6480                 return this.removeFrom(this._map || this._mapToAdd);
 
6483         // @method removeFrom(map: Map): this
 
6484         // Removes the layer from the given map
 
6485         removeFrom: function (obj) {
 
6487                         obj.removeLayer(this);
 
6492         // @method getPane(name? : String): HTMLElement
 
6493         // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
 
6494         getPane: function (name) {
 
6495                 return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
 
6498         addInteractiveTarget: function (targetEl) {
 
6499                 this._map._targets[stamp(targetEl)] = this;
 
6503         removeInteractiveTarget: function (targetEl) {
 
6504                 delete this._map._targets[stamp(targetEl)];
 
6508         // @method getAttribution: String
 
6509         // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
 
6510         getAttribution: function () {
 
6511                 return this.options.attribution;
 
6514         _layerAdd: function (e) {
 
6517                 // check in case layer gets added and then removed before the map is ready
 
6518                 if (!map.hasLayer(this)) { return; }
 
6521                 this._zoomAnimated = map._zoomAnimated;
 
6523                 if (this.getEvents) {
 
6524                         var events = this.getEvents();
 
6525                         map.on(events, this);
 
6526                         this.once('remove', function () {
 
6527                                 map.off(events, this);
 
6533                 if (this.getAttribution && map.attributionControl) {
 
6534                         map.attributionControl.addAttribution(this.getAttribution());
 
6538                 map.fire('layeradd', {layer: this});
 
6542 /* @section Extension methods
 
6545  * Every layer should extend from `L.Layer` and (re-)implement the following methods.
 
6547  * @method onAdd(map: Map): this
 
6548  * 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).
 
6550  * @method onRemove(map: Map): this
 
6551  * 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).
 
6553  * @method getEvents(): Object
 
6554  * 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.
 
6556  * @method getAttribution(): String
 
6557  * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
 
6559  * @method beforeAdd(map: Map): this
 
6560  * 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.
 
6565  * @section Layer events
 
6567  * @event layeradd: LayerEvent
 
6568  * Fired when a new layer is added to the map.
 
6570  * @event layerremove: LayerEvent
 
6571  * Fired when some layer is removed from the map
 
6573  * @section Methods for Layers and Controls
 
6576         // @method addLayer(layer: Layer): this
 
6577         // Adds the given layer to the map
 
6578         addLayer: function (layer) {
 
6579                 if (!layer._layerAdd) {
 
6580                         throw new Error('The provided object is not a Layer.');
 
6583                 var id = stamp(layer);
 
6584                 if (this._layers[id]) { return this; }
 
6585                 this._layers[id] = layer;
 
6587                 layer._mapToAdd = this;
 
6589                 if (layer.beforeAdd) {
 
6590                         layer.beforeAdd(this);
 
6593                 this.whenReady(layer._layerAdd, layer);
 
6598         // @method removeLayer(layer: Layer): this
 
6599         // Removes the given layer from the map.
 
6600         removeLayer: function (layer) {
 
6601                 var id = stamp(layer);
 
6603                 if (!this._layers[id]) { return this; }
 
6606                         layer.onRemove(this);
 
6609                 if (layer.getAttribution && this.attributionControl) {
 
6610                         this.attributionControl.removeAttribution(layer.getAttribution());
 
6613                 delete this._layers[id];
 
6616                         this.fire('layerremove', {layer: layer});
 
6617                         layer.fire('remove');
 
6620                 layer._map = layer._mapToAdd = null;
 
6625         // @method hasLayer(layer: Layer): Boolean
 
6626         // Returns `true` if the given layer is currently added to the map
 
6627         hasLayer: function (layer) {
 
6628                 return !!layer && (stamp(layer) in this._layers);
 
6631         /* @method eachLayer(fn: Function, context?: Object): this
 
6632          * Iterates over the layers of the map, optionally specifying context of the iterator function.
 
6634          * map.eachLayer(function(layer){
 
6635          *     layer.bindPopup('Hello');
 
6639         eachLayer: function (method, context) {
 
6640                 for (var i in this._layers) {
 
6641                         method.call(context, this._layers[i]);
 
6646         _addLayers: function (layers) {
 
6647                 layers = layers ? (isArray(layers) ? layers : [layers]) : [];
 
6649                 for (var i = 0, len = layers.length; i < len; i++) {
 
6650                         this.addLayer(layers[i]);
 
6654         _addZoomLimit: function (layer) {
 
6655                 if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
 
6656                         this._zoomBoundLayers[stamp(layer)] = layer;
 
6657                         this._updateZoomLevels();
 
6661         _removeZoomLimit: function (layer) {
 
6662                 var id = stamp(layer);
 
6664                 if (this._zoomBoundLayers[id]) {
 
6665                         delete this._zoomBoundLayers[id];
 
6666                         this._updateZoomLevels();
 
6670         _updateZoomLevels: function () {
 
6671                 var minZoom = Infinity,
 
6672                     maxZoom = -Infinity,
 
6673                     oldZoomSpan = this._getZoomSpan();
 
6675                 for (var i in this._zoomBoundLayers) {
 
6676                         var options = this._zoomBoundLayers[i].options;
 
6678                         minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
 
6679                         maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
 
6682                 this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
 
6683                 this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
 
6685                 // @section Map state change events
 
6686                 // @event zoomlevelschange: Event
 
6687                 // Fired when the number of zoomlevels on the map is changed due
 
6688                 // to adding or removing a layer.
 
6689                 if (oldZoomSpan !== this._getZoomSpan()) {
 
6690                         this.fire('zoomlevelschange');
 
6693                 if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
 
6694                         this.setZoom(this._layersMaxZoom);
 
6696                 if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
 
6697                         this.setZoom(this._layersMinZoom);
 
6707  * Used to group several layers and handle them as one. If you add it to the map,
 
6708  * any layers added or removed from the group will be added/removed on the map as
 
6709  * well. Extends `Layer`.
 
6714  * L.layerGroup([marker1, marker2])
 
6715  *      .addLayer(polyline)
 
6720 var LayerGroup = Layer.extend({
 
6722         initialize: function (layers, options) {
 
6723                 setOptions(this, options);
 
6730                         for (i = 0, len = layers.length; i < len; i++) {
 
6731                                 this.addLayer(layers[i]);
 
6736         // @method addLayer(layer: Layer): this
 
6737         // Adds the given layer to the group.
 
6738         addLayer: function (layer) {
 
6739                 var id = this.getLayerId(layer);
 
6741                 this._layers[id] = layer;
 
6744                         this._map.addLayer(layer);
 
6750         // @method removeLayer(layer: Layer): this
 
6751         // Removes the given layer from the group.
 
6753         // @method removeLayer(id: Number): this
 
6754         // Removes the layer with the given internal ID from the group.
 
6755         removeLayer: function (layer) {
 
6756                 var id = layer in this._layers ? layer : this.getLayerId(layer);
 
6758                 if (this._map && this._layers[id]) {
 
6759                         this._map.removeLayer(this._layers[id]);
 
6762                 delete this._layers[id];
 
6767         // @method hasLayer(layer: Layer): Boolean
 
6768         // Returns `true` if the given layer is currently added to the group.
 
6770         // @method hasLayer(id: Number): Boolean
 
6771         // Returns `true` if the given internal ID is currently added to the group.
 
6772         hasLayer: function (layer) {
 
6773                 return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
 
6776         // @method clearLayers(): this
 
6777         // Removes all the layers from the group.
 
6778         clearLayers: function () {
 
6779                 return this.eachLayer(this.removeLayer, this);
 
6782         // @method invoke(methodName: String, …): this
 
6783         // Calls `methodName` on every layer contained in this group, passing any
 
6784         // additional parameters. Has no effect if the layers contained do not
 
6785         // implement `methodName`.
 
6786         invoke: function (methodName) {
 
6787                 var args = Array.prototype.slice.call(arguments, 1),
 
6790                 for (i in this._layers) {
 
6791                         layer = this._layers[i];
 
6793                         if (layer[methodName]) {
 
6794                                 layer[methodName].apply(layer, args);
 
6801         onAdd: function (map) {
 
6802                 this.eachLayer(map.addLayer, map);
 
6805         onRemove: function (map) {
 
6806                 this.eachLayer(map.removeLayer, map);
 
6809         // @method eachLayer(fn: Function, context?: Object): this
 
6810         // Iterates over the layers of the group, optionally specifying context of the iterator function.
 
6812         // group.eachLayer(function (layer) {
 
6813         //      layer.bindPopup('Hello');
 
6816         eachLayer: function (method, context) {
 
6817                 for (var i in this._layers) {
 
6818                         method.call(context, this._layers[i]);
 
6823         // @method getLayer(id: Number): Layer
 
6824         // Returns the layer with the given internal ID.
 
6825         getLayer: function (id) {
 
6826                 return this._layers[id];
 
6829         // @method getLayers(): Layer[]
 
6830         // Returns an array of all the layers added to the group.
 
6831         getLayers: function () {
 
6833                 this.eachLayer(layers.push, layers);
 
6837         // @method setZIndex(zIndex: Number): this
 
6838         // Calls `setZIndex` on every layer contained in this group, passing the z-index.
 
6839         setZIndex: function (zIndex) {
 
6840                 return this.invoke('setZIndex', zIndex);
 
6843         // @method getLayerId(layer: Layer): Number
 
6844         // Returns the internal ID for a layer
 
6845         getLayerId: function (layer) {
 
6846                 return stamp(layer);
 
6851 // @factory L.layerGroup(layers?: Layer[], options?: Object)
 
6852 // Create a layer group, optionally given an initial set of layers and an `options` object.
 
6853 var layerGroup = function (layers, options) {
 
6854         return new LayerGroup(layers, options);
 
6858  * @class FeatureGroup
 
6859  * @aka L.FeatureGroup
 
6860  * @inherits LayerGroup
 
6862  * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
 
6863  *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
 
6864  *  * Events are propagated to the `FeatureGroup`, so if the group has an event
 
6865  * handler, it will handle events from any of the layers. This includes mouse events
 
6866  * and custom events.
 
6867  *  * Has `layeradd` and `layerremove` events
 
6872  * L.featureGroup([marker1, marker2, polyline])
 
6873  *      .bindPopup('Hello world!')
 
6874  *      .on('click', function() { alert('Clicked on a member of the group!'); })
 
6879 var FeatureGroup = LayerGroup.extend({
 
6881         addLayer: function (layer) {
 
6882                 if (this.hasLayer(layer)) {
 
6886                 layer.addEventParent(this);
 
6888                 LayerGroup.prototype.addLayer.call(this, layer);
 
6890                 // @event layeradd: LayerEvent
 
6891                 // Fired when a layer is added to this `FeatureGroup`
 
6892                 return this.fire('layeradd', {layer: layer});
 
6895         removeLayer: function (layer) {
 
6896                 if (!this.hasLayer(layer)) {
 
6899                 if (layer in this._layers) {
 
6900                         layer = this._layers[layer];
 
6903                 layer.removeEventParent(this);
 
6905                 LayerGroup.prototype.removeLayer.call(this, layer);
 
6907                 // @event layerremove: LayerEvent
 
6908                 // Fired when a layer is removed from this `FeatureGroup`
 
6909                 return this.fire('layerremove', {layer: layer});
 
6912         // @method setStyle(style: Path options): this
 
6913         // Sets the given path options to each layer of the group that has a `setStyle` method.
 
6914         setStyle: function (style) {
 
6915                 return this.invoke('setStyle', style);
 
6918         // @method bringToFront(): this
 
6919         // Brings the layer group to the top of all other layers
 
6920         bringToFront: function () {
 
6921                 return this.invoke('bringToFront');
 
6924         // @method bringToBack(): this
 
6925         // Brings the layer group to the back of all other layers
 
6926         bringToBack: function () {
 
6927                 return this.invoke('bringToBack');
 
6930         // @method getBounds(): LatLngBounds
 
6931         // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
 
6932         getBounds: function () {
 
6933                 var bounds = new LatLngBounds();
 
6935                 for (var id in this._layers) {
 
6936                         var layer = this._layers[id];
 
6937                         bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
 
6943 // @factory L.featureGroup(layers: Layer[])
 
6944 // Create a feature group, optionally given an initial set of layers.
 
6945 var featureGroup = function (layers) {
 
6946         return new FeatureGroup(layers);
 
6953  * Represents an icon to provide when creating a marker.
 
6958  * var myIcon = L.icon({
 
6959  *     iconUrl: 'my-icon.png',
 
6960  *     iconRetinaUrl: 'my-icon@2x.png',
 
6961  *     iconSize: [38, 95],
 
6962  *     iconAnchor: [22, 94],
 
6963  *     popupAnchor: [-3, -76],
 
6964  *     shadowUrl: 'my-icon-shadow.png',
 
6965  *     shadowRetinaUrl: 'my-icon-shadow@2x.png',
 
6966  *     shadowSize: [68, 95],
 
6967  *     shadowAnchor: [22, 94]
 
6970  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
 
6973  * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
 
6977 var Icon = Class.extend({
 
6982          * @option iconUrl: String = null
 
6983          * **(required)** The URL to the icon image (absolute or relative to your script path).
 
6985          * @option iconRetinaUrl: String = null
 
6986          * The URL to a retina sized version of the icon image (absolute or relative to your
 
6987          * script path). Used for Retina screen devices.
 
6989          * @option iconSize: Point = null
 
6990          * Size of the icon image in pixels.
 
6992          * @option iconAnchor: Point = null
 
6993          * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
 
6994          * will be aligned so that this point is at the marker's geographical location. Centered
 
6995          * by default if size is specified, also can be set in CSS with negative margins.
 
6997          * @option popupAnchor: Point = [0, 0]
 
6998          * The coordinates of the point from which popups will "open", relative to the icon anchor.
 
7000          * @option tooltipAnchor: Point = [0, 0]
 
7001          * The coordinates of the point from which tooltips will "open", relative to the icon anchor.
 
7003          * @option shadowUrl: String = null
 
7004          * The URL to the icon shadow image. If not specified, no shadow image will be created.
 
7006          * @option shadowRetinaUrl: String = null
 
7008          * @option shadowSize: Point = null
 
7009          * Size of the shadow image in pixels.
 
7011          * @option shadowAnchor: Point = null
 
7012          * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
 
7013          * as iconAnchor if not specified).
 
7015          * @option className: String = ''
 
7016          * A custom class name to assign to both icon and shadow images. Empty by default.
 
7020                 popupAnchor: [0, 0],
 
7021                 tooltipAnchor: [0, 0]
 
7024         initialize: function (options) {
 
7025                 setOptions(this, options);
 
7028         // @method createIcon(oldIcon?: HTMLElement): HTMLElement
 
7029         // Called internally when the icon has to be shown, returns a `<img>` HTML element
 
7030         // styled according to the options.
 
7031         createIcon: function (oldIcon) {
 
7032                 return this._createIcon('icon', oldIcon);
 
7035         // @method createShadow(oldIcon?: HTMLElement): HTMLElement
 
7036         // As `createIcon`, but for the shadow beneath it.
 
7037         createShadow: function (oldIcon) {
 
7038                 return this._createIcon('shadow', oldIcon);
 
7041         _createIcon: function (name, oldIcon) {
 
7042                 var src = this._getIconUrl(name);
 
7045                         if (name === 'icon') {
 
7046                                 throw new Error('iconUrl not set in Icon options (see the docs).');
 
7051                 var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
 
7052                 this._setIconStyles(img, name);
 
7057         _setIconStyles: function (img, name) {
 
7058                 var options = this.options;
 
7059                 var sizeOption = options[name + 'Size'];
 
7061                 if (typeof sizeOption === 'number') {
 
7062                         sizeOption = [sizeOption, sizeOption];
 
7065                 var size = toPoint(sizeOption),
 
7066                     anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
 
7067                             size && size.divideBy(2, true));
 
7069                 img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
 
7072                         img.style.marginLeft = (-anchor.x) + 'px';
 
7073                         img.style.marginTop  = (-anchor.y) + 'px';
 
7077                         img.style.width  = size.x + 'px';
 
7078                         img.style.height = size.y + 'px';
 
7082         _createImg: function (src, el) {
 
7083                 el = el || document.createElement('img');
 
7088         _getIconUrl: function (name) {
 
7089                 return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
 
7094 // @factory L.icon(options: Icon options)
 
7095 // Creates an icon instance with the given options.
 
7096 function icon(options) {
 
7097         return new Icon(options);
 
7101  * @miniclass Icon.Default (Icon)
 
7102  * @aka L.Icon.Default
 
7105  * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
 
7106  * no icon is specified. Points to the blue marker image distributed with Leaflet
 
7109  * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
 
7110  * (which is a set of `Icon options`).
 
7112  * If you want to _completely_ replace the default icon, override the
 
7113  * `L.Marker.prototype.options.icon` with your own icon instead.
 
7116 var IconDefault = Icon.extend({
 
7119                 iconUrl:       'marker-icon.png',
 
7120                 iconRetinaUrl: 'marker-icon-2x.png',
 
7121                 shadowUrl:     'marker-shadow.png',
 
7123                 iconAnchor:  [12, 41],
 
7124                 popupAnchor: [1, -34],
 
7125                 tooltipAnchor: [16, -28],
 
7126                 shadowSize:  [41, 41]
 
7129         _getIconUrl: function (name) {
 
7130                 if (!IconDefault.imagePath) {   // Deprecated, backwards-compatibility only
 
7131                         IconDefault.imagePath = this._detectIconPath();
 
7134                 // @option imagePath: String
 
7135                 // `Icon.Default` will try to auto-detect the location of the
 
7136                 // blue icon images. If you are placing these images in a non-standard
 
7137                 // way, set this option to point to the right path.
 
7138                 return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
 
7141         _detectIconPath: function () {
 
7142                 var el = create$1('div',  'leaflet-default-icon-path', document.body);
 
7143                 var path = getStyle(el, 'background-image') ||
 
7144                            getStyle(el, 'backgroundImage');     // IE8
 
7146                 document.body.removeChild(el);
 
7148                 if (path === null || path.indexOf('url') !== 0) {
 
7151                         path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, '');
 
7159  * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
 
7163 /* @namespace Marker
 
7164  * @section Interaction handlers
 
7166  * 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:
 
7169  * marker.dragging.disable();
 
7172  * @property dragging: Handler
 
7173  * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
 
7176 var MarkerDrag = Handler.extend({
 
7177         initialize: function (marker) {
 
7178                 this._marker = marker;
 
7181         addHooks: function () {
 
7182                 var icon = this._marker._icon;
 
7184                 if (!this._draggable) {
 
7185                         this._draggable = new Draggable(icon, icon, true);
 
7188                 this._draggable.on({
 
7189                         dragstart: this._onDragStart,
 
7190                         predrag: this._onPreDrag,
 
7192                         dragend: this._onDragEnd
 
7195                 addClass(icon, 'leaflet-marker-draggable');
 
7198         removeHooks: function () {
 
7199                 this._draggable.off({
 
7200                         dragstart: this._onDragStart,
 
7201                         predrag: this._onPreDrag,
 
7203                         dragend: this._onDragEnd
 
7206                 if (this._marker._icon) {
 
7207                         removeClass(this._marker._icon, 'leaflet-marker-draggable');
 
7211         moved: function () {
 
7212                 return this._draggable && this._draggable._moved;
 
7215         _adjustPan: function (e) {
 
7216                 var marker = this._marker,
 
7218                     speed = this._marker.options.autoPanSpeed,
 
7219                     padding = this._marker.options.autoPanPadding,
 
7220                     iconPos = getPosition(marker._icon),
 
7221                     bounds = map.getPixelBounds(),
 
7222                     origin = map.getPixelOrigin();
 
7224                 var panBounds = toBounds(
 
7225                         bounds.min._subtract(origin).add(padding),
 
7226                         bounds.max._subtract(origin).subtract(padding)
 
7229                 if (!panBounds.contains(iconPos)) {
 
7230                         // Compute incremental movement
 
7231                         var movement = toPoint(
 
7232                                 (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) -
 
7233                                 (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x),
 
7235                                 (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) -
 
7236                                 (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y)
 
7237                         ).multiplyBy(speed);
 
7239                         map.panBy(movement, {animate: false});
 
7241                         this._draggable._newPos._add(movement);
 
7242                         this._draggable._startPos._add(movement);
 
7244                         setPosition(marker._icon, this._draggable._newPos);
 
7247                         this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
 
7251         _onDragStart: function () {
 
7252                 // @section Dragging events
 
7253                 // @event dragstart: Event
 
7254                 // Fired when the user starts dragging the marker.
 
7256                 // @event movestart: Event
 
7257                 // Fired when the marker starts moving (because of dragging).
 
7259                 this._oldLatLng = this._marker.getLatLng();
 
7266         _onPreDrag: function (e) {
 
7267                 if (this._marker.options.autoPan) {
 
7268                         cancelAnimFrame(this._panRequest);
 
7269                         this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
 
7273         _onDrag: function (e) {
 
7274                 var marker = this._marker,
 
7275                     shadow = marker._shadow,
 
7276                     iconPos = getPosition(marker._icon),
 
7277                     latlng = marker._map.layerPointToLatLng(iconPos);
 
7279                 // update shadow position
 
7281                         setPosition(shadow, iconPos);
 
7284                 marker._latlng = latlng;
 
7286                 e.oldLatLng = this._oldLatLng;
 
7288                 // @event drag: Event
 
7289                 // Fired repeatedly while the user drags the marker.
 
7295         _onDragEnd: function (e) {
 
7296                 // @event dragend: DragEndEvent
 
7297                 // Fired when the user stops dragging the marker.
 
7299                  cancelAnimFrame(this._panRequest);
 
7301                 // @event moveend: Event
 
7302                 // Fired when the marker stops moving (because of dragging).
 
7303                 delete this._oldLatLng;
 
7306                     .fire('dragend', e);
 
7312  * @inherits Interactive layer
 
7314  * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
 
7319  * L.marker([50.5, 30.5]).addTo(map);
 
7323 var Marker = Layer.extend({
 
7326         // @aka Marker options
 
7328                 // @option icon: Icon = *
 
7329                 // Icon instance to use for rendering the marker.
 
7330                 // See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
 
7331                 // If not specified, a common instance of `L.Icon.Default` is used.
 
7332                 icon: new IconDefault(),
 
7334                 // Option inherited from "Interactive layer" abstract class
 
7337                 // @option keyboard: Boolean = true
 
7338                 // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
 
7341                 // @option title: String = ''
 
7342                 // Text for the browser tooltip that appear on marker hover (no tooltip by default).
 
7345                 // @option alt: String = ''
 
7346                 // Text for the `alt` attribute of the icon image (useful for accessibility).
 
7349                 // @option zIndexOffset: Number = 0
 
7350                 // 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).
 
7353                 // @option opacity: Number = 1.0
 
7354                 // The opacity of the marker.
 
7357                 // @option riseOnHover: Boolean = false
 
7358                 // If `true`, the marker will get on top of others when you hover the mouse over it.
 
7361                 // @option riseOffset: Number = 250
 
7362                 // The z-index offset used for the `riseOnHover` feature.
 
7365                 // @option pane: String = 'markerPane'
 
7366                 // `Map pane` where the markers icon will be added.
 
7369                 // @option bubblingMouseEvents: Boolean = false
 
7370                 // When `true`, a mouse event on this marker will trigger the same event on the map
 
7371                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
 
7372                 bubblingMouseEvents: false,
 
7374                 // @section Draggable marker options
 
7375                 // @option draggable: Boolean = false
 
7376                 // Whether the marker is draggable with mouse/touch or not.
 
7379                 // @option autoPan: Boolean = false
 
7380                 // Whether to pan the map when dragging this marker near its edge or not.
 
7383                 // @option autoPanPadding: Point = Point(50, 50)
 
7384                 // Distance (in pixels to the left/right and to the top/bottom) of the
 
7385                 // map edge to start panning the map.
 
7386                 autoPanPadding: [50, 50],
 
7388                 // @option autoPanSpeed: Number = 10
 
7389                 // Number of pixels the map should pan by.
 
7395          * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
 
7398         initialize: function (latlng, options) {
 
7399                 setOptions(this, options);
 
7400                 this._latlng = toLatLng(latlng);
 
7403         onAdd: function (map) {
 
7404                 this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
 
7406                 if (this._zoomAnimated) {
 
7407                         map.on('zoomanim', this._animateZoom, this);
 
7414         onRemove: function (map) {
 
7415                 if (this.dragging && this.dragging.enabled()) {
 
7416                         this.options.draggable = true;
 
7417                         this.dragging.removeHooks();
 
7419                 delete this.dragging;
 
7421                 if (this._zoomAnimated) {
 
7422                         map.off('zoomanim', this._animateZoom, this);
 
7426                 this._removeShadow();
 
7429         getEvents: function () {
 
7432                         viewreset: this.update
 
7436         // @method getLatLng: LatLng
 
7437         // Returns the current geographical position of the marker.
 
7438         getLatLng: function () {
 
7439                 return this._latlng;
 
7442         // @method setLatLng(latlng: LatLng): this
 
7443         // Changes the marker position to the given point.
 
7444         setLatLng: function (latlng) {
 
7445                 var oldLatLng = this._latlng;
 
7446                 this._latlng = toLatLng(latlng);
 
7449                 // @event move: Event
 
7450                 // 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`.
 
7451                 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
 
7454         // @method setZIndexOffset(offset: Number): this
 
7455         // Changes the [zIndex offset](#marker-zindexoffset) of the marker.
 
7456         setZIndexOffset: function (offset) {
 
7457                 this.options.zIndexOffset = offset;
 
7458                 return this.update();
 
7461         // @method setIcon(icon: Icon): this
 
7462         // Changes the marker icon.
 
7463         setIcon: function (icon) {
 
7465                 this.options.icon = icon;
 
7473                         this.bindPopup(this._popup, this._popup.options);
 
7479         getElement: function () {
 
7483         update: function () {
 
7485                 if (this._icon && this._map) {
 
7486                         var pos = this._map.latLngToLayerPoint(this._latlng).round();
 
7493         _initIcon: function () {
 
7494                 var options = this.options,
 
7495                     classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
 
7497                 var icon = options.icon.createIcon(this._icon),
 
7500                 // if we're not reusing the icon, remove the old one and init new one
 
7501                 if (icon !== this._icon) {
 
7507                         if (options.title) {
 
7508                                 icon.title = options.title;
 
7511                         if (icon.tagName === 'IMG') {
 
7512                                 icon.alt = options.alt || '';
 
7516                 addClass(icon, classToAdd);
 
7518                 if (options.keyboard) {
 
7519                         icon.tabIndex = '0';
 
7524                 if (options.riseOnHover) {
 
7526                                 mouseover: this._bringToFront,
 
7527                                 mouseout: this._resetZIndex
 
7531                 var newShadow = options.icon.createShadow(this._shadow),
 
7534                 if (newShadow !== this._shadow) {
 
7535                         this._removeShadow();
 
7540                         addClass(newShadow, classToAdd);
 
7543                 this._shadow = newShadow;
 
7546                 if (options.opacity < 1) {
 
7547                         this._updateOpacity();
 
7552                         this.getPane().appendChild(this._icon);
 
7554                 this._initInteraction();
 
7555                 if (newShadow && addShadow) {
 
7556                         this.getPane('shadowPane').appendChild(this._shadow);
 
7560         _removeIcon: function () {
 
7561                 if (this.options.riseOnHover) {
 
7563                                 mouseover: this._bringToFront,
 
7564                                 mouseout: this._resetZIndex
 
7569                 this.removeInteractiveTarget(this._icon);
 
7574         _removeShadow: function () {
 
7576                         remove(this._shadow);
 
7578                 this._shadow = null;
 
7581         _setPos: function (pos) {
 
7582                 setPosition(this._icon, pos);
 
7585                         setPosition(this._shadow, pos);
 
7588                 this._zIndex = pos.y + this.options.zIndexOffset;
 
7590                 this._resetZIndex();
 
7593         _updateZIndex: function (offset) {
 
7594                 this._icon.style.zIndex = this._zIndex + offset;
 
7597         _animateZoom: function (opt) {
 
7598                 var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
 
7603         _initInteraction: function () {
 
7605                 if (!this.options.interactive) { return; }
 
7607                 addClass(this._icon, 'leaflet-interactive');
 
7609                 this.addInteractiveTarget(this._icon);
 
7612                         var draggable = this.options.draggable;
 
7613                         if (this.dragging) {
 
7614                                 draggable = this.dragging.enabled();
 
7615                                 this.dragging.disable();
 
7618                         this.dragging = new MarkerDrag(this);
 
7621                                 this.dragging.enable();
 
7626         // @method setOpacity(opacity: Number): this
 
7627         // Changes the opacity of the marker.
 
7628         setOpacity: function (opacity) {
 
7629                 this.options.opacity = opacity;
 
7631                         this._updateOpacity();
 
7637         _updateOpacity: function () {
 
7638                 var opacity = this.options.opacity;
 
7640                 setOpacity(this._icon, opacity);
 
7643                         setOpacity(this._shadow, opacity);
 
7647         _bringToFront: function () {
 
7648                 this._updateZIndex(this.options.riseOffset);
 
7651         _resetZIndex: function () {
 
7652                 this._updateZIndex(0);
 
7655         _getPopupAnchor: function () {
 
7656                 return this.options.icon.options.popupAnchor;
 
7659         _getTooltipAnchor: function () {
 
7660                 return this.options.icon.options.tooltipAnchor;
 
7665 // factory L.marker(latlng: LatLng, options? : Marker options)
 
7667 // @factory L.marker(latlng: LatLng, options? : Marker options)
 
7668 // Instantiates a Marker object given a geographical point and optionally an options object.
 
7669 function marker(latlng, options) {
 
7670         return new Marker(latlng, options);
 
7676  * @inherits Interactive layer
 
7678  * An abstract class that contains options and constants shared between vector
 
7679  * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
 
7682 var Path = Layer.extend({
 
7685         // @aka Path options
 
7687                 // @option stroke: Boolean = true
 
7688                 // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
 
7691                 // @option color: String = '#3388ff'
 
7695                 // @option weight: Number = 3
 
7696                 // Stroke width in pixels
 
7699                 // @option opacity: Number = 1.0
 
7703                 // @option lineCap: String= 'round'
 
7704                 // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
 
7707                 // @option lineJoin: String = 'round'
 
7708                 // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
 
7711                 // @option dashArray: String = null
 
7712                 // 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).
 
7715                 // @option dashOffset: String = null
 
7716                 // 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).
 
7719                 // @option fill: Boolean = depends
 
7720                 // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
 
7723                 // @option fillColor: String = *
 
7724                 // Fill color. Defaults to the value of the [`color`](#path-color) option
 
7727                 // @option fillOpacity: Number = 0.2
 
7731                 // @option fillRule: String = 'evenodd'
 
7732                 // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
 
7733                 fillRule: 'evenodd',
 
7737                 // Option inherited from "Interactive layer" abstract class
 
7740                 // @option bubblingMouseEvents: Boolean = true
 
7741                 // When `true`, a mouse event on this path will trigger the same event on the map
 
7742                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
 
7743                 bubblingMouseEvents: true
 
7746         beforeAdd: function (map) {
 
7747                 // Renderer is set here because we need to call renderer.getEvents
 
7748                 // before this.getEvents.
 
7749                 this._renderer = map.getRenderer(this);
 
7752         onAdd: function () {
 
7753                 this._renderer._initPath(this);
 
7755                 this._renderer._addPath(this);
 
7758         onRemove: function () {
 
7759                 this._renderer._removePath(this);
 
7762         // @method redraw(): this
 
7763         // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
 
7764         redraw: function () {
 
7766                         this._renderer._updatePath(this);
 
7771         // @method setStyle(style: Path options): this
 
7772         // Changes the appearance of a Path based on the options in the `Path options` object.
 
7773         setStyle: function (style) {
 
7774                 setOptions(this, style);
 
7775                 if (this._renderer) {
 
7776                         this._renderer._updateStyle(this);
 
7781         // @method bringToFront(): this
 
7782         // Brings the layer to the top of all path layers.
 
7783         bringToFront: function () {
 
7784                 if (this._renderer) {
 
7785                         this._renderer._bringToFront(this);
 
7790         // @method bringToBack(): this
 
7791         // Brings the layer to the bottom of all path layers.
 
7792         bringToBack: function () {
 
7793                 if (this._renderer) {
 
7794                         this._renderer._bringToBack(this);
 
7799         getElement: function () {
 
7803         _reset: function () {
 
7804                 // defined in child classes
 
7809         _clickTolerance: function () {
 
7810                 // used when doing hit detection for Canvas layers
 
7811                 return (this.options.stroke ? this.options.weight / 2 : 0) + this._renderer.options.tolerance;
 
7816  * @class CircleMarker
 
7817  * @aka L.CircleMarker
 
7820  * A circle of a fixed size with radius specified in pixels. Extends `Path`.
 
7823 var CircleMarker = Path.extend({
 
7826         // @aka CircleMarker options
 
7830                 // @option radius: Number = 10
 
7831                 // Radius of the circle marker, in pixels
 
7835         initialize: function (latlng, options) {
 
7836                 setOptions(this, options);
 
7837                 this._latlng = toLatLng(latlng);
 
7838                 this._radius = this.options.radius;
 
7841         // @method setLatLng(latLng: LatLng): this
 
7842         // Sets the position of a circle marker to a new location.
 
7843         setLatLng: function (latlng) {
 
7844                 this._latlng = toLatLng(latlng);
 
7846                 return this.fire('move', {latlng: this._latlng});
 
7849         // @method getLatLng(): LatLng
 
7850         // Returns the current geographical position of the circle marker
 
7851         getLatLng: function () {
 
7852                 return this._latlng;
 
7855         // @method setRadius(radius: Number): this
 
7856         // Sets the radius of a circle marker. Units are in pixels.
 
7857         setRadius: function (radius) {
 
7858                 this.options.radius = this._radius = radius;
 
7859                 return this.redraw();
 
7862         // @method getRadius(): Number
 
7863         // Returns the current radius of the circle
 
7864         getRadius: function () {
 
7865                 return this._radius;
 
7868         setStyle : function (options) {
 
7869                 var radius = options && options.radius || this._radius;
 
7870                 Path.prototype.setStyle.call(this, options);
 
7871                 this.setRadius(radius);
 
7875         _project: function () {
 
7876                 this._point = this._map.latLngToLayerPoint(this._latlng);
 
7877                 this._updateBounds();
 
7880         _updateBounds: function () {
 
7881                 var r = this._radius,
 
7882                     r2 = this._radiusY || r,
 
7883                     w = this._clickTolerance(),
 
7884                     p = [r + w, r2 + w];
 
7885                 this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
 
7888         _update: function () {
 
7894         _updatePath: function () {
 
7895                 this._renderer._updateCircle(this);
 
7898         _empty: function () {
 
7899                 return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
 
7902         // Needed by the `Canvas` renderer for interactivity
 
7903         _containsPoint: function (p) {
 
7904                 return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
 
7909 // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
 
7910 // Instantiates a circle marker object given a geographical point, and an optional options object.
 
7911 function circleMarker(latlng, options) {
 
7912         return new CircleMarker(latlng, options);
 
7918  * @inherits CircleMarker
 
7920  * A class for drawing circle overlays on a map. Extends `CircleMarker`.
 
7922  * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
 
7927  * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
 
7931 var Circle = CircleMarker.extend({
 
7933         initialize: function (latlng, options, legacyOptions) {
 
7934                 if (typeof options === 'number') {
 
7935                         // Backwards compatibility with 0.7.x factory (latlng, radius, options?)
 
7936                         options = extend({}, legacyOptions, {radius: options});
 
7938                 setOptions(this, options);
 
7939                 this._latlng = toLatLng(latlng);
 
7941                 if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
 
7944                 // @aka Circle options
 
7945                 // @option radius: Number; Radius of the circle, in meters.
 
7946                 this._mRadius = this.options.radius;
 
7949         // @method setRadius(radius: Number): this
 
7950         // Sets the radius of a circle. Units are in meters.
 
7951         setRadius: function (radius) {
 
7952                 this._mRadius = radius;
 
7953                 return this.redraw();
 
7956         // @method getRadius(): Number
 
7957         // Returns the current radius of a circle. Units are in meters.
 
7958         getRadius: function () {
 
7959                 return this._mRadius;
 
7962         // @method getBounds(): LatLngBounds
 
7963         // Returns the `LatLngBounds` of the path.
 
7964         getBounds: function () {
 
7965                 var half = [this._radius, this._radiusY || this._radius];
 
7967                 return new LatLngBounds(
 
7968                         this._map.layerPointToLatLng(this._point.subtract(half)),
 
7969                         this._map.layerPointToLatLng(this._point.add(half)));
 
7972         setStyle: Path.prototype.setStyle,
 
7974         _project: function () {
 
7976                 var lng = this._latlng.lng,
 
7977                     lat = this._latlng.lat,
 
7979                     crs = map.options.crs;
 
7981                 if (crs.distance === Earth.distance) {
 
7982                         var d = Math.PI / 180,
 
7983                             latR = (this._mRadius / Earth.R) / d,
 
7984                             top = map.project([lat + latR, lng]),
 
7985                             bottom = map.project([lat - latR, lng]),
 
7986                             p = top.add(bottom).divideBy(2),
 
7987                             lat2 = map.unproject(p).lat,
 
7988                             lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
 
7989                                     (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
 
7991                         if (isNaN(lngR) || lngR === 0) {
 
7992                                 lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
 
7995                         this._point = p.subtract(map.getPixelOrigin());
 
7996                         this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x;
 
7997                         this._radiusY = p.y - top.y;
 
8000                         var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
 
8002                         this._point = map.latLngToLayerPoint(this._latlng);
 
8003                         this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
 
8006                 this._updateBounds();
 
8010 // @factory L.circle(latlng: LatLng, options?: Circle options)
 
8011 // Instantiates a circle object given a geographical point, and an options object
 
8012 // which contains the circle radius.
 
8014 // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
 
8015 // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
 
8016 // Do not use in new applications or plugins.
 
8017 function circle(latlng, options, legacyOptions) {
 
8018         return new Circle(latlng, options, legacyOptions);
 
8026  * A class for drawing polyline overlays on a map. Extends `Path`.
 
8031  * // create a red polyline from an array of LatLng points
 
8038  * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
 
8040  * // zoom the map to the polyline
 
8041  * map.fitBounds(polyline.getBounds());
 
8044  * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
 
8047  * // create a red polyline from an array of arrays of LatLng points
 
8049  *      [[45.51, -122.68],
 
8060 var Polyline = Path.extend({
 
8063         // @aka Polyline options
 
8065                 // @option smoothFactor: Number = 1.0
 
8066                 // How much to simplify the polyline on each zoom level. More means
 
8067                 // better performance and smoother look, and less means more accurate representation.
 
8070                 // @option noClip: Boolean = false
 
8071                 // Disable polyline clipping.
 
8075         initialize: function (latlngs, options) {
 
8076                 setOptions(this, options);
 
8077                 this._setLatLngs(latlngs);
 
8080         // @method getLatLngs(): LatLng[]
 
8081         // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
 
8082         getLatLngs: function () {
 
8083                 return this._latlngs;
 
8086         // @method setLatLngs(latlngs: LatLng[]): this
 
8087         // Replaces all the points in the polyline with the given array of geographical points.
 
8088         setLatLngs: function (latlngs) {
 
8089                 this._setLatLngs(latlngs);
 
8090                 return this.redraw();
 
8093         // @method isEmpty(): Boolean
 
8094         // Returns `true` if the Polyline has no LatLngs.
 
8095         isEmpty: function () {
 
8096                 return !this._latlngs.length;
 
8099         // @method closestLayerPoint(p: Point): Point
 
8100         // Returns the point closest to `p` on the Polyline.
 
8101         closestLayerPoint: function (p) {
 
8102                 var minDistance = Infinity,
 
8104                     closest = _sqClosestPointOnSegment,
 
8107                 for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
 
8108                         var points = this._parts[j];
 
8110                         for (var i = 1, len = points.length; i < len; i++) {
 
8114                                 var sqDist = closest(p, p1, p2, true);
 
8116                                 if (sqDist < minDistance) {
 
8117                                         minDistance = sqDist;
 
8118                                         minPoint = closest(p, p1, p2);
 
8123                         minPoint.distance = Math.sqrt(minDistance);
 
8128         // @method getCenter(): LatLng
 
8129         // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
 
8130         getCenter: function () {
 
8131                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
8133                         throw new Error('Must add layer to map before using getCenter()');
 
8136                 var i, halfDist, segDist, dist, p1, p2, ratio,
 
8137                     points = this._rings[0],
 
8138                     len = points.length;
 
8140                 if (!len) { return null; }
 
8142                 // polyline centroid algorithm; only uses the first ring if there are multiple
 
8144                 for (i = 0, halfDist = 0; i < len - 1; i++) {
 
8145                         halfDist += points[i].distanceTo(points[i + 1]) / 2;
 
8148                 // The line is so small in the current view that all points are on the same pixel.
 
8149                 if (halfDist === 0) {
 
8150                         return this._map.layerPointToLatLng(points[0]);
 
8153                 for (i = 0, dist = 0; i < len - 1; i++) {
 
8156                         segDist = p1.distanceTo(p2);
 
8159                         if (dist > halfDist) {
 
8160                                 ratio = (dist - halfDist) / segDist;
 
8161                                 return this._map.layerPointToLatLng([
 
8162                                         p2.x - ratio * (p2.x - p1.x),
 
8163                                         p2.y - ratio * (p2.y - p1.y)
 
8169         // @method getBounds(): LatLngBounds
 
8170         // Returns the `LatLngBounds` of the path.
 
8171         getBounds: function () {
 
8172                 return this._bounds;
 
8175         // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
 
8176         // Adds a given point to the polyline. By default, adds to the first ring of
 
8177         // the polyline in case of a multi-polyline, but can be overridden by passing
 
8178         // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
 
8179         addLatLng: function (latlng, latlngs) {
 
8180                 latlngs = latlngs || this._defaultShape();
 
8181                 latlng = toLatLng(latlng);
 
8182                 latlngs.push(latlng);
 
8183                 this._bounds.extend(latlng);
 
8184                 return this.redraw();
 
8187         _setLatLngs: function (latlngs) {
 
8188                 this._bounds = new LatLngBounds();
 
8189                 this._latlngs = this._convertLatLngs(latlngs);
 
8192         _defaultShape: function () {
 
8193                 return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
 
8196         // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
 
8197         _convertLatLngs: function (latlngs) {
 
8199                     flat = isFlat(latlngs);
 
8201                 for (var i = 0, len = latlngs.length; i < len; i++) {
 
8203                                 result[i] = toLatLng(latlngs[i]);
 
8204                                 this._bounds.extend(result[i]);
 
8206                                 result[i] = this._convertLatLngs(latlngs[i]);
 
8213         _project: function () {
 
8214                 var pxBounds = new Bounds();
 
8216                 this._projectLatlngs(this._latlngs, this._rings, pxBounds);
 
8218                 var w = this._clickTolerance(),
 
8219                     p = new Point(w, w);
 
8221                 if (this._bounds.isValid() && pxBounds.isValid()) {
 
8222                         pxBounds.min._subtract(p);
 
8223                         pxBounds.max._add(p);
 
8224                         this._pxBounds = pxBounds;
 
8228         // recursively turns latlngs into a set of rings with projected coordinates
 
8229         _projectLatlngs: function (latlngs, result, projectedBounds) {
 
8230                 var flat = latlngs[0] instanceof LatLng,
 
8231                     len = latlngs.length,
 
8236                         for (i = 0; i < len; i++) {
 
8237                                 ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
 
8238                                 projectedBounds.extend(ring[i]);
 
8242                         for (i = 0; i < len; i++) {
 
8243                                 this._projectLatlngs(latlngs[i], result, projectedBounds);
 
8248         // clip polyline by renderer bounds so that we have less to render for performance
 
8249         _clipPoints: function () {
 
8250                 var bounds = this._renderer._bounds;
 
8253                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
8257                 if (this.options.noClip) {
 
8258                         this._parts = this._rings;
 
8262                 var parts = this._parts,
 
8263                     i, j, k, len, len2, segment, points;
 
8265                 for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
 
8266                         points = this._rings[i];
 
8268                         for (j = 0, len2 = points.length; j < len2 - 1; j++) {
 
8269                                 segment = clipSegment(points[j], points[j + 1], bounds, j, true);
 
8271                                 if (!segment) { continue; }
 
8273                                 parts[k] = parts[k] || [];
 
8274                                 parts[k].push(segment[0]);
 
8276                                 // if segment goes out of screen, or it's the last one, it's the end of the line part
 
8277                                 if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
 
8278                                         parts[k].push(segment[1]);
 
8285         // simplify each clipped part of the polyline for performance
 
8286         _simplifyPoints: function () {
 
8287                 var parts = this._parts,
 
8288                     tolerance = this.options.smoothFactor;
 
8290                 for (var i = 0, len = parts.length; i < len; i++) {
 
8291                         parts[i] = simplify(parts[i], tolerance);
 
8295         _update: function () {
 
8296                 if (!this._map) { return; }
 
8299                 this._simplifyPoints();
 
8303         _updatePath: function () {
 
8304                 this._renderer._updatePoly(this);
 
8307         // Needed by the `Canvas` renderer for interactivity
 
8308         _containsPoint: function (p, closed) {
 
8309                 var i, j, k, len, len2, part,
 
8310                     w = this._clickTolerance();
 
8312                 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
 
8314                 // hit detection for polylines
 
8315                 for (i = 0, len = this._parts.length; i < len; i++) {
 
8316                         part = this._parts[i];
 
8318                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
8319                                 if (!closed && (j === 0)) { continue; }
 
8321                                 if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
 
8330 // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
 
8331 // Instantiates a polyline object given an array of geographical points and
 
8332 // optionally an options object. You can create a `Polyline` object with
 
8333 // multiple separate lines (`MultiPolyline`) by passing an array of arrays
 
8334 // of geographic points.
 
8335 function polyline(latlngs, options) {
 
8336         return new Polyline(latlngs, options);
 
8339 // Retrocompat. Allow plugins to support Leaflet versions before and after 1.1.
 
8340 Polyline._flat = _flat;
 
8345  * @inherits Polyline
 
8347  * A class for drawing polygon overlays on a map. Extends `Polyline`.
 
8349  * 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.
 
8355  * // create a red polygon from an array of LatLng points
 
8356  * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
 
8358  * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
 
8360  * // zoom the map to the polygon
 
8361  * map.fitBounds(polygon.getBounds());
 
8364  * 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:
 
8368  *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
 
8369  *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
 
8373  * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
 
8377  *   [ // first polygon
 
8378  *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
 
8379  *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
 
8381  *   [ // second polygon
 
8382  *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
 
8388 var Polygon = Polyline.extend({
 
8394         isEmpty: function () {
 
8395                 return !this._latlngs.length || !this._latlngs[0].length;
 
8398         getCenter: function () {
 
8399                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
8401                         throw new Error('Must add layer to map before using getCenter()');
 
8404                 var i, j, p1, p2, f, area, x, y, center,
 
8405                     points = this._rings[0],
 
8406                     len = points.length;
 
8408                 if (!len) { return null; }
 
8410                 // polygon centroid algorithm; only uses the first ring if there are multiple
 
8414                 for (i = 0, j = len - 1; i < len; j = i++) {
 
8418                         f = p1.y * p2.x - p2.y * p1.x;
 
8419                         x += (p1.x + p2.x) * f;
 
8420                         y += (p1.y + p2.y) * f;
 
8425                         // Polygon is so small that all points are on same pixel.
 
8428                         center = [x / area, y / area];
 
8430                 return this._map.layerPointToLatLng(center);
 
8433         _convertLatLngs: function (latlngs) {
 
8434                 var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
 
8435                     len = result.length;
 
8437                 // remove last point if it equals first one
 
8438                 if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
 
8444         _setLatLngs: function (latlngs) {
 
8445                 Polyline.prototype._setLatLngs.call(this, latlngs);
 
8446                 if (isFlat(this._latlngs)) {
 
8447                         this._latlngs = [this._latlngs];
 
8451         _defaultShape: function () {
 
8452                 return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
 
8455         _clipPoints: function () {
 
8456                 // polygons need a different clipping algorithm so we redefine that
 
8458                 var bounds = this._renderer._bounds,
 
8459                     w = this.options.weight,
 
8460                     p = new Point(w, w);
 
8462                 // increase clip padding by stroke width to avoid stroke on clip edges
 
8463                 bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
 
8466                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
8470                 if (this.options.noClip) {
 
8471                         this._parts = this._rings;
 
8475                 for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
 
8476                         clipped = clipPolygon(this._rings[i], bounds, true);
 
8477                         if (clipped.length) {
 
8478                                 this._parts.push(clipped);
 
8483         _updatePath: function () {
 
8484                 this._renderer._updatePoly(this, true);
 
8487         // Needed by the `Canvas` renderer for interactivity
 
8488         _containsPoint: function (p) {
 
8490                     part, p1, p2, i, j, k, len, len2;
 
8492                 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
 
8494                 // ray casting algorithm for detecting if point is in polygon
 
8495                 for (i = 0, len = this._parts.length; i < len; i++) {
 
8496                         part = this._parts[i];
 
8498                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
8502                                 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)) {
 
8508                 // also check if it's on polygon stroke
 
8509                 return inside || Polyline.prototype._containsPoint.call(this, p, true);
 
8515 // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
 
8516 function polygon(latlngs, options) {
 
8517         return new Polygon(latlngs, options);
 
8523  * @inherits FeatureGroup
 
8525  * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
 
8526  * GeoJSON data and display it on the map. Extends `FeatureGroup`.
 
8532  *      style: function (feature) {
 
8533  *              return {color: feature.properties.color};
 
8535  * }).bindPopup(function (layer) {
 
8536  *      return layer.feature.properties.description;
 
8541 var GeoJSON = FeatureGroup.extend({
 
8544          * @aka GeoJSON options
 
8546          * @option pointToLayer: Function = *
 
8547          * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
 
8548          * called when data is added, passing the GeoJSON point feature and its `LatLng`.
 
8549          * The default is to spawn a default `Marker`:
 
8551          * function(geoJsonPoint, latlng) {
 
8552          *      return L.marker(latlng);
 
8556          * @option style: Function = *
 
8557          * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
 
8558          * called internally when data is added.
 
8559          * The default value is to not override any defaults:
 
8561          * function (geoJsonFeature) {
 
8566          * @option onEachFeature: Function = *
 
8567          * A `Function` that will be called once for each created `Feature`, after it has
 
8568          * been created and styled. Useful for attaching events and popups to features.
 
8569          * The default is to do nothing with the newly created layers:
 
8571          * function (feature, layer) {}
 
8574          * @option filter: Function = *
 
8575          * A `Function` that will be used to decide whether to include a feature or not.
 
8576          * The default is to include all features:
 
8578          * function (geoJsonFeature) {
 
8582          * Note: dynamically changing the `filter` option will have effect only on newly
 
8583          * added data. It will _not_ re-evaluate already included features.
 
8585          * @option coordsToLatLng: Function = *
 
8586          * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
 
8587          * The default is the `coordsToLatLng` static method.
 
8590         initialize: function (geojson, options) {
 
8591                 setOptions(this, options);
 
8596                         this.addData(geojson);
 
8600         // @method addData( <GeoJSON> data ): this
 
8601         // Adds a GeoJSON object to the layer.
 
8602         addData: function (geojson) {
 
8603                 var features = isArray(geojson) ? geojson : geojson.features,
 
8607                         for (i = 0, len = features.length; i < len; i++) {
 
8608                                 // only add this if geometry or geometries are set and not null
 
8609                                 feature = features[i];
 
8610                                 if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
 
8611                                         this.addData(feature);
 
8617                 var options = this.options;
 
8619                 if (options.filter && !options.filter(geojson)) { return this; }
 
8621                 var layer = geometryToLayer(geojson, options);
 
8625                 layer.feature = asFeature(geojson);
 
8627                 layer.defaultOptions = layer.options;
 
8628                 this.resetStyle(layer);
 
8630                 if (options.onEachFeature) {
 
8631                         options.onEachFeature(geojson, layer);
 
8634                 return this.addLayer(layer);
 
8637         // @method resetStyle( <Path> layer ): this
 
8638         // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
 
8639         resetStyle: function (layer) {
 
8640                 // reset any custom styles
 
8641                 layer.options = extend({}, layer.defaultOptions);
 
8642                 this._setLayerStyle(layer, this.options.style);
 
8646         // @method setStyle( <Function> style ): this
 
8647         // Changes styles of GeoJSON vector layers with the given style function.
 
8648         setStyle: function (style) {
 
8649                 return this.eachLayer(function (layer) {
 
8650                         this._setLayerStyle(layer, style);
 
8654         _setLayerStyle: function (layer, style) {
 
8655                 if (typeof style === 'function') {
 
8656                         style = style(layer.feature);
 
8658                 if (layer.setStyle) {
 
8659                         layer.setStyle(style);
 
8665 // There are several static functions which can be called without instantiating L.GeoJSON:
 
8667 // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
 
8668 // Creates a `Layer` from a given GeoJSON feature. Can use a custom
 
8669 // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
 
8670 // functions if provided as options.
 
8671 function geometryToLayer(geojson, options) {
 
8673         var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
 
8674             coords = geometry ? geometry.coordinates : null,
 
8676             pointToLayer = options && options.pointToLayer,
 
8677             _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
 
8678             latlng, latlngs, i, len;
 
8680         if (!coords && !geometry) {
 
8684         switch (geometry.type) {
 
8686                 latlng = _coordsToLatLng(coords);
 
8687                 return pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng);
 
8690                 for (i = 0, len = coords.length; i < len; i++) {
 
8691                         latlng = _coordsToLatLng(coords[i]);
 
8692                         layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng));
 
8694                 return new FeatureGroup(layers);
 
8697         case 'MultiLineString':
 
8698                 latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
 
8699                 return new Polyline(latlngs, options);
 
8702         case 'MultiPolygon':
 
8703                 latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
 
8704                 return new Polygon(latlngs, options);
 
8706         case 'GeometryCollection':
 
8707                 for (i = 0, len = geometry.geometries.length; i < len; i++) {
 
8708                         var layer = geometryToLayer({
 
8709                                 geometry: geometry.geometries[i],
 
8711                                 properties: geojson.properties
 
8718                 return new FeatureGroup(layers);
 
8721                 throw new Error('Invalid GeoJSON object.');
 
8725 // @function coordsToLatLng(coords: Array): LatLng
 
8726 // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
 
8727 // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
 
8728 function coordsToLatLng(coords) {
 
8729         return new LatLng(coords[1], coords[0], coords[2]);
 
8732 // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
 
8733 // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
 
8734 // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
 
8735 // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
 
8736 function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
 
8739         for (var i = 0, len = coords.length, latlng; i < len; i++) {
 
8740                 latlng = levelsDeep ?
 
8741                         coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
 
8742                         (_coordsToLatLng || coordsToLatLng)(coords[i]);
 
8744                 latlngs.push(latlng);
 
8750 // @function latLngToCoords(latlng: LatLng, precision?: Number): Array
 
8751 // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
 
8752 function latLngToCoords(latlng, precision) {
 
8753         precision = typeof precision === 'number' ? precision : 6;
 
8754         return latlng.alt !== undefined ?
 
8755                 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
 
8756                 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
 
8759 // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
 
8760 // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
 
8761 // `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.
 
8762 function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
 
8765         for (var i = 0, len = latlngs.length; i < len; i++) {
 
8766                 coords.push(levelsDeep ?
 
8767                         latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) :
 
8768                         latLngToCoords(latlngs[i], precision));
 
8771         if (!levelsDeep && closed) {
 
8772                 coords.push(coords[0]);
 
8778 function getFeature(layer, newGeometry) {
 
8779         return layer.feature ?
 
8780                 extend({}, layer.feature, {geometry: newGeometry}) :
 
8781                 asFeature(newGeometry);
 
8784 // @function asFeature(geojson: Object): Object
 
8785 // Normalize GeoJSON geometries/features into GeoJSON features.
 
8786 function asFeature(geojson) {
 
8787         if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
 
8798 var PointToGeoJSON = {
 
8799         toGeoJSON: function (precision) {
 
8800                 return getFeature(this, {
 
8802                         coordinates: latLngToCoords(this.getLatLng(), precision)
 
8807 // @namespace Marker
 
8808 // @method toGeoJSON(): Object
 
8809 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
 
8810 Marker.include(PointToGeoJSON);
 
8812 // @namespace CircleMarker
 
8813 // @method toGeoJSON(): Object
 
8814 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
 
8815 Circle.include(PointToGeoJSON);
 
8816 CircleMarker.include(PointToGeoJSON);
 
8819 // @namespace Polyline
 
8820 // @method toGeoJSON(): Object
 
8821 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
 
8823         toGeoJSON: function (precision) {
 
8824                 var multi = !isFlat(this._latlngs);
 
8826                 var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
 
8828                 return getFeature(this, {
 
8829                         type: (multi ? 'Multi' : '') + 'LineString',
 
8835 // @namespace Polygon
 
8836 // @method toGeoJSON(): Object
 
8837 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
 
8839         toGeoJSON: function (precision) {
 
8840                 var holes = !isFlat(this._latlngs),
 
8841                     multi = holes && !isFlat(this._latlngs[0]);
 
8843                 var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
 
8849                 return getFeature(this, {
 
8850                         type: (multi ? 'Multi' : '') + 'Polygon',
 
8857 // @namespace LayerGroup
 
8858 LayerGroup.include({
 
8859         toMultiPoint: function (precision) {
 
8862                 this.eachLayer(function (layer) {
 
8863                         coords.push(layer.toGeoJSON(precision).geometry.coordinates);
 
8866                 return getFeature(this, {
 
8872         // @method toGeoJSON(): Object
 
8873         // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
 
8874         toGeoJSON: function (precision) {
 
8876                 var type = this.feature && this.feature.geometry && this.feature.geometry.type;
 
8878                 if (type === 'MultiPoint') {
 
8879                         return this.toMultiPoint(precision);
 
8882                 var isGeometryCollection = type === 'GeometryCollection',
 
8885                 this.eachLayer(function (layer) {
 
8886                         if (layer.toGeoJSON) {
 
8887                                 var json = layer.toGeoJSON(precision);
 
8888                                 if (isGeometryCollection) {
 
8889                                         jsons.push(json.geometry);
 
8891                                         var feature = asFeature(json);
 
8892                                         // Squash nested feature collections
 
8893                                         if (feature.type === 'FeatureCollection') {
 
8894                                                 jsons.push.apply(jsons, feature.features);
 
8896                                                 jsons.push(feature);
 
8902                 if (isGeometryCollection) {
 
8903                         return getFeature(this, {
 
8905                                 type: 'GeometryCollection'
 
8910                         type: 'FeatureCollection',
 
8916 // @namespace GeoJSON
 
8917 // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
 
8918 // Creates a GeoJSON layer. Optionally accepts an object in
 
8919 // [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map
 
8920 // (you can alternatively add it later with `addData` method) and an `options` object.
 
8921 function geoJSON(geojson, options) {
 
8922         return new GeoJSON(geojson, options);
 
8925 // Backward compatibility.
 
8926 var geoJson = geoJSON;
 
8929  * @class ImageOverlay
 
8930  * @aka L.ImageOverlay
 
8931  * @inherits Interactive layer
 
8933  * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
 
8938  * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
 
8939  *      imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
 
8940  * L.imageOverlay(imageUrl, imageBounds).addTo(map);
 
8944 var ImageOverlay = Layer.extend({
 
8947         // @aka ImageOverlay options
 
8949                 // @option opacity: Number = 1.0
 
8950                 // The opacity of the image overlay.
 
8953                 // @option alt: String = ''
 
8954                 // Text for the `alt` attribute of the image (useful for accessibility).
 
8957                 // @option interactive: Boolean = false
 
8958                 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
 
8961                 // @option crossOrigin: Boolean|String = false
 
8962                 // Whether the crossOrigin attribute will be added to the image.
 
8963                 // If a String is provided, the image will have its crossOrigin attribute set to the String provided. This is needed if you want to access image pixel data.
 
8964                 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
 
8967                 // @option errorOverlayUrl: String = ''
 
8968                 // URL to the overlay image to show in place of the overlay that failed to load.
 
8969                 errorOverlayUrl: '',
 
8971                 // @option zIndex: Number = 1
 
8972                 // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer.
 
8975                 // @option className: String = ''
 
8976                 // A custom class name to assign to the image. Empty by default.
 
8980         initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
 
8982                 this._bounds = toLatLngBounds(bounds);
 
8984                 setOptions(this, options);
 
8987         onAdd: function () {
 
8991                         if (this.options.opacity < 1) {
 
8992                                 this._updateOpacity();
 
8996                 if (this.options.interactive) {
 
8997                         addClass(this._image, 'leaflet-interactive');
 
8998                         this.addInteractiveTarget(this._image);
 
9001                 this.getPane().appendChild(this._image);
 
9005         onRemove: function () {
 
9006                 remove(this._image);
 
9007                 if (this.options.interactive) {
 
9008                         this.removeInteractiveTarget(this._image);
 
9012         // @method setOpacity(opacity: Number): this
 
9013         // Sets the opacity of the overlay.
 
9014         setOpacity: function (opacity) {
 
9015                 this.options.opacity = opacity;
 
9018                         this._updateOpacity();
 
9023         setStyle: function (styleOpts) {
 
9024                 if (styleOpts.opacity) {
 
9025                         this.setOpacity(styleOpts.opacity);
 
9030         // @method bringToFront(): this
 
9031         // Brings the layer to the top of all overlays.
 
9032         bringToFront: function () {
 
9034                         toFront(this._image);
 
9039         // @method bringToBack(): this
 
9040         // Brings the layer to the bottom of all overlays.
 
9041         bringToBack: function () {
 
9043                         toBack(this._image);
 
9048         // @method setUrl(url: String): this
 
9049         // Changes the URL of the image.
 
9050         setUrl: function (url) {
 
9054                         this._image.src = url;
 
9059         // @method setBounds(bounds: LatLngBounds): this
 
9060         // Update the bounds that this ImageOverlay covers
 
9061         setBounds: function (bounds) {
 
9062                 this._bounds = toLatLngBounds(bounds);
 
9070         getEvents: function () {
 
9073                         viewreset: this._reset
 
9076                 if (this._zoomAnimated) {
 
9077                         events.zoomanim = this._animateZoom;
 
9083         // @method setZIndex(value: Number): this
 
9084         // Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
 
9085         setZIndex: function (value) {
 
9086                 this.options.zIndex = value;
 
9087                 this._updateZIndex();
 
9091         // @method getBounds(): LatLngBounds
 
9092         // Get the bounds that this ImageOverlay covers
 
9093         getBounds: function () {
 
9094                 return this._bounds;
 
9097         // @method getElement(): HTMLElement
 
9098         // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
 
9099         // used by this overlay.
 
9100         getElement: function () {
 
9104         _initImage: function () {
 
9105                 var wasElementSupplied = this._url.tagName === 'IMG';
 
9106                 var img = this._image = wasElementSupplied ? this._url : create$1('img');
 
9108                 addClass(img, 'leaflet-image-layer');
 
9109                 if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); }
 
9110                 if (this.options.className) { addClass(img, this.options.className); }
 
9112                 img.onselectstart = falseFn;
 
9113                 img.onmousemove = falseFn;
 
9115                 // @event load: Event
 
9116                 // Fired when the ImageOverlay layer has loaded its image
 
9117                 img.onload = bind(this.fire, this, 'load');
 
9118                 img.onerror = bind(this._overlayOnError, this, 'error');
 
9120                 if (this.options.crossOrigin || this.options.crossOrigin === '') {
 
9121                         img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
 
9124                 if (this.options.zIndex) {
 
9125                         this._updateZIndex();
 
9128                 if (wasElementSupplied) {
 
9129                         this._url = img.src;
 
9133                 img.src = this._url;
 
9134                 img.alt = this.options.alt;
 
9137         _animateZoom: function (e) {
 
9138                 var scale = this._map.getZoomScale(e.zoom),
 
9139                     offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
 
9141                 setTransform(this._image, offset, scale);
 
9144         _reset: function () {
 
9145                 var image = this._image,
 
9146                     bounds = new Bounds(
 
9147                         this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
 
9148                         this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
 
9149                     size = bounds.getSize();
 
9151                 setPosition(image, bounds.min);
 
9153                 image.style.width  = size.x + 'px';
 
9154                 image.style.height = size.y + 'px';
 
9157         _updateOpacity: function () {
 
9158                 setOpacity(this._image, this.options.opacity);
 
9161         _updateZIndex: function () {
 
9162                 if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
 
9163                         this._image.style.zIndex = this.options.zIndex;
 
9167         _overlayOnError: function () {
 
9168                 // @event error: Event
 
9169                 // Fired when the ImageOverlay layer fails to load its image
 
9172                 var errorUrl = this.options.errorOverlayUrl;
 
9173                 if (errorUrl && this._url !== errorUrl) {
 
9174                         this._url = errorUrl;
 
9175                         this._image.src = errorUrl;
 
9180 // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
 
9181 // Instantiates an image overlay object given the URL of the image and the
 
9182 // geographical bounds it is tied to.
 
9183 var imageOverlay = function (url, bounds, options) {
 
9184         return new ImageOverlay(url, bounds, options);
 
9188  * @class VideoOverlay
 
9189  * @aka L.VideoOverlay
 
9190  * @inherits ImageOverlay
 
9192  * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
 
9194  * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
 
9200  * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
 
9201  *      videoBounds = [[ 32, -130], [ 13, -100]];
 
9202  * L.videoOverlay(videoUrl, videoBounds ).addTo(map);
 
9206 var VideoOverlay = ImageOverlay.extend({
 
9209         // @aka VideoOverlay options
 
9211                 // @option autoplay: Boolean = true
 
9212                 // Whether the video starts playing automatically when loaded.
 
9215                 // @option loop: Boolean = true
 
9216                 // Whether the video will loop back to the beginning when played.
 
9220         _initImage: function () {
 
9221                 var wasElementSupplied = this._url.tagName === 'VIDEO';
 
9222                 var vid = this._image = wasElementSupplied ? this._url : create$1('video');
 
9224                 addClass(vid, 'leaflet-image-layer');
 
9225                 if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); }
 
9227                 vid.onselectstart = falseFn;
 
9228                 vid.onmousemove = falseFn;
 
9230                 // @event load: Event
 
9231                 // Fired when the video has finished loading the first frame
 
9232                 vid.onloadeddata = bind(this.fire, this, 'load');
 
9234                 if (wasElementSupplied) {
 
9235                         var sourceElements = vid.getElementsByTagName('source');
 
9237                         for (var j = 0; j < sourceElements.length; j++) {
 
9238                                 sources.push(sourceElements[j].src);
 
9241                         this._url = (sourceElements.length > 0) ? sources : [vid.src];
 
9245                 if (!isArray(this._url)) { this._url = [this._url]; }
 
9247                 vid.autoplay = !!this.options.autoplay;
 
9248                 vid.loop = !!this.options.loop;
 
9249                 for (var i = 0; i < this._url.length; i++) {
 
9250                         var source = create$1('source');
 
9251                         source.src = this._url[i];
 
9252                         vid.appendChild(source);
 
9256         // @method getElement(): HTMLVideoElement
 
9257         // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
 
9258         // used by this overlay.
 
9262 // @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options)
 
9263 // Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the
 
9264 // geographical bounds it is tied to.
 
9266 function videoOverlay(video, bounds, options) {
 
9267         return new VideoOverlay(video, bounds, options);
 
9274  * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
 
9277 // @namespace DivOverlay
 
9278 var DivOverlay = Layer.extend({
 
9281         // @aka DivOverlay options
 
9283                 // @option offset: Point = Point(0, 7)
 
9284                 // The offset of the popup position. Useful to control the anchor
 
9285                 // of the popup when opening it on some overlays.
 
9288                 // @option className: String = ''
 
9289                 // A custom CSS class name to assign to the popup.
 
9292                 // @option pane: String = 'popupPane'
 
9293                 // `Map pane` where the popup will be added.
 
9297         initialize: function (options, source) {
 
9298                 setOptions(this, options);
 
9300                 this._source = source;
 
9303         onAdd: function (map) {
 
9304                 this._zoomAnimated = map._zoomAnimated;
 
9306                 if (!this._container) {
 
9310                 if (map._fadeAnimated) {
 
9311                         setOpacity(this._container, 0);
 
9314                 clearTimeout(this._removeTimeout);
 
9315                 this.getPane().appendChild(this._container);
 
9318                 if (map._fadeAnimated) {
 
9319                         setOpacity(this._container, 1);
 
9322                 this.bringToFront();
 
9325         onRemove: function (map) {
 
9326                 if (map._fadeAnimated) {
 
9327                         setOpacity(this._container, 0);
 
9328                         this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200);
 
9330                         remove(this._container);
 
9335         // @method getLatLng: LatLng
 
9336         // Returns the geographical point of popup.
 
9337         getLatLng: function () {
 
9338                 return this._latlng;
 
9341         // @method setLatLng(latlng: LatLng): this
 
9342         // Sets the geographical point where the popup will open.
 
9343         setLatLng: function (latlng) {
 
9344                 this._latlng = toLatLng(latlng);
 
9346                         this._updatePosition();
 
9352         // @method getContent: String|HTMLElement
 
9353         // Returns the content of the popup.
 
9354         getContent: function () {
 
9355                 return this._content;
 
9358         // @method setContent(htmlContent: String|HTMLElement|Function): this
 
9359         // 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.
 
9360         setContent: function (content) {
 
9361                 this._content = content;
 
9366         // @method getElement: String|HTMLElement
 
9367         // Alias for [getContent()](#popup-getcontent)
 
9368         getElement: function () {
 
9369                 return this._container;
 
9372         // @method update: null
 
9373         // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
 
9374         update: function () {
 
9375                 if (!this._map) { return; }
 
9377                 this._container.style.visibility = 'hidden';
 
9379                 this._updateContent();
 
9380                 this._updateLayout();
 
9381                 this._updatePosition();
 
9383                 this._container.style.visibility = '';
 
9388         getEvents: function () {
 
9390                         zoom: this._updatePosition,
 
9391                         viewreset: this._updatePosition
 
9394                 if (this._zoomAnimated) {
 
9395                         events.zoomanim = this._animateZoom;
 
9400         // @method isOpen: Boolean
 
9401         // Returns `true` when the popup is visible on the map.
 
9402         isOpen: function () {
 
9403                 return !!this._map && this._map.hasLayer(this);
 
9406         // @method bringToFront: this
 
9407         // Brings this popup in front of other popups (in the same map pane).
 
9408         bringToFront: function () {
 
9410                         toFront(this._container);
 
9415         // @method bringToBack: this
 
9416         // Brings this popup to the back of other popups (in the same map pane).
 
9417         bringToBack: function () {
 
9419                         toBack(this._container);
 
9424         _updateContent: function () {
 
9425                 if (!this._content) { return; }
 
9427                 var node = this._contentNode;
 
9428                 var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
 
9430                 if (typeof content === 'string') {
 
9431                         node.innerHTML = content;
 
9433                         while (node.hasChildNodes()) {
 
9434                                 node.removeChild(node.firstChild);
 
9436                         node.appendChild(content);
 
9438                 this.fire('contentupdate');
 
9441         _updatePosition: function () {
 
9442                 if (!this._map) { return; }
 
9444                 var pos = this._map.latLngToLayerPoint(this._latlng),
 
9445                     offset = toPoint(this.options.offset),
 
9446                     anchor = this._getAnchor();
 
9448                 if (this._zoomAnimated) {
 
9449                         setPosition(this._container, pos.add(anchor));
 
9451                         offset = offset.add(pos).add(anchor);
 
9454                 var bottom = this._containerBottom = -offset.y,
 
9455                     left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
 
9457                 // bottom position the popup in case the height of the popup changes (images loading etc)
 
9458                 this._container.style.bottom = bottom + 'px';
 
9459                 this._container.style.left = left + 'px';
 
9462         _getAnchor: function () {
 
9470  * @inherits DivOverlay
 
9472  * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
 
9473  * open popups while making sure that only one popup is open at one time
 
9474  * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
 
9478  * If you want to just bind a popup to marker click and then open it, it's really easy:
 
9481  * marker.bindPopup(popupContent).openPopup();
 
9483  * Path overlays like polylines also have a `bindPopup` method.
 
9484  * Here's a more complicated way to open a popup on a map:
 
9487  * var popup = L.popup()
 
9488  *      .setLatLng(latlng)
 
9489  *      .setContent('<p>Hello world!<br />This is a nice popup.</p>')
 
9496 var Popup = DivOverlay.extend({
 
9499         // @aka Popup options
 
9501                 // @option maxWidth: Number = 300
 
9502                 // Max width of the popup, in pixels.
 
9505                 // @option minWidth: Number = 50
 
9506                 // Min width of the popup, in pixels.
 
9509                 // @option maxHeight: Number = null
 
9510                 // If set, creates a scrollable container of the given height
 
9511                 // inside a popup if its content exceeds it.
 
9514                 // @option autoPan: Boolean = true
 
9515                 // Set it to `false` if you don't want the map to do panning animation
 
9516                 // to fit the opened popup.
 
9519                 // @option autoPanPaddingTopLeft: Point = null
 
9520                 // The margin between the popup and the top left corner of the map
 
9521                 // view after autopanning was performed.
 
9522                 autoPanPaddingTopLeft: null,
 
9524                 // @option autoPanPaddingBottomRight: Point = null
 
9525                 // The margin between the popup and the bottom right corner of the map
 
9526                 // view after autopanning was performed.
 
9527                 autoPanPaddingBottomRight: null,
 
9529                 // @option autoPanPadding: Point = Point(5, 5)
 
9530                 // Equivalent of setting both top left and bottom right autopan padding to the same value.
 
9531                 autoPanPadding: [5, 5],
 
9533                 // @option keepInView: Boolean = false
 
9534                 // Set it to `true` if you want to prevent users from panning the popup
 
9535                 // off of the screen while it is open.
 
9538                 // @option closeButton: Boolean = true
 
9539                 // Controls the presence of a close button in the popup.
 
9542                 // @option autoClose: Boolean = true
 
9543                 // Set it to `false` if you want to override the default behavior of
 
9544                 // the popup closing when another popup is opened.
 
9547                 // @option closeOnEscapeKey: Boolean = true
 
9548                 // Set it to `false` if you want to override the default behavior of
 
9549                 // the ESC key for closing of the popup.
 
9550                 closeOnEscapeKey: true,
 
9552                 // @option closeOnClick: Boolean = *
 
9553                 // Set it if you want to override the default behavior of the popup closing when user clicks
 
9554                 // on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
 
9556                 // @option className: String = ''
 
9557                 // A custom CSS class name to assign to the popup.
 
9562         // @method openOn(map: Map): this
 
9563         // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
 
9564         openOn: function (map) {
 
9565                 map.openPopup(this);
 
9569         onAdd: function (map) {
 
9570                 DivOverlay.prototype.onAdd.call(this, map);
 
9573                 // @section Popup events
 
9574                 // @event popupopen: PopupEvent
 
9575                 // Fired when a popup is opened in the map
 
9576                 map.fire('popupopen', {popup: this});
 
9580                         // @section Popup events
 
9581                         // @event popupopen: PopupEvent
 
9582                         // Fired when a popup bound to this layer is opened
 
9583                         this._source.fire('popupopen', {popup: this}, true);
 
9584                         // For non-path layers, we toggle the popup when clicking
 
9585                         // again the layer, so prevent the map to reopen it.
 
9586                         if (!(this._source instanceof Path)) {
 
9587                                 this._source.on('preclick', stopPropagation);
 
9592         onRemove: function (map) {
 
9593                 DivOverlay.prototype.onRemove.call(this, map);
 
9596                 // @section Popup events
 
9597                 // @event popupclose: PopupEvent
 
9598                 // Fired when a popup in the map is closed
 
9599                 map.fire('popupclose', {popup: this});
 
9603                         // @section Popup events
 
9604                         // @event popupclose: PopupEvent
 
9605                         // Fired when a popup bound to this layer is closed
 
9606                         this._source.fire('popupclose', {popup: this}, true);
 
9607                         if (!(this._source instanceof Path)) {
 
9608                                 this._source.off('preclick', stopPropagation);
 
9613         getEvents: function () {
 
9614                 var events = DivOverlay.prototype.getEvents.call(this);
 
9616                 if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
 
9617                         events.preclick = this._close;
 
9620                 if (this.options.keepInView) {
 
9621                         events.moveend = this._adjustPan;
 
9627         _close: function () {
 
9629                         this._map.closePopup(this);
 
9633         _initLayout: function () {
 
9634                 var prefix = 'leaflet-popup',
 
9635                     container = this._container = create$1('div',
 
9636                         prefix + ' ' + (this.options.className || '') +
 
9637                         ' leaflet-zoom-animated');
 
9639                 var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
 
9640                 this._contentNode = create$1('div', prefix + '-content', wrapper);
 
9642                 disableClickPropagation(wrapper);
 
9643                 disableScrollPropagation(this._contentNode);
 
9644                 on(wrapper, 'contextmenu', stopPropagation);
 
9646                 this._tipContainer = create$1('div', prefix + '-tip-container', container);
 
9647                 this._tip = create$1('div', prefix + '-tip', this._tipContainer);
 
9649                 if (this.options.closeButton) {
 
9650                         var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
 
9651                         closeButton.href = '#close';
 
9652                         closeButton.innerHTML = '×';
 
9654                         on(closeButton, 'click', this._onCloseButtonClick, this);
 
9658         _updateLayout: function () {
 
9659                 var container = this._contentNode,
 
9660                     style = container.style;
 
9663                 style.whiteSpace = 'nowrap';
 
9665                 var width = container.offsetWidth;
 
9666                 width = Math.min(width, this.options.maxWidth);
 
9667                 width = Math.max(width, this.options.minWidth);
 
9669                 style.width = (width + 1) + 'px';
 
9670                 style.whiteSpace = '';
 
9674                 var height = container.offsetHeight,
 
9675                     maxHeight = this.options.maxHeight,
 
9676                     scrolledClass = 'leaflet-popup-scrolled';
 
9678                 if (maxHeight && height > maxHeight) {
 
9679                         style.height = maxHeight + 'px';
 
9680                         addClass(container, scrolledClass);
 
9682                         removeClass(container, scrolledClass);
 
9685                 this._containerWidth = this._container.offsetWidth;
 
9688         _animateZoom: function (e) {
 
9689                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
 
9690                     anchor = this._getAnchor();
 
9691                 setPosition(this._container, pos.add(anchor));
 
9694         _adjustPan: function () {
 
9695                 if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; }
 
9697                 var map = this._map,
 
9698                     marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
 
9699                     containerHeight = this._container.offsetHeight + marginBottom,
 
9700                     containerWidth = this._containerWidth,
 
9701                     layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
 
9703                 layerPos._add(getPosition(this._container));
 
9705                 var containerPos = map.layerPointToContainerPoint(layerPos),
 
9706                     padding = toPoint(this.options.autoPanPadding),
 
9707                     paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
 
9708                     paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
 
9709                     size = map.getSize(),
 
9713                 if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
 
9714                         dx = containerPos.x + containerWidth - size.x + paddingBR.x;
 
9716                 if (containerPos.x - dx - paddingTL.x < 0) { // left
 
9717                         dx = containerPos.x - paddingTL.x;
 
9719                 if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
 
9720                         dy = containerPos.y + containerHeight - size.y + paddingBR.y;
 
9722                 if (containerPos.y - dy - paddingTL.y < 0) { // top
 
9723                         dy = containerPos.y - paddingTL.y;
 
9727                 // @section Popup events
 
9728                 // @event autopanstart: Event
 
9729                 // Fired when the map starts autopanning when opening a popup.
 
9732                             .fire('autopanstart')
 
9737         _onCloseButtonClick: function (e) {
 
9742         _getAnchor: function () {
 
9743                 // Where should we anchor the popup on the source layer?
 
9744                 return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
 
9750 // @factory L.popup(options?: Popup options, source?: Layer)
 
9751 // 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.
 
9752 var popup = function (options, source) {
 
9753         return new Popup(options, source);
 
9758  * @section Interaction Options
 
9759  * @option closePopupOnClick: Boolean = true
 
9760  * Set it to `false` if you don't want popups to close when user clicks the map.
 
9763         closePopupOnClick: true
 
9768 // @section Methods for Layers and Controls
 
9770         // @method openPopup(popup: Popup): this
 
9771         // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
 
9773         // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
 
9774         // Creates a popup with the specified content and options and opens it in the given point on a map.
 
9775         openPopup: function (popup, latlng, options) {
 
9776                 if (!(popup instanceof Popup)) {
 
9777                         popup = new Popup(options).setContent(popup);
 
9781                         popup.setLatLng(latlng);
 
9784                 if (this.hasLayer(popup)) {
 
9788                 if (this._popup && this._popup.options.autoClose) {
 
9792                 this._popup = popup;
 
9793                 return this.addLayer(popup);
 
9796         // @method closePopup(popup?: Popup): this
 
9797         // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
 
9798         closePopup: function (popup) {
 
9799                 if (!popup || popup === this._popup) {
 
9800                         popup = this._popup;
 
9804                         this.removeLayer(popup);
 
9812  * @section Popup methods example
 
9814  * All layers share a set of methods convenient for binding popups to it.
 
9817  * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
 
9818  * layer.openPopup();
 
9819  * layer.closePopup();
 
9822  * 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.
 
9825 // @section Popup methods
 
9828         // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
 
9829         // Binds a popup to the layer with the passed `content` and sets up the
 
9830         // necessary event listeners. If a `Function` is passed it will receive
 
9831         // the layer as the first argument and should return a `String` or `HTMLElement`.
 
9832         bindPopup: function (content, options) {
 
9834                 if (content instanceof Popup) {
 
9835                         setOptions(content, options);
 
9836                         this._popup = content;
 
9837                         content._source = this;
 
9839                         if (!this._popup || options) {
 
9840                                 this._popup = new Popup(options, this);
 
9842                         this._popup.setContent(content);
 
9845                 if (!this._popupHandlersAdded) {
 
9847                                 click: this._openPopup,
 
9848                                 keypress: this._onKeyPress,
 
9849                                 remove: this.closePopup,
 
9850                                 move: this._movePopup
 
9852                         this._popupHandlersAdded = true;
 
9858         // @method unbindPopup(): this
 
9859         // Removes the popup previously bound with `bindPopup`.
 
9860         unbindPopup: function () {
 
9863                                 click: this._openPopup,
 
9864                                 keypress: this._onKeyPress,
 
9865                                 remove: this.closePopup,
 
9866                                 move: this._movePopup
 
9868                         this._popupHandlersAdded = false;
 
9874         // @method openPopup(latlng?: LatLng): this
 
9875         // Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
 
9876         openPopup: function (layer, latlng) {
 
9877                 if (!(layer instanceof Layer)) {
 
9882                 if (layer instanceof FeatureGroup) {
 
9883                         for (var id in this._layers) {
 
9884                                 layer = this._layers[id];
 
9890                         latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
 
9893                 if (this._popup && this._map) {
 
9894                         // set popup source to this layer
 
9895                         this._popup._source = layer;
 
9897                         // update the popup (content, layout, ect...)
 
9898                         this._popup.update();
 
9900                         // open the popup on the map
 
9901                         this._map.openPopup(this._popup, latlng);
 
9907         // @method closePopup(): this
 
9908         // Closes the popup bound to this layer if it is open.
 
9909         closePopup: function () {
 
9911                         this._popup._close();
 
9916         // @method togglePopup(): this
 
9917         // Opens or closes the popup bound to this layer depending on its current state.
 
9918         togglePopup: function (target) {
 
9920                         if (this._popup._map) {
 
9923                                 this.openPopup(target);
 
9929         // @method isPopupOpen(): boolean
 
9930         // Returns `true` if the popup bound to this layer is currently open.
 
9931         isPopupOpen: function () {
 
9932                 return (this._popup ? this._popup.isOpen() : false);
 
9935         // @method setPopupContent(content: String|HTMLElement|Popup): this
 
9936         // Sets the content of the popup bound to this layer.
 
9937         setPopupContent: function (content) {
 
9939                         this._popup.setContent(content);
 
9944         // @method getPopup(): Popup
 
9945         // Returns the popup bound to this layer.
 
9946         getPopup: function () {
 
9950         _openPopup: function (e) {
 
9951                 var layer = e.layer || e.target;
 
9961                 // prevent map click
 
9964                 // if this inherits from Path its a vector and we can just
 
9965                 // open the popup at the new location
 
9966                 if (layer instanceof Path) {
 
9967                         this.openPopup(e.layer || e.target, e.latlng);
 
9971                 // otherwise treat it like a marker and figure out
 
9972                 // if we should toggle it open/closed
 
9973                 if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
 
9976                         this.openPopup(layer, e.latlng);
 
9980         _movePopup: function (e) {
 
9981                 this._popup.setLatLng(e.latlng);
 
9984         _onKeyPress: function (e) {
 
9985                 if (e.originalEvent.keyCode === 13) {
 
9993  * @inherits DivOverlay
 
9995  * Used to display small texts on top of map layers.
 
10000  * marker.bindTooltip("my tooltip text").openTooltip();
 
10002  * Note about tooltip offset. Leaflet takes two options in consideration
 
10003  * for computing tooltip offsetting:
 
10004  * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
 
10005  *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
 
10006  *   move it to the bottom. Negatives will move to the left and top.
 
10007  * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
 
10008  *   should adapt this value if you use a custom icon.
 
10012 // @namespace Tooltip
 
10013 var Tooltip = DivOverlay.extend({
 
10016         // @aka Tooltip options
 
10018                 // @option pane: String = 'tooltipPane'
 
10019                 // `Map pane` where the tooltip will be added.
 
10020                 pane: 'tooltipPane',
 
10022                 // @option offset: Point = Point(0, 0)
 
10023                 // Optional offset of the tooltip position.
 
10026                 // @option direction: String = 'auto'
 
10027                 // Direction where to open the tooltip. Possible values are: `right`, `left`,
 
10028                 // `top`, `bottom`, `center`, `auto`.
 
10029                 // `auto` will dynamically switch between `right` and `left` according to the tooltip
 
10030                 // position on the map.
 
10033                 // @option permanent: Boolean = false
 
10034                 // Whether to open the tooltip permanently or only on mouseover.
 
10037                 // @option sticky: Boolean = false
 
10038                 // If true, the tooltip will follow the mouse instead of being fixed at the feature center.
 
10041                 // @option interactive: Boolean = false
 
10042                 // If true, the tooltip will listen to the feature events.
 
10043                 interactive: false,
 
10045                 // @option opacity: Number = 0.9
 
10046                 // Tooltip container opacity.
 
10050         onAdd: function (map) {
 
10051                 DivOverlay.prototype.onAdd.call(this, map);
 
10052                 this.setOpacity(this.options.opacity);
 
10055                 // @section Tooltip events
 
10056                 // @event tooltipopen: TooltipEvent
 
10057                 // Fired when a tooltip is opened in the map.
 
10058                 map.fire('tooltipopen', {tooltip: this});
 
10060                 if (this._source) {
 
10061                         // @namespace Layer
 
10062                         // @section Tooltip events
 
10063                         // @event tooltipopen: TooltipEvent
 
10064                         // Fired when a tooltip bound to this layer is opened.
 
10065                         this._source.fire('tooltipopen', {tooltip: this}, true);
 
10069         onRemove: function (map) {
 
10070                 DivOverlay.prototype.onRemove.call(this, map);
 
10073                 // @section Tooltip events
 
10074                 // @event tooltipclose: TooltipEvent
 
10075                 // Fired when a tooltip in the map is closed.
 
10076                 map.fire('tooltipclose', {tooltip: this});
 
10078                 if (this._source) {
 
10079                         // @namespace Layer
 
10080                         // @section Tooltip events
 
10081                         // @event tooltipclose: TooltipEvent
 
10082                         // Fired when a tooltip bound to this layer is closed.
 
10083                         this._source.fire('tooltipclose', {tooltip: this}, true);
 
10087         getEvents: function () {
 
10088                 var events = DivOverlay.prototype.getEvents.call(this);
 
10090                 if (touch && !this.options.permanent) {
 
10091                         events.preclick = this._close;
 
10097         _close: function () {
 
10099                         this._map.closeTooltip(this);
 
10103         _initLayout: function () {
 
10104                 var prefix = 'leaflet-tooltip',
 
10105                     className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
 
10107                 this._contentNode = this._container = create$1('div', className);
 
10110         _updateLayout: function () {},
 
10112         _adjustPan: function () {},
 
10114         _setPosition: function (pos) {
 
10115                 var map = this._map,
 
10116                     container = this._container,
 
10117                     centerPoint = map.latLngToContainerPoint(map.getCenter()),
 
10118                     tooltipPoint = map.layerPointToContainerPoint(pos),
 
10119                     direction = this.options.direction,
 
10120                     tooltipWidth = container.offsetWidth,
 
10121                     tooltipHeight = container.offsetHeight,
 
10122                     offset = toPoint(this.options.offset),
 
10123                     anchor = this._getAnchor();
 
10125                 if (direction === 'top') {
 
10126                         pos = pos.add(toPoint(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true));
 
10127                 } else if (direction === 'bottom') {
 
10128                         pos = pos.subtract(toPoint(tooltipWidth / 2 - offset.x, -offset.y, true));
 
10129                 } else if (direction === 'center') {
 
10130                         pos = pos.subtract(toPoint(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true));
 
10131                 } else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
 
10132                         direction = 'right';
 
10133                         pos = pos.add(toPoint(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true));
 
10135                         direction = 'left';
 
10136                         pos = pos.subtract(toPoint(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true));
 
10139                 removeClass(container, 'leaflet-tooltip-right');
 
10140                 removeClass(container, 'leaflet-tooltip-left');
 
10141                 removeClass(container, 'leaflet-tooltip-top');
 
10142                 removeClass(container, 'leaflet-tooltip-bottom');
 
10143                 addClass(container, 'leaflet-tooltip-' + direction);
 
10144                 setPosition(container, pos);
 
10147         _updatePosition: function () {
 
10148                 var pos = this._map.latLngToLayerPoint(this._latlng);
 
10149                 this._setPosition(pos);
 
10152         setOpacity: function (opacity) {
 
10153                 this.options.opacity = opacity;
 
10155                 if (this._container) {
 
10156                         setOpacity(this._container, opacity);
 
10160         _animateZoom: function (e) {
 
10161                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
 
10162                 this._setPosition(pos);
 
10165         _getAnchor: function () {
 
10166                 // Where should we anchor the tooltip on the source layer?
 
10167                 return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
 
10172 // @namespace Tooltip
 
10173 // @factory L.tooltip(options?: Tooltip options, source?: Layer)
 
10174 // 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.
 
10175 var tooltip = function (options, source) {
 
10176         return new Tooltip(options, source);
 
10180 // @section Methods for Layers and Controls
 
10183         // @method openTooltip(tooltip: Tooltip): this
 
10184         // Opens the specified tooltip.
 
10186         // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
 
10187         // Creates a tooltip with the specified content and options and open it.
 
10188         openTooltip: function (tooltip, latlng, options) {
 
10189                 if (!(tooltip instanceof Tooltip)) {
 
10190                         tooltip = new Tooltip(options).setContent(tooltip);
 
10194                         tooltip.setLatLng(latlng);
 
10197                 if (this.hasLayer(tooltip)) {
 
10201                 return this.addLayer(tooltip);
 
10204         // @method closeTooltip(tooltip?: Tooltip): this
 
10205         // Closes the tooltip given as parameter.
 
10206         closeTooltip: function (tooltip) {
 
10208                         this.removeLayer(tooltip);
 
10217  * @section Tooltip methods example
 
10219  * All layers share a set of methods convenient for binding tooltips to it.
 
10222  * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
 
10223  * layer.openTooltip();
 
10224  * layer.closeTooltip();
 
10228 // @section Tooltip methods
 
10231         // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
 
10232         // Binds a tooltip to the layer with the passed `content` and sets up the
 
10233         // necessary event listeners. If a `Function` is passed it will receive
 
10234         // the layer as the first argument and should return a `String` or `HTMLElement`.
 
10235         bindTooltip: function (content, options) {
 
10237                 if (content instanceof Tooltip) {
 
10238                         setOptions(content, options);
 
10239                         this._tooltip = content;
 
10240                         content._source = this;
 
10242                         if (!this._tooltip || options) {
 
10243                                 this._tooltip = new Tooltip(options, this);
 
10245                         this._tooltip.setContent(content);
 
10249                 this._initTooltipInteractions();
 
10251                 if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
 
10252                         this.openTooltip();
 
10258         // @method unbindTooltip(): this
 
10259         // Removes the tooltip previously bound with `bindTooltip`.
 
10260         unbindTooltip: function () {
 
10261                 if (this._tooltip) {
 
10262                         this._initTooltipInteractions(true);
 
10263                         this.closeTooltip();
 
10264                         this._tooltip = null;
 
10269         _initTooltipInteractions: function (remove$$1) {
 
10270                 if (!remove$$1 && this._tooltipHandlersAdded) { return; }
 
10271                 var onOff = remove$$1 ? 'off' : 'on',
 
10273                         remove: this.closeTooltip,
 
10274                         move: this._moveTooltip
 
10276                 if (!this._tooltip.options.permanent) {
 
10277                         events.mouseover = this._openTooltip;
 
10278                         events.mouseout = this.closeTooltip;
 
10279                         if (this._tooltip.options.sticky) {
 
10280                                 events.mousemove = this._moveTooltip;
 
10283                                 events.click = this._openTooltip;
 
10286                         events.add = this._openTooltip;
 
10288                 this[onOff](events);
 
10289                 this._tooltipHandlersAdded = !remove$$1;
 
10292         // @method openTooltip(latlng?: LatLng): this
 
10293         // Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
 
10294         openTooltip: function (layer, latlng) {
 
10295                 if (!(layer instanceof Layer)) {
 
10300                 if (layer instanceof FeatureGroup) {
 
10301                         for (var id in this._layers) {
 
10302                                 layer = this._layers[id];
 
10308                         latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
 
10311                 if (this._tooltip && this._map) {
 
10313                         // set tooltip source to this layer
 
10314                         this._tooltip._source = layer;
 
10316                         // update the tooltip (content, layout, ect...)
 
10317                         this._tooltip.update();
 
10319                         // open the tooltip on the map
 
10320                         this._map.openTooltip(this._tooltip, latlng);
 
10322                         // Tooltip container may not be defined if not permanent and never
 
10324                         if (this._tooltip.options.interactive && this._tooltip._container) {
 
10325                                 addClass(this._tooltip._container, 'leaflet-clickable');
 
10326                                 this.addInteractiveTarget(this._tooltip._container);
 
10333         // @method closeTooltip(): this
 
10334         // Closes the tooltip bound to this layer if it is open.
 
10335         closeTooltip: function () {
 
10336                 if (this._tooltip) {
 
10337                         this._tooltip._close();
 
10338                         if (this._tooltip.options.interactive && this._tooltip._container) {
 
10339                                 removeClass(this._tooltip._container, 'leaflet-clickable');
 
10340                                 this.removeInteractiveTarget(this._tooltip._container);
 
10346         // @method toggleTooltip(): this
 
10347         // Opens or closes the tooltip bound to this layer depending on its current state.
 
10348         toggleTooltip: function (target) {
 
10349                 if (this._tooltip) {
 
10350                         if (this._tooltip._map) {
 
10351                                 this.closeTooltip();
 
10353                                 this.openTooltip(target);
 
10359         // @method isTooltipOpen(): boolean
 
10360         // Returns `true` if the tooltip bound to this layer is currently open.
 
10361         isTooltipOpen: function () {
 
10362                 return this._tooltip.isOpen();
 
10365         // @method setTooltipContent(content: String|HTMLElement|Tooltip): this
 
10366         // Sets the content of the tooltip bound to this layer.
 
10367         setTooltipContent: function (content) {
 
10368                 if (this._tooltip) {
 
10369                         this._tooltip.setContent(content);
 
10374         // @method getTooltip(): Tooltip
 
10375         // Returns the tooltip bound to this layer.
 
10376         getTooltip: function () {
 
10377                 return this._tooltip;
 
10380         _openTooltip: function (e) {
 
10381                 var layer = e.layer || e.target;
 
10383                 if (!this._tooltip || !this._map) {
 
10386                 this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
 
10389         _moveTooltip: function (e) {
 
10390                 var latlng = e.latlng, containerPoint, layerPoint;
 
10391                 if (this._tooltip.options.sticky && e.originalEvent) {
 
10392                         containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
 
10393                         layerPoint = this._map.containerPointToLayerPoint(containerPoint);
 
10394                         latlng = this._map.layerPointToLatLng(layerPoint);
 
10396                 this._tooltip.setLatLng(latlng);
 
10405  * Represents a lightweight icon for markers that uses a simple `<div>`
 
10406  * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
 
10410  * var myIcon = L.divIcon({className: 'my-div-icon'});
 
10411  * // you can set .my-div-icon styles in CSS
 
10413  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
 
10416  * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
 
10419 var DivIcon = Icon.extend({
 
10422                 // @aka DivIcon options
 
10423                 iconSize: [12, 12], // also can be set through CSS
 
10425                 // iconAnchor: (Point),
 
10426                 // popupAnchor: (Point),
 
10428                 // @option html: String = ''
 
10429                 // Custom HTML code to put inside the div element, empty by default.
 
10432                 // @option bgPos: Point = [0, 0]
 
10433                 // Optional relative position of the background, in pixels
 
10436                 className: 'leaflet-div-icon'
 
10439         createIcon: function (oldIcon) {
 
10440                 var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
 
10441                     options = this.options;
 
10443                 div.innerHTML = options.html !== false ? options.html : '';
 
10445                 if (options.bgPos) {
 
10446                         var bgPos = toPoint(options.bgPos);
 
10447                         div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
 
10449                 this._setIconStyles(div, 'icon');
 
10454         createShadow: function () {
 
10459 // @factory L.divIcon(options: DivIcon options)
 
10460 // Creates a `DivIcon` instance with the given options.
 
10461 function divIcon(options) {
 
10462         return new DivIcon(options);
 
10465 Icon.Default = IconDefault;
 
10472  * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
 
10473  * 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.
 
10476  * @section Synchronous usage
 
10479  * 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.
 
10482  * var CanvasLayer = L.GridLayer.extend({
 
10483  *     createTile: function(coords){
 
10484  *         // create a <canvas> element for drawing
 
10485  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
10487  *         // setup tile width and height according to the options
 
10488  *         var size = this.getTileSize();
 
10489  *         tile.width = size.x;
 
10490  *         tile.height = size.y;
 
10492  *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
 
10493  *         var ctx = tile.getContext('2d');
 
10495  *         // return the tile so it can be rendered on screen
 
10501  * @section Asynchronous usage
 
10504  * 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.
 
10507  * var CanvasLayer = L.GridLayer.extend({
 
10508  *     createTile: function(coords, done){
 
10511  *         // create a <canvas> element for drawing
 
10512  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
10514  *         // setup tile width and height according to the options
 
10515  *         var size = this.getTileSize();
 
10516  *         tile.width = size.x;
 
10517  *         tile.height = size.y;
 
10519  *         // draw something asynchronously and pass the tile to the done() callback
 
10520  *         setTimeout(function() {
 
10521  *             done(error, tile);
 
10533 var GridLayer = Layer.extend({
 
10536         // @aka GridLayer options
 
10538                 // @option tileSize: Number|Point = 256
 
10539                 // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
 
10542                 // @option opacity: Number = 1.0
 
10543                 // Opacity of the tiles. Can be used in the `createTile()` function.
 
10546                 // @option updateWhenIdle: Boolean = (depends)
 
10547                 // Load new tiles only when panning ends.
 
10548                 // `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation.
 
10549                 // `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the
 
10550                 // [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers.
 
10551                 updateWhenIdle: mobile,
 
10553                 // @option updateWhenZooming: Boolean = true
 
10554                 // 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.
 
10555                 updateWhenZooming: true,
 
10557                 // @option updateInterval: Number = 200
 
10558                 // Tiles will not update more than once every `updateInterval` milliseconds when panning.
 
10559                 updateInterval: 200,
 
10561                 // @option zIndex: Number = 1
 
10562                 // The explicit zIndex of the tile layer.
 
10565                 // @option bounds: LatLngBounds = undefined
 
10566                 // If set, tiles will only be loaded inside the set `LatLngBounds`.
 
10569                 // @option minZoom: Number = 0
 
10570                 // The minimum zoom level down to which this layer will be displayed (inclusive).
 
10573                 // @option maxZoom: Number = undefined
 
10574                 // The maximum zoom level up to which this layer will be displayed (inclusive).
 
10575                 maxZoom: undefined,
 
10577                 // @option maxNativeZoom: Number = undefined
 
10578                 // Maximum zoom number the tile source has available. If it is specified,
 
10579                 // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
 
10580                 // from `maxNativeZoom` level and auto-scaled.
 
10581                 maxNativeZoom: undefined,
 
10583                 // @option minNativeZoom: Number = undefined
 
10584                 // Minimum zoom number the tile source has available. If it is specified,
 
10585                 // the tiles on all zoom levels lower than `minNativeZoom` will be loaded
 
10586                 // from `minNativeZoom` level and auto-scaled.
 
10587                 minNativeZoom: undefined,
 
10589                 // @option noWrap: Boolean = false
 
10590                 // Whether the layer is wrapped around the antimeridian. If `true`, the
 
10591                 // GridLayer will only be displayed once at low zoom levels. Has no
 
10592                 // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
 
10593                 // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
 
10594                 // tiles outside the CRS limits.
 
10597                 // @option pane: String = 'tilePane'
 
10598                 // `Map pane` where the grid layer will be added.
 
10601                 // @option className: String = ''
 
10602                 // A custom class name to assign to the tile layer. Empty by default.
 
10605                 // @option keepBuffer: Number = 2
 
10606                 // When panning the map, keep this many rows and columns of tiles before unloading them.
 
10610         initialize: function (options) {
 
10611                 setOptions(this, options);
 
10614         onAdd: function () {
 
10615                 this._initContainer();
 
10624         beforeAdd: function (map) {
 
10625                 map._addZoomLimit(this);
 
10628         onRemove: function (map) {
 
10629                 this._removeAllTiles();
 
10630                 remove(this._container);
 
10631                 map._removeZoomLimit(this);
 
10632                 this._container = null;
 
10633                 this._tileZoom = undefined;
 
10636         // @method bringToFront: this
 
10637         // Brings the tile layer to the top of all tile layers.
 
10638         bringToFront: function () {
 
10640                         toFront(this._container);
 
10641                         this._setAutoZIndex(Math.max);
 
10646         // @method bringToBack: this
 
10647         // Brings the tile layer to the bottom of all tile layers.
 
10648         bringToBack: function () {
 
10650                         toBack(this._container);
 
10651                         this._setAutoZIndex(Math.min);
 
10656         // @method getContainer: HTMLElement
 
10657         // Returns the HTML element that contains the tiles for this layer.
 
10658         getContainer: function () {
 
10659                 return this._container;
 
10662         // @method setOpacity(opacity: Number): this
 
10663         // Changes the [opacity](#gridlayer-opacity) of the grid layer.
 
10664         setOpacity: function (opacity) {
 
10665                 this.options.opacity = opacity;
 
10666                 this._updateOpacity();
 
10670         // @method setZIndex(zIndex: Number): this
 
10671         // Changes the [zIndex](#gridlayer-zindex) of the grid layer.
 
10672         setZIndex: function (zIndex) {
 
10673                 this.options.zIndex = zIndex;
 
10674                 this._updateZIndex();
 
10679         // @method isLoading: Boolean
 
10680         // Returns `true` if any tile in the grid layer has not finished loading.
 
10681         isLoading: function () {
 
10682                 return this._loading;
 
10685         // @method redraw: this
 
10686         // Causes the layer to clear all the tiles and request them again.
 
10687         redraw: function () {
 
10689                         this._removeAllTiles();
 
10695         getEvents: function () {
 
10697                         viewprereset: this._invalidateAll,
 
10698                         viewreset: this._resetView,
 
10699                         zoom: this._resetView,
 
10700                         moveend: this._onMoveEnd
 
10703                 if (!this.options.updateWhenIdle) {
 
10704                         // update tiles on move, but not more often than once per given interval
 
10705                         if (!this._onMove) {
 
10706                                 this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
 
10709                         events.move = this._onMove;
 
10712                 if (this._zoomAnimated) {
 
10713                         events.zoomanim = this._animateZoom;
 
10719         // @section Extension methods
 
10720         // Layers extending `GridLayer` shall reimplement the following method.
 
10721         // @method createTile(coords: Object, done?: Function): HTMLElement
 
10722         // Called only internally, must be overridden by classes extending `GridLayer`.
 
10723         // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
 
10724         // is specified, it must be called when the tile has finished loading and drawing.
 
10725         createTile: function () {
 
10726                 return document.createElement('div');
 
10730         // @method getTileSize: Point
 
10731         // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
 
10732         getTileSize: function () {
 
10733                 var s = this.options.tileSize;
 
10734                 return s instanceof Point ? s : new Point(s, s);
 
10737         _updateZIndex: function () {
 
10738                 if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
 
10739                         this._container.style.zIndex = this.options.zIndex;
 
10743         _setAutoZIndex: function (compare) {
 
10744                 // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
 
10746                 var layers = this.getPane().children,
 
10747                     edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
 
10749                 for (var i = 0, len = layers.length, zIndex; i < len; i++) {
 
10751                         zIndex = layers[i].style.zIndex;
 
10753                         if (layers[i] !== this._container && zIndex) {
 
10754                                 edgeZIndex = compare(edgeZIndex, +zIndex);
 
10758                 if (isFinite(edgeZIndex)) {
 
10759                         this.options.zIndex = edgeZIndex + compare(-1, 1);
 
10760                         this._updateZIndex();
 
10764         _updateOpacity: function () {
 
10765                 if (!this._map) { return; }
 
10767                 // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
 
10768                 if (ielt9) { return; }
 
10770                 setOpacity(this._container, this.options.opacity);
 
10772                 var now = +new Date(),
 
10776                 for (var key in this._tiles) {
 
10777                         var tile = this._tiles[key];
 
10778                         if (!tile.current || !tile.loaded) { continue; }
 
10780                         var fade = Math.min(1, (now - tile.loaded) / 200);
 
10782                         setOpacity(tile.el, fade);
 
10789                                         this._onOpaqueTile(tile);
 
10791                                 tile.active = true;
 
10795                 if (willPrune && !this._noPrune) { this._pruneTiles(); }
 
10798                         cancelAnimFrame(this._fadeFrame);
 
10799                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
 
10803         _onOpaqueTile: falseFn,
 
10805         _initContainer: function () {
 
10806                 if (this._container) { return; }
 
10808                 this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
 
10809                 this._updateZIndex();
 
10811                 if (this.options.opacity < 1) {
 
10812                         this._updateOpacity();
 
10815                 this.getPane().appendChild(this._container);
 
10818         _updateLevels: function () {
 
10820                 var zoom = this._tileZoom,
 
10821                     maxZoom = this.options.maxZoom;
 
10823                 if (zoom === undefined) { return undefined; }
 
10825                 for (var z in this._levels) {
 
10826                         if (this._levels[z].el.children.length || z === zoom) {
 
10827                                 this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
 
10828                                 this._onUpdateLevel(z);
 
10830                                 remove(this._levels[z].el);
 
10831                                 this._removeTilesAtZoom(z);
 
10832                                 this._onRemoveLevel(z);
 
10833                                 delete this._levels[z];
 
10837                 var level = this._levels[zoom],
 
10841                         level = this._levels[zoom] = {};
 
10843                         level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
 
10844                         level.el.style.zIndex = maxZoom;
 
10846                         level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
 
10849                         this._setZoomTransform(level, map.getCenter(), map.getZoom());
 
10851                         // force the browser to consider the newly added element for transition
 
10852                         falseFn(level.el.offsetWidth);
 
10854                         this._onCreateLevel(level);
 
10857                 this._level = level;
 
10862         _onUpdateLevel: falseFn,
 
10864         _onRemoveLevel: falseFn,
 
10866         _onCreateLevel: falseFn,
 
10868         _pruneTiles: function () {
 
10875                 var zoom = this._map.getZoom();
 
10876                 if (zoom > this.options.maxZoom ||
 
10877                         zoom < this.options.minZoom) {
 
10878                         this._removeAllTiles();
 
10882                 for (key in this._tiles) {
 
10883                         tile = this._tiles[key];
 
10884                         tile.retain = tile.current;
 
10887                 for (key in this._tiles) {
 
10888                         tile = this._tiles[key];
 
10889                         if (tile.current && !tile.active) {
 
10890                                 var coords = tile.coords;
 
10891                                 if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
 
10892                                         this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
 
10897                 for (key in this._tiles) {
 
10898                         if (!this._tiles[key].retain) {
 
10899                                 this._removeTile(key);
 
10904         _removeTilesAtZoom: function (zoom) {
 
10905                 for (var key in this._tiles) {
 
10906                         if (this._tiles[key].coords.z !== zoom) {
 
10909                         this._removeTile(key);
 
10913         _removeAllTiles: function () {
 
10914                 for (var key in this._tiles) {
 
10915                         this._removeTile(key);
 
10919         _invalidateAll: function () {
 
10920                 for (var z in this._levels) {
 
10921                         remove(this._levels[z].el);
 
10922                         this._onRemoveLevel(z);
 
10923                         delete this._levels[z];
 
10925                 this._removeAllTiles();
 
10927                 this._tileZoom = undefined;
 
10930         _retainParent: function (x, y, z, minZoom) {
 
10931                 var x2 = Math.floor(x / 2),
 
10932                     y2 = Math.floor(y / 2),
 
10934                     coords2 = new Point(+x2, +y2);
 
10937                 var key = this._tileCoordsToKey(coords2),
 
10938                     tile = this._tiles[key];
 
10940                 if (tile && tile.active) {
 
10941                         tile.retain = true;
 
10944                 } else if (tile && tile.loaded) {
 
10945                         tile.retain = true;
 
10948                 if (z2 > minZoom) {
 
10949                         return this._retainParent(x2, y2, z2, minZoom);
 
10955         _retainChildren: function (x, y, z, maxZoom) {
 
10957                 for (var i = 2 * x; i < 2 * x + 2; i++) {
 
10958                         for (var j = 2 * y; j < 2 * y + 2; j++) {
 
10960                                 var coords = new Point(i, j);
 
10963                                 var key = this._tileCoordsToKey(coords),
 
10964                                     tile = this._tiles[key];
 
10966                                 if (tile && tile.active) {
 
10967                                         tile.retain = true;
 
10970                                 } else if (tile && tile.loaded) {
 
10971                                         tile.retain = true;
 
10974                                 if (z + 1 < maxZoom) {
 
10975                                         this._retainChildren(i, j, z + 1, maxZoom);
 
10981         _resetView: function (e) {
 
10982                 var animating = e && (e.pinch || e.flyTo);
 
10983                 this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
 
10986         _animateZoom: function (e) {
 
10987                 this._setView(e.center, e.zoom, true, e.noUpdate);
 
10990         _clampZoom: function (zoom) {
 
10991                 var options = this.options;
 
10993                 if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
 
10994                         return options.minNativeZoom;
 
10997                 if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
 
10998                         return options.maxNativeZoom;
 
11004         _setView: function (center, zoom, noPrune, noUpdate) {
 
11005                 var tileZoom = this._clampZoom(Math.round(zoom));
 
11006                 if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
 
11007                     (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
 
11008                         tileZoom = undefined;
 
11011                 var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
 
11013                 if (!noUpdate || tileZoomChanged) {
 
11015                         this._tileZoom = tileZoom;
 
11017                         if (this._abortLoading) {
 
11018                                 this._abortLoading();
 
11021                         this._updateLevels();
 
11024                         if (tileZoom !== undefined) {
 
11025                                 this._update(center);
 
11029                                 this._pruneTiles();
 
11032                         // Flag to prevent _updateOpacity from pruning tiles during
 
11033                         // a zoom anim or a pinch gesture
 
11034                         this._noPrune = !!noPrune;
 
11037                 this._setZoomTransforms(center, zoom);
 
11040         _setZoomTransforms: function (center, zoom) {
 
11041                 for (var i in this._levels) {
 
11042                         this._setZoomTransform(this._levels[i], center, zoom);
 
11046         _setZoomTransform: function (level, center, zoom) {
 
11047                 var scale = this._map.getZoomScale(zoom, level.zoom),
 
11048                     translate = level.origin.multiplyBy(scale)
 
11049                         .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
 
11052                         setTransform(level.el, translate, scale);
 
11054                         setPosition(level.el, translate);
 
11058         _resetGrid: function () {
 
11059                 var map = this._map,
 
11060                     crs = map.options.crs,
 
11061                     tileSize = this._tileSize = this.getTileSize(),
 
11062                     tileZoom = this._tileZoom;
 
11064                 var bounds = this._map.getPixelWorldBounds(this._tileZoom);
 
11066                         this._globalTileRange = this._pxBoundsToTileRange(bounds);
 
11069                 this._wrapX = crs.wrapLng && !this.options.noWrap && [
 
11070                         Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
 
11071                         Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
 
11073                 this._wrapY = crs.wrapLat && !this.options.noWrap && [
 
11074                         Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
 
11075                         Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
 
11079         _onMoveEnd: function () {
 
11080                 if (!this._map || this._map._animatingZoom) { return; }
 
11085         _getTiledPixelBounds: function (center) {
 
11086                 var map = this._map,
 
11087                     mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
 
11088                     scale = map.getZoomScale(mapZoom, this._tileZoom),
 
11089                     pixelCenter = map.project(center, this._tileZoom).floor(),
 
11090                     halfSize = map.getSize().divideBy(scale * 2);
 
11092                 return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
 
11095         // Private method to load tiles in the grid's active zoom level according to map bounds
 
11096         _update: function (center) {
 
11097                 var map = this._map;
 
11098                 if (!map) { return; }
 
11099                 var zoom = this._clampZoom(map.getZoom());
 
11101                 if (center === undefined) { center = map.getCenter(); }
 
11102                 if (this._tileZoom === undefined) { return; }   // if out of minzoom/maxzoom
 
11104                 var pixelBounds = this._getTiledPixelBounds(center),
 
11105                     tileRange = this._pxBoundsToTileRange(pixelBounds),
 
11106                     tileCenter = tileRange.getCenter(),
 
11108                     margin = this.options.keepBuffer,
 
11109                     noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
 
11110                                               tileRange.getTopRight().add([margin, -margin]));
 
11112                 // Sanity check: panic if the tile range contains Infinity somewhere.
 
11113                 if (!(isFinite(tileRange.min.x) &&
 
11114                       isFinite(tileRange.min.y) &&
 
11115                       isFinite(tileRange.max.x) &&
 
11116                       isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
 
11118                 for (var key in this._tiles) {
 
11119                         var c = this._tiles[key].coords;
 
11120                         if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
 
11121                                 this._tiles[key].current = false;
 
11125                 // _update just loads more tiles. If the tile zoom level differs too much
 
11126                 // from the map's, let _setView reset levels and prune old tiles.
 
11127                 if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
 
11129                 // create a queue of coordinates to load tiles from
 
11130                 for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
 
11131                         for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
 
11132                                 var coords = new Point(i, j);
 
11133                                 coords.z = this._tileZoom;
 
11135                                 if (!this._isValidTile(coords)) { continue; }
 
11137                                 var tile = this._tiles[this._tileCoordsToKey(coords)];
 
11139                                         tile.current = true;
 
11141                                         queue.push(coords);
 
11146                 // sort tile queue to load tiles in order of their distance to center
 
11147                 queue.sort(function (a, b) {
 
11148                         return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
 
11151                 if (queue.length !== 0) {
 
11152                         // if it's the first batch of tiles to load
 
11153                         if (!this._loading) {
 
11154                                 this._loading = true;
 
11155                                 // @event loading: Event
 
11156                                 // Fired when the grid layer starts loading tiles.
 
11157                                 this.fire('loading');
 
11160                         // create DOM fragment to append tiles in one batch
 
11161                         var fragment = document.createDocumentFragment();
 
11163                         for (i = 0; i < queue.length; i++) {
 
11164                                 this._addTile(queue[i], fragment);
 
11167                         this._level.el.appendChild(fragment);
 
11171         _isValidTile: function (coords) {
 
11172                 var crs = this._map.options.crs;
 
11174                 if (!crs.infinite) {
 
11175                         // don't load tile if it's out of bounds and not wrapped
 
11176                         var bounds = this._globalTileRange;
 
11177                         if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
 
11178                             (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
 
11181                 if (!this.options.bounds) { return true; }
 
11183                 // don't load tile if it doesn't intersect the bounds in options
 
11184                 var tileBounds = this._tileCoordsToBounds(coords);
 
11185                 return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
 
11188         _keyToBounds: function (key) {
 
11189                 return this._tileCoordsToBounds(this._keyToTileCoords(key));
 
11192         _tileCoordsToNwSe: function (coords) {
 
11193                 var map = this._map,
 
11194                     tileSize = this.getTileSize(),
 
11195                     nwPoint = coords.scaleBy(tileSize),
 
11196                     sePoint = nwPoint.add(tileSize),
 
11197                     nw = map.unproject(nwPoint, coords.z),
 
11198                     se = map.unproject(sePoint, coords.z);
 
11202         // converts tile coordinates to its geographical bounds
 
11203         _tileCoordsToBounds: function (coords) {
 
11204                 var bp = this._tileCoordsToNwSe(coords),
 
11205                     bounds = new LatLngBounds(bp[0], bp[1]);
 
11207                 if (!this.options.noWrap) {
 
11208                         bounds = this._map.wrapLatLngBounds(bounds);
 
11212         // converts tile coordinates to key for the tile cache
 
11213         _tileCoordsToKey: function (coords) {
 
11214                 return coords.x + ':' + coords.y + ':' + coords.z;
 
11217         // converts tile cache key to coordinates
 
11218         _keyToTileCoords: function (key) {
 
11219                 var k = key.split(':'),
 
11220                     coords = new Point(+k[0], +k[1]);
 
11225         _removeTile: function (key) {
 
11226                 var tile = this._tiles[key];
 
11227                 if (!tile) { return; }
 
11231                 delete this._tiles[key];
 
11233                 // @event tileunload: TileEvent
 
11234                 // Fired when a tile is removed (e.g. when a tile goes off the screen).
 
11235                 this.fire('tileunload', {
 
11237                         coords: this._keyToTileCoords(key)
 
11241         _initTile: function (tile) {
 
11242                 addClass(tile, 'leaflet-tile');
 
11244                 var tileSize = this.getTileSize();
 
11245                 tile.style.width = tileSize.x + 'px';
 
11246                 tile.style.height = tileSize.y + 'px';
 
11248                 tile.onselectstart = falseFn;
 
11249                 tile.onmousemove = falseFn;
 
11251                 // update opacity on tiles in IE7-8 because of filter inheritance problems
 
11252                 if (ielt9 && this.options.opacity < 1) {
 
11253                         setOpacity(tile, this.options.opacity);
 
11256                 // without this hack, tiles disappear after zoom on Chrome for Android
 
11257                 // https://github.com/Leaflet/Leaflet/issues/2078
 
11258                 if (android && !android23) {
 
11259                         tile.style.WebkitBackfaceVisibility = 'hidden';
 
11263         _addTile: function (coords, container) {
 
11264                 var tilePos = this._getTilePos(coords),
 
11265                     key = this._tileCoordsToKey(coords);
 
11267                 var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
 
11269                 this._initTile(tile);
 
11271                 // if createTile is defined with a second argument ("done" callback),
 
11272                 // we know that tile is async and will be ready later; otherwise
 
11273                 if (this.createTile.length < 2) {
 
11274                         // mark tile as ready, but delay one frame for opacity animation to happen
 
11275                         requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
 
11278                 setPosition(tile, tilePos);
 
11280                 // save tile in cache
 
11281                 this._tiles[key] = {
 
11287                 container.appendChild(tile);
 
11288                 // @event tileloadstart: TileEvent
 
11289                 // Fired when a tile is requested and starts loading.
 
11290                 this.fire('tileloadstart', {
 
11296         _tileReady: function (coords, err, tile) {
 
11298                         // @event tileerror: TileErrorEvent
 
11299                         // Fired when there is an error loading a tile.
 
11300                         this.fire('tileerror', {
 
11307                 var key = this._tileCoordsToKey(coords);
 
11309                 tile = this._tiles[key];
 
11310                 if (!tile) { return; }
 
11312                 tile.loaded = +new Date();
 
11313                 if (this._map._fadeAnimated) {
 
11314                         setOpacity(tile.el, 0);
 
11315                         cancelAnimFrame(this._fadeFrame);
 
11316                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
 
11318                         tile.active = true;
 
11319                         this._pruneTiles();
 
11323                         addClass(tile.el, 'leaflet-tile-loaded');
 
11325                         // @event tileload: TileEvent
 
11326                         // Fired when a tile loads.
 
11327                         this.fire('tileload', {
 
11333                 if (this._noTilesToLoad()) {
 
11334                         this._loading = false;
 
11335                         // @event load: Event
 
11336                         // Fired when the grid layer loaded all visible tiles.
 
11339                         if (ielt9 || !this._map._fadeAnimated) {
 
11340                                 requestAnimFrame(this._pruneTiles, this);
 
11342                                 // Wait a bit more than 0.2 secs (the duration of the tile fade-in)
 
11343                                 // to trigger a pruning.
 
11344                                 setTimeout(bind(this._pruneTiles, this), 250);
 
11349         _getTilePos: function (coords) {
 
11350                 return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
 
11353         _wrapCoords: function (coords) {
 
11354                 var newCoords = new Point(
 
11355                         this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
 
11356                         this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
 
11357                 newCoords.z = coords.z;
 
11361         _pxBoundsToTileRange: function (bounds) {
 
11362                 var tileSize = this.getTileSize();
 
11364                         bounds.min.unscaleBy(tileSize).floor(),
 
11365                         bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
 
11368         _noTilesToLoad: function () {
 
11369                 for (var key in this._tiles) {
 
11370                         if (!this._tiles[key].loaded) { return false; }
 
11376 // @factory L.gridLayer(options?: GridLayer options)
 
11377 // Creates a new instance of GridLayer with the supplied options.
 
11378 function gridLayer(options) {
 
11379         return new GridLayer(options);
 
11384  * @inherits GridLayer
 
11386  * Used to load and display tile layers on the map. Extends `GridLayer`.
 
11391  * L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar'}).addTo(map);
 
11394  * @section URL template
 
11397  * A string of the following form:
 
11400  * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
 
11403  * `{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.
 
11405  * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
 
11408  * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
 
11413 var TileLayer = GridLayer.extend({
 
11416         // @aka TileLayer options
 
11418                 // @option minZoom: Number = 0
 
11419                 // The minimum zoom level down to which this layer will be displayed (inclusive).
 
11422                 // @option maxZoom: Number = 18
 
11423                 // The maximum zoom level up to which this layer will be displayed (inclusive).
 
11426                 // @option subdomains: String|String[] = 'abc'
 
11427                 // 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.
 
11430                 // @option errorTileUrl: String = ''
 
11431                 // URL to the tile image to show in place of the tile that failed to load.
 
11434                 // @option zoomOffset: Number = 0
 
11435                 // The zoom number used in tile URLs will be offset with this value.
 
11438                 // @option tms: Boolean = false
 
11439                 // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
 
11442                 // @option zoomReverse: Boolean = false
 
11443                 // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
 
11444                 zoomReverse: false,
 
11446                 // @option detectRetina: Boolean = false
 
11447                 // 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.
 
11448                 detectRetina: false,
 
11450                 // @option crossOrigin: Boolean|String = false
 
11451                 // Whether the crossOrigin attribute will be added to the tiles.
 
11452                 // If a String is provided, all tiles will have their crossOrigin attribute set to the String provided. This is needed if you want to access tile pixel data.
 
11453                 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
 
11457         initialize: function (url, options) {
 
11461                 options = setOptions(this, options);
 
11463                 // detecting retina displays, adjusting tileSize and zoom levels
 
11464                 if (options.detectRetina && retina && options.maxZoom > 0) {
 
11466                         options.tileSize = Math.floor(options.tileSize / 2);
 
11468                         if (!options.zoomReverse) {
 
11469                                 options.zoomOffset++;
 
11472                                 options.zoomOffset--;
 
11476                         options.minZoom = Math.max(0, options.minZoom);
 
11479                 if (typeof options.subdomains === 'string') {
 
11480                         options.subdomains = options.subdomains.split('');
 
11483                 // for https://github.com/Leaflet/Leaflet/issues/137
 
11485                         this.on('tileunload', this._onTileRemove);
 
11489         // @method setUrl(url: String, noRedraw?: Boolean): this
 
11490         // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
 
11491         setUrl: function (url, noRedraw) {
 
11500         // @method createTile(coords: Object, done?: Function): HTMLElement
 
11501         // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
 
11502         // to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
 
11503         // callback is called when the tile has been loaded.
 
11504         createTile: function (coords, done) {
 
11505                 var tile = document.createElement('img');
 
11507                 on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
 
11508                 on(tile, 'error', bind(this._tileOnError, this, done, tile));
 
11510                 if (this.options.crossOrigin || this.options.crossOrigin === '') {
 
11511                         tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
 
11515                  Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
 
11516                  http://www.w3.org/TR/WCAG20-TECHS/H67
 
11521                  Set role="presentation" to force screen readers to ignore this
 
11522                  https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
 
11524                 tile.setAttribute('role', 'presentation');
 
11526                 tile.src = this.getTileUrl(coords);
 
11531         // @section Extension methods
 
11533         // Layers extending `TileLayer` might reimplement the following method.
 
11534         // @method getTileUrl(coords: Object): String
 
11535         // Called only internally, returns the URL for a tile given its coordinates.
 
11536         // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
 
11537         getTileUrl: function (coords) {
 
11539                         r: retina ? '@2x' : '',
 
11540                         s: this._getSubdomain(coords),
 
11543                         z: this._getZoomForUrl()
 
11545                 if (this._map && !this._map.options.crs.infinite) {
 
11546                         var invertedY = this._globalTileRange.max.y - coords.y;
 
11547                         if (this.options.tms) {
 
11548                                 data['y'] = invertedY;
 
11550                         data['-y'] = invertedY;
 
11553                 return template(this._url, extend(data, this.options));
 
11556         _tileOnLoad: function (done, tile) {
 
11557                 // For https://github.com/Leaflet/Leaflet/issues/3332
 
11559                         setTimeout(bind(done, this, null, tile), 0);
 
11565         _tileOnError: function (done, tile, e) {
 
11566                 var errorUrl = this.options.errorTileUrl;
 
11567                 if (errorUrl && tile.getAttribute('src') !== errorUrl) {
 
11568                         tile.src = errorUrl;
 
11573         _onTileRemove: function (e) {
 
11574                 e.tile.onload = null;
 
11577         _getZoomForUrl: function () {
 
11578                 var zoom = this._tileZoom,
 
11579                 maxZoom = this.options.maxZoom,
 
11580                 zoomReverse = this.options.zoomReverse,
 
11581                 zoomOffset = this.options.zoomOffset;
 
11584                         zoom = maxZoom - zoom;
 
11587                 return zoom + zoomOffset;
 
11590         _getSubdomain: function (tilePoint) {
 
11591                 var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
 
11592                 return this.options.subdomains[index];
 
11595         // stops loading all tiles in the background layer
 
11596         _abortLoading: function () {
 
11598                 for (i in this._tiles) {
 
11599                         if (this._tiles[i].coords.z !== this._tileZoom) {
 
11600                                 tile = this._tiles[i].el;
 
11602                                 tile.onload = falseFn;
 
11603                                 tile.onerror = falseFn;
 
11605                                 if (!tile.complete) {
 
11606                                         tile.src = emptyImageUrl;
 
11608                                         delete this._tiles[i];
 
11614         _removeTile: function (key) {
 
11615                 var tile = this._tiles[key];
 
11616                 if (!tile) { return; }
 
11618                 // Cancels any pending http requests associated with the tile
 
11619                 // unless we're on Android's stock browser,
 
11620                 // see https://github.com/Leaflet/Leaflet/issues/137
 
11621                 if (!androidStock) {
 
11622                         tile.el.setAttribute('src', emptyImageUrl);
 
11625                 return GridLayer.prototype._removeTile.call(this, key);
 
11628         _tileReady: function (coords, err, tile) {
 
11629                 if (!this._map || (tile && tile.getAttribute('src') === emptyImageUrl)) {
 
11633                 return GridLayer.prototype._tileReady.call(this, coords, err, tile);
 
11638 // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
 
11639 // Instantiates a tile layer object given a `URL template` and optionally an options object.
 
11641 function tileLayer(url, options) {
 
11642         return new TileLayer(url, options);
 
11646  * @class TileLayer.WMS
 
11647  * @inherits TileLayer
 
11648  * @aka L.TileLayer.WMS
 
11649  * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
 
11654  * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
 
11655  *      layers: 'nexrad-n0r-900913',
 
11656  *      format: 'image/png',
 
11657  *      transparent: true,
 
11658  *      attribution: "Weather data © 2012 IEM Nexrad"
 
11663 var TileLayerWMS = TileLayer.extend({
 
11666         // @aka TileLayer.WMS options
 
11667         // If any custom options not documented here are used, they will be sent to the
 
11668         // WMS server as extra parameters in each request URL. This can be useful for
 
11669         // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
 
11670         defaultWmsParams: {
 
11674                 // @option layers: String = ''
 
11675                 // **(required)** Comma-separated list of WMS layers to show.
 
11678                 // @option styles: String = ''
 
11679                 // Comma-separated list of WMS styles.
 
11682                 // @option format: String = 'image/jpeg'
 
11683                 // WMS image format (use `'image/png'` for layers with transparency).
 
11684                 format: 'image/jpeg',
 
11686                 // @option transparent: Boolean = false
 
11687                 // If `true`, the WMS service will return images with transparency.
 
11688                 transparent: false,
 
11690                 // @option version: String = '1.1.1'
 
11691                 // Version of the WMS service to use
 
11696                 // @option crs: CRS = null
 
11697                 // Coordinate Reference System to use for the WMS requests, defaults to
 
11698                 // map CRS. Don't change this if you're not sure what it means.
 
11701                 // @option uppercase: Boolean = false
 
11702                 // If `true`, WMS request parameter keys will be uppercase.
 
11706         initialize: function (url, options) {
 
11710                 var wmsParams = extend({}, this.defaultWmsParams);
 
11712                 // all keys that are not TileLayer options go to WMS params
 
11713                 for (var i in options) {
 
11714                         if (!(i in this.options)) {
 
11715                                 wmsParams[i] = options[i];
 
11719                 options = setOptions(this, options);
 
11721                 var realRetina = options.detectRetina && retina ? 2 : 1;
 
11722                 var tileSize = this.getTileSize();
 
11723                 wmsParams.width = tileSize.x * realRetina;
 
11724                 wmsParams.height = tileSize.y * realRetina;
 
11726                 this.wmsParams = wmsParams;
 
11729         onAdd: function (map) {
 
11731                 this._crs = this.options.crs || map.options.crs;
 
11732                 this._wmsVersion = parseFloat(this.wmsParams.version);
 
11734                 var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
 
11735                 this.wmsParams[projectionKey] = this._crs.code;
 
11737                 TileLayer.prototype.onAdd.call(this, map);
 
11740         getTileUrl: function (coords) {
 
11742                 var tileBounds = this._tileCoordsToNwSe(coords),
 
11744                     bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])),
 
11747                     bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
 
11748                     [min.y, min.x, max.y, max.x] :
 
11749                     [min.x, min.y, max.x, max.y]).join(','),
 
11750                     url = TileLayer.prototype.getTileUrl.call(this, coords);
 
11752                         getParamString(this.wmsParams, url, this.options.uppercase) +
 
11753                         (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
 
11756         // @method setParams(params: Object, noRedraw?: Boolean): this
 
11757         // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
 
11758         setParams: function (params, noRedraw) {
 
11760                 extend(this.wmsParams, params);
 
11771 // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
 
11772 // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
 
11773 function tileLayerWMS(url, options) {
 
11774         return new TileLayerWMS(url, options);
 
11777 TileLayer.WMS = TileLayerWMS;
 
11778 tileLayer.wms = tileLayerWMS;
 
11785  * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
 
11786  * DOM container of the renderer, its bounds, and its zoom animation.
 
11788  * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
 
11789  * itself can be added or removed to the map. All paths use a renderer, which can
 
11790  * be implicit (the map will decide the type of renderer and use it automatically)
 
11791  * or explicit (using the [`renderer`](#path-renderer) option of the path).
 
11793  * Do not use this class directly, use `SVG` and `Canvas` instead.
 
11795  * @event update: Event
 
11796  * Fired when the renderer updates its bounds, center and zoom, for example when
 
11797  * its map has moved
 
11800 var Renderer = Layer.extend({
 
11803         // @aka Renderer options
 
11805                 // @option padding: Number = 0.1
 
11806                 // How much to extend the clip area around the map view (relative to its size)
 
11807                 // e.g. 0.1 would be 10% of map view in each direction
 
11810                 // @option tolerance: Number = 0
 
11811                 // How much to extend click tolerance round a path/object on the map
 
11815         initialize: function (options) {
 
11816                 setOptions(this, options);
 
11818                 this._layers = this._layers || {};
 
11821         onAdd: function () {
 
11822                 if (!this._container) {
 
11823                         this._initContainer(); // defined by renderer implementations
 
11825                         if (this._zoomAnimated) {
 
11826                                 addClass(this._container, 'leaflet-zoom-animated');
 
11830                 this.getPane().appendChild(this._container);
 
11832                 this.on('update', this._updatePaths, this);
 
11835         onRemove: function () {
 
11836                 this.off('update', this._updatePaths, this);
 
11837                 this._destroyContainer();
 
11840         getEvents: function () {
 
11842                         viewreset: this._reset,
 
11843                         zoom: this._onZoom,
 
11844                         moveend: this._update,
 
11845                         zoomend: this._onZoomEnd
 
11847                 if (this._zoomAnimated) {
 
11848                         events.zoomanim = this._onAnimZoom;
 
11853         _onAnimZoom: function (ev) {
 
11854                 this._updateTransform(ev.center, ev.zoom);
 
11857         _onZoom: function () {
 
11858                 this._updateTransform(this._map.getCenter(), this._map.getZoom());
 
11861         _updateTransform: function (center, zoom) {
 
11862                 var scale = this._map.getZoomScale(zoom, this._zoom),
 
11863                     position = getPosition(this._container),
 
11864                     viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
 
11865                     currentCenterPoint = this._map.project(this._center, zoom),
 
11866                     destCenterPoint = this._map.project(center, zoom),
 
11867                     centerOffset = destCenterPoint.subtract(currentCenterPoint),
 
11869                     topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
 
11872                         setTransform(this._container, topLeftOffset, scale);
 
11874                         setPosition(this._container, topLeftOffset);
 
11878         _reset: function () {
 
11880                 this._updateTransform(this._center, this._zoom);
 
11882                 for (var id in this._layers) {
 
11883                         this._layers[id]._reset();
 
11887         _onZoomEnd: function () {
 
11888                 for (var id in this._layers) {
 
11889                         this._layers[id]._project();
 
11893         _updatePaths: function () {
 
11894                 for (var id in this._layers) {
 
11895                         this._layers[id]._update();
 
11899         _update: function () {
 
11900                 // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
 
11901                 // Subclasses are responsible of firing the 'update' event.
 
11902                 var p = this.options.padding,
 
11903                     size = this._map.getSize(),
 
11904                     min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
 
11906                 this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
 
11908                 this._center = this._map.getCenter();
 
11909                 this._zoom = this._map.getZoom();
 
11915  * @inherits Renderer
 
11918  * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
 
11919  * Inherits `Renderer`.
 
11921  * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
 
11922  * available in all web browsers, notably IE8, and overlapping geometries might
 
11923  * not display properly in some edge cases.
 
11927  * Use Canvas by default for all paths in the map:
 
11930  * var map = L.map('map', {
 
11931  *      renderer: L.canvas()
 
11935  * Use a Canvas renderer with extra padding for specific vector geometries:
 
11938  * var map = L.map('map');
 
11939  * var myRenderer = L.canvas({ padding: 0.5 });
 
11940  * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
11941  * var circle = L.circle( center, { renderer: myRenderer } );
 
11945 var Canvas = Renderer.extend({
 
11946         getEvents: function () {
 
11947                 var events = Renderer.prototype.getEvents.call(this);
 
11948                 events.viewprereset = this._onViewPreReset;
 
11952         _onViewPreReset: function () {
 
11953                 // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
 
11954                 this._postponeUpdatePaths = true;
 
11957         onAdd: function () {
 
11958                 Renderer.prototype.onAdd.call(this);
 
11960                 // Redraw vectors since canvas is cleared upon removal,
 
11961                 // in case of removing the renderer itself from the map.
 
11965         _initContainer: function () {
 
11966                 var container = this._container = document.createElement('canvas');
 
11968                 on(container, 'mousemove', throttle(this._onMouseMove, 32, this), this);
 
11969                 on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
 
11970                 on(container, 'mouseout', this._handleMouseOut, this);
 
11972                 this._ctx = container.getContext('2d');
 
11975         _destroyContainer: function () {
 
11976                 cancelAnimFrame(this._redrawRequest);
 
11978                 remove(this._container);
 
11979                 off(this._container);
 
11980                 delete this._container;
 
11983         _updatePaths: function () {
 
11984                 if (this._postponeUpdatePaths) { return; }
 
11987                 this._redrawBounds = null;
 
11988                 for (var id in this._layers) {
 
11989                         layer = this._layers[id];
 
11995         _update: function () {
 
11996                 if (this._map._animatingZoom && this._bounds) { return; }
 
11998                 this._drawnLayers = {};
 
12000                 Renderer.prototype._update.call(this);
 
12002                 var b = this._bounds,
 
12003                     container = this._container,
 
12004                     size = b.getSize(),
 
12005                     m = retina ? 2 : 1;
 
12007                 setPosition(container, b.min);
 
12009                 // set canvas size (also clearing it); use double size on retina
 
12010                 container.width = m * size.x;
 
12011                 container.height = m * size.y;
 
12012                 container.style.width = size.x + 'px';
 
12013                 container.style.height = size.y + 'px';
 
12016                         this._ctx.scale(2, 2);
 
12019                 // translate so we use the same path coordinates after canvas element moves
 
12020                 this._ctx.translate(-b.min.x, -b.min.y);
 
12022                 // Tell paths to redraw themselves
 
12023                 this.fire('update');
 
12026         _reset: function () {
 
12027                 Renderer.prototype._reset.call(this);
 
12029                 if (this._postponeUpdatePaths) {
 
12030                         this._postponeUpdatePaths = false;
 
12031                         this._updatePaths();
 
12035         _initPath: function (layer) {
 
12036                 this._updateDashArray(layer);
 
12037                 this._layers[stamp(layer)] = layer;
 
12039                 var order = layer._order = {
 
12041                         prev: this._drawLast,
 
12044                 if (this._drawLast) { this._drawLast.next = order; }
 
12045                 this._drawLast = order;
 
12046                 this._drawFirst = this._drawFirst || this._drawLast;
 
12049         _addPath: function (layer) {
 
12050                 this._requestRedraw(layer);
 
12053         _removePath: function (layer) {
 
12054                 var order = layer._order;
 
12055                 var next = order.next;
 
12056                 var prev = order.prev;
 
12061                         this._drawLast = prev;
 
12066                         this._drawFirst = next;
 
12069                 delete this._drawnLayers[layer._leaflet_id];
 
12071                 delete layer._order;
 
12073                 delete this._layers[stamp(layer)];
 
12075                 this._requestRedraw(layer);
 
12078         _updatePath: function (layer) {
 
12079                 // Redraw the union of the layer's old pixel
 
12080                 // bounds and the new pixel bounds.
 
12081                 this._extendRedrawBounds(layer);
 
12084                 // The redraw will extend the redraw bounds
 
12085                 // with the new pixel bounds.
 
12086                 this._requestRedraw(layer);
 
12089         _updateStyle: function (layer) {
 
12090                 this._updateDashArray(layer);
 
12091                 this._requestRedraw(layer);
 
12094         _updateDashArray: function (layer) {
 
12095                 if (typeof layer.options.dashArray === 'string') {
 
12096                         var parts = layer.options.dashArray.split(/[, ]+/),
 
12099                         for (i = 0; i < parts.length; i++) {
 
12100                                 dashArray.push(Number(parts[i]));
 
12102                         layer.options._dashArray = dashArray;
 
12104                         layer.options._dashArray = layer.options.dashArray;
 
12108         _requestRedraw: function (layer) {
 
12109                 if (!this._map) { return; }
 
12111                 this._extendRedrawBounds(layer);
 
12112                 this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
 
12115         _extendRedrawBounds: function (layer) {
 
12116                 if (layer._pxBounds) {
 
12117                         var padding = (layer.options.weight || 0) + 1;
 
12118                         this._redrawBounds = this._redrawBounds || new Bounds();
 
12119                         this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
 
12120                         this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
 
12124         _redraw: function () {
 
12125                 this._redrawRequest = null;
 
12127                 if (this._redrawBounds) {
 
12128                         this._redrawBounds.min._floor();
 
12129                         this._redrawBounds.max._ceil();
 
12132                 this._clear(); // clear layers in redraw bounds
 
12133                 this._draw(); // draw layers
 
12135                 this._redrawBounds = null;
 
12138         _clear: function () {
 
12139                 var bounds = this._redrawBounds;
 
12141                         var size = bounds.getSize();
 
12142                         this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
 
12144                         this._ctx.clearRect(0, 0, this._container.width, this._container.height);
 
12148         _draw: function () {
 
12149                 var layer, bounds = this._redrawBounds;
 
12152                         var size = bounds.getSize();
 
12153                         this._ctx.beginPath();
 
12154                         this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
 
12158                 this._drawing = true;
 
12160                 for (var order = this._drawFirst; order; order = order.next) {
 
12161                         layer = order.layer;
 
12162                         if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
 
12163                                 layer._updatePath();
 
12167                 this._drawing = false;
 
12169                 this._ctx.restore();  // Restore state before clipping.
 
12172         _updatePoly: function (layer, closed) {
 
12173                 if (!this._drawing) { return; }
 
12176                     parts = layer._parts,
 
12177                     len = parts.length,
 
12180                 if (!len) { return; }
 
12182                 this._drawnLayers[layer._leaflet_id] = layer;
 
12186                 for (i = 0; i < len; i++) {
 
12187                         for (j = 0, len2 = parts[i].length; j < len2; j++) {
 
12189                                 ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
 
12196                 this._fillStroke(ctx, layer);
 
12198                 // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
 
12201         _updateCircle: function (layer) {
 
12203                 if (!this._drawing || layer._empty()) { return; }
 
12205                 var p = layer._point,
 
12207                     r = Math.max(Math.round(layer._radius), 1),
 
12208                     s = (Math.max(Math.round(layer._radiusY), 1) || r) / r;
 
12210                 this._drawnLayers[layer._leaflet_id] = layer;
 
12218                 ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
 
12224                 this._fillStroke(ctx, layer);
 
12227         _fillStroke: function (ctx, layer) {
 
12228                 var options = layer.options;
 
12230                 if (options.fill) {
 
12231                         ctx.globalAlpha = options.fillOpacity;
 
12232                         ctx.fillStyle = options.fillColor || options.color;
 
12233                         ctx.fill(options.fillRule || 'evenodd');
 
12236                 if (options.stroke && options.weight !== 0) {
 
12237                         if (ctx.setLineDash) {
 
12238                                 ctx.setLineDash(layer.options && layer.options._dashArray || []);
 
12240                         ctx.globalAlpha = options.opacity;
 
12241                         ctx.lineWidth = options.weight;
 
12242                         ctx.strokeStyle = options.color;
 
12243                         ctx.lineCap = options.lineCap;
 
12244                         ctx.lineJoin = options.lineJoin;
 
12249         // Canvas obviously doesn't have mouse events for individual drawn objects,
 
12250         // so we emulate that by calculating what's under the mouse on mousemove/click manually
 
12252         _onClick: function (e) {
 
12253                 var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
 
12255                 for (var order = this._drawFirst; order; order = order.next) {
 
12256                         layer = order.layer;
 
12257                         if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
 
12258                                 clickedLayer = layer;
 
12261                 if (clickedLayer)  {
 
12263                         this._fireEvent([clickedLayer], e);
 
12267         _onMouseMove: function (e) {
 
12268                 if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
 
12270                 var point = this._map.mouseEventToLayerPoint(e);
 
12271                 this._handleMouseHover(e, point);
 
12275         _handleMouseOut: function (e) {
 
12276                 var layer = this._hoveredLayer;
 
12278                         // if we're leaving the layer, fire mouseout
 
12279                         removeClass(this._container, 'leaflet-interactive');
 
12280                         this._fireEvent([layer], e, 'mouseout');
 
12281                         this._hoveredLayer = null;
 
12285         _handleMouseHover: function (e, point) {
 
12286                 var layer, candidateHoveredLayer;
 
12288                 for (var order = this._drawFirst; order; order = order.next) {
 
12289                         layer = order.layer;
 
12290                         if (layer.options.interactive && layer._containsPoint(point)) {
 
12291                                 candidateHoveredLayer = layer;
 
12295                 if (candidateHoveredLayer !== this._hoveredLayer) {
 
12296                         this._handleMouseOut(e);
 
12298                         if (candidateHoveredLayer) {
 
12299                                 addClass(this._container, 'leaflet-interactive'); // change cursor
 
12300                                 this._fireEvent([candidateHoveredLayer], e, 'mouseover');
 
12301                                 this._hoveredLayer = candidateHoveredLayer;
 
12305                 if (this._hoveredLayer) {
 
12306                         this._fireEvent([this._hoveredLayer], e);
 
12310         _fireEvent: function (layers, e, type) {
 
12311                 this._map._fireDOMEvent(e, type || e.type, layers);
 
12314         _bringToFront: function (layer) {
 
12315                 var order = layer._order;
 
12316                 var next = order.next;
 
12317                 var prev = order.prev;
 
12328                         // Update first entry unless this is the
 
12330                         this._drawFirst = next;
 
12333                 order.prev = this._drawLast;
 
12334                 this._drawLast.next = order;
 
12337                 this._drawLast = order;
 
12339                 this._requestRedraw(layer);
 
12342         _bringToBack: function (layer) {
 
12343                 var order = layer._order;
 
12344                 var next = order.next;
 
12345                 var prev = order.prev;
 
12356                         // Update last entry unless this is the
 
12358                         this._drawLast = prev;
 
12363                 order.next = this._drawFirst;
 
12364                 this._drawFirst.prev = order;
 
12365                 this._drawFirst = order;
 
12367                 this._requestRedraw(layer);
 
12371 // @factory L.canvas(options?: Renderer options)
 
12372 // Creates a Canvas renderer with the given options.
 
12373 function canvas$1(options) {
 
12374         return canvas ? new Canvas(options) : null;
 
12378  * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
 
12382 var vmlCreate = (function () {
 
12384                 document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
 
12385                 return function (name) {
 
12386                         return document.createElement('<lvml:' + name + ' class="lvml">');
 
12389                 return function (name) {
 
12390                         return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
 
12399  * 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.
 
12401  * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
 
12402  * with old versions of Internet Explorer.
 
12405 // mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
 
12408         _initContainer: function () {
 
12409                 this._container = create$1('div', 'leaflet-vml-container');
 
12412         _update: function () {
 
12413                 if (this._map._animatingZoom) { return; }
 
12414                 Renderer.prototype._update.call(this);
 
12415                 this.fire('update');
 
12418         _initPath: function (layer) {
 
12419                 var container = layer._container = vmlCreate('shape');
 
12421                 addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
 
12423                 container.coordsize = '1 1';
 
12425                 layer._path = vmlCreate('path');
 
12426                 container.appendChild(layer._path);
 
12428                 this._updateStyle(layer);
 
12429                 this._layers[stamp(layer)] = layer;
 
12432         _addPath: function (layer) {
 
12433                 var container = layer._container;
 
12434                 this._container.appendChild(container);
 
12436                 if (layer.options.interactive) {
 
12437                         layer.addInteractiveTarget(container);
 
12441         _removePath: function (layer) {
 
12442                 var container = layer._container;
 
12444                 layer.removeInteractiveTarget(container);
 
12445                 delete this._layers[stamp(layer)];
 
12448         _updateStyle: function (layer) {
 
12449                 var stroke = layer._stroke,
 
12450                     fill = layer._fill,
 
12451                     options = layer.options,
 
12452                     container = layer._container;
 
12454                 container.stroked = !!options.stroke;
 
12455                 container.filled = !!options.fill;
 
12457                 if (options.stroke) {
 
12459                                 stroke = layer._stroke = vmlCreate('stroke');
 
12461                         container.appendChild(stroke);
 
12462                         stroke.weight = options.weight + 'px';
 
12463                         stroke.color = options.color;
 
12464                         stroke.opacity = options.opacity;
 
12466                         if (options.dashArray) {
 
12467                                 stroke.dashStyle = isArray(options.dashArray) ?
 
12468                                     options.dashArray.join(' ') :
 
12469                                     options.dashArray.replace(/( *, *)/g, ' ');
 
12471                                 stroke.dashStyle = '';
 
12473                         stroke.endcap = options.lineCap.replace('butt', 'flat');
 
12474                         stroke.joinstyle = options.lineJoin;
 
12476                 } else if (stroke) {
 
12477                         container.removeChild(stroke);
 
12478                         layer._stroke = null;
 
12481                 if (options.fill) {
 
12483                                 fill = layer._fill = vmlCreate('fill');
 
12485                         container.appendChild(fill);
 
12486                         fill.color = options.fillColor || options.color;
 
12487                         fill.opacity = options.fillOpacity;
 
12490                         container.removeChild(fill);
 
12491                         layer._fill = null;
 
12495         _updateCircle: function (layer) {
 
12496                 var p = layer._point.round(),
 
12497                     r = Math.round(layer._radius),
 
12498                     r2 = Math.round(layer._radiusY || r);
 
12500                 this._setPath(layer, layer._empty() ? 'M0 0' :
 
12501                         'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
 
12504         _setPath: function (layer, path) {
 
12505                 layer._path.v = path;
 
12508         _bringToFront: function (layer) {
 
12509                 toFront(layer._container);
 
12512         _bringToBack: function (layer) {
 
12513                 toBack(layer._container);
 
12517 var create$2 = vml ? vmlCreate : svgCreate;
 
12521  * @inherits Renderer
 
12524  * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
 
12525  * Inherits `Renderer`.
 
12527  * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
 
12528  * available in all web browsers, notably Android 2.x and 3.x.
 
12530  * Although SVG is not available on IE7 and IE8, these browsers support
 
12531  * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
 
12532  * (a now deprecated technology), and the SVG renderer will fall back to VML in
 
12537  * Use SVG by default for all paths in the map:
 
12540  * var map = L.map('map', {
 
12541  *      renderer: L.svg()
 
12545  * Use a SVG renderer with extra padding for specific vector geometries:
 
12548  * var map = L.map('map');
 
12549  * var myRenderer = L.svg({ padding: 0.5 });
 
12550  * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
12551  * var circle = L.circle( center, { renderer: myRenderer } );
 
12555 var SVG = Renderer.extend({
 
12557         getEvents: function () {
 
12558                 var events = Renderer.prototype.getEvents.call(this);
 
12559                 events.zoomstart = this._onZoomStart;
 
12563         _initContainer: function () {
 
12564                 this._container = create$2('svg');
 
12566                 // makes it possible to click through svg root; we'll reset it back in individual paths
 
12567                 this._container.setAttribute('pointer-events', 'none');
 
12569                 this._rootGroup = create$2('g');
 
12570                 this._container.appendChild(this._rootGroup);
 
12573         _destroyContainer: function () {
 
12574                 remove(this._container);
 
12575                 off(this._container);
 
12576                 delete this._container;
 
12577                 delete this._rootGroup;
 
12578                 delete this._svgSize;
 
12581         _onZoomStart: function () {
 
12582                 // Drag-then-pinch interactions might mess up the center and zoom.
 
12583                 // In this case, the easiest way to prevent this is re-do the renderer
 
12584                 //   bounds and padding when the zooming starts.
 
12588         _update: function () {
 
12589                 if (this._map._animatingZoom && this._bounds) { return; }
 
12591                 Renderer.prototype._update.call(this);
 
12593                 var b = this._bounds,
 
12594                     size = b.getSize(),
 
12595                     container = this._container;
 
12597                 // set size of svg-container if changed
 
12598                 if (!this._svgSize || !this._svgSize.equals(size)) {
 
12599                         this._svgSize = size;
 
12600                         container.setAttribute('width', size.x);
 
12601                         container.setAttribute('height', size.y);
 
12604                 // movement: update container viewBox so that we don't have to change coordinates of individual layers
 
12605                 setPosition(container, b.min);
 
12606                 container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
 
12608                 this.fire('update');
 
12611         // methods below are called by vector layers implementations
 
12613         _initPath: function (layer) {
 
12614                 var path = layer._path = create$2('path');
 
12617                 // @option className: String = null
 
12618                 // Custom class name set on an element. Only for SVG renderer.
 
12619                 if (layer.options.className) {
 
12620                         addClass(path, layer.options.className);
 
12623                 if (layer.options.interactive) {
 
12624                         addClass(path, 'leaflet-interactive');
 
12627                 this._updateStyle(layer);
 
12628                 this._layers[stamp(layer)] = layer;
 
12631         _addPath: function (layer) {
 
12632                 if (!this._rootGroup) { this._initContainer(); }
 
12633                 this._rootGroup.appendChild(layer._path);
 
12634                 layer.addInteractiveTarget(layer._path);
 
12637         _removePath: function (layer) {
 
12638                 remove(layer._path);
 
12639                 layer.removeInteractiveTarget(layer._path);
 
12640                 delete this._layers[stamp(layer)];
 
12643         _updatePath: function (layer) {
 
12648         _updateStyle: function (layer) {
 
12649                 var path = layer._path,
 
12650                     options = layer.options;
 
12652                 if (!path) { return; }
 
12654                 if (options.stroke) {
 
12655                         path.setAttribute('stroke', options.color);
 
12656                         path.setAttribute('stroke-opacity', options.opacity);
 
12657                         path.setAttribute('stroke-width', options.weight);
 
12658                         path.setAttribute('stroke-linecap', options.lineCap);
 
12659                         path.setAttribute('stroke-linejoin', options.lineJoin);
 
12661                         if (options.dashArray) {
 
12662                                 path.setAttribute('stroke-dasharray', options.dashArray);
 
12664                                 path.removeAttribute('stroke-dasharray');
 
12667                         if (options.dashOffset) {
 
12668                                 path.setAttribute('stroke-dashoffset', options.dashOffset);
 
12670                                 path.removeAttribute('stroke-dashoffset');
 
12673                         path.setAttribute('stroke', 'none');
 
12676                 if (options.fill) {
 
12677                         path.setAttribute('fill', options.fillColor || options.color);
 
12678                         path.setAttribute('fill-opacity', options.fillOpacity);
 
12679                         path.setAttribute('fill-rule', options.fillRule || 'evenodd');
 
12681                         path.setAttribute('fill', 'none');
 
12685         _updatePoly: function (layer, closed) {
 
12686                 this._setPath(layer, pointsToPath(layer._parts, closed));
 
12689         _updateCircle: function (layer) {
 
12690                 var p = layer._point,
 
12691                     r = Math.max(Math.round(layer._radius), 1),
 
12692                     r2 = Math.max(Math.round(layer._radiusY), 1) || r,
 
12693                     arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
 
12695                 // drawing a circle with two half-arcs
 
12696                 var d = layer._empty() ? 'M0 0' :
 
12697                         'M' + (p.x - r) + ',' + p.y +
 
12698                         arc + (r * 2) + ',0 ' +
 
12699                         arc + (-r * 2) + ',0 ';
 
12701                 this._setPath(layer, d);
 
12704         _setPath: function (layer, path) {
 
12705                 layer._path.setAttribute('d', path);
 
12708         // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
 
12709         _bringToFront: function (layer) {
 
12710                 toFront(layer._path);
 
12713         _bringToBack: function (layer) {
 
12714                 toBack(layer._path);
 
12719         SVG.include(vmlMixin);
 
12723 // @factory L.svg(options?: Renderer options)
 
12724 // Creates a SVG renderer with the given options.
 
12725 function svg$1(options) {
 
12726         return svg || vml ? new SVG(options) : null;
 
12730         // @namespace Map; @method getRenderer(layer: Path): Renderer
 
12731         // Returns the instance of `Renderer` that should be used to render the given
 
12732         // `Path`. It will ensure that the `renderer` options of the map and paths
 
12733         // are respected, and that the renderers do exist on the map.
 
12734         getRenderer: function (layer) {
 
12735                 // @namespace Path; @option renderer: Renderer
 
12736                 // Use this specific instance of `Renderer` for this path. Takes
 
12737                 // precedence over the map's [default renderer](#map-renderer).
 
12738                 var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
 
12741                         renderer = this._renderer = this._createRenderer();
 
12744                 if (!this.hasLayer(renderer)) {
 
12745                         this.addLayer(renderer);
 
12750         _getPaneRenderer: function (name) {
 
12751                 if (name === 'overlayPane' || name === undefined) {
 
12755                 var renderer = this._paneRenderers[name];
 
12756                 if (renderer === undefined) {
 
12757                         renderer = this._createRenderer({pane: name});
 
12758                         this._paneRenderers[name] = renderer;
 
12763         _createRenderer: function (options) {
 
12764                 // @namespace Map; @option preferCanvas: Boolean = false
 
12765                 // Whether `Path`s should be rendered on a `Canvas` renderer.
 
12766                 // By default, all `Path`s are rendered in a `SVG` renderer.
 
12767                 return (this.options.preferCanvas && canvas$1(options)) || svg$1(options);
 
12772  * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
 
12778  * @inherits Polygon
 
12780  * A class for drawing rectangle overlays on a map. Extends `Polygon`.
 
12785  * // define rectangle geographical bounds
 
12786  * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
 
12788  * // create an orange rectangle
 
12789  * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
 
12791  * // zoom the map to the rectangle bounds
 
12792  * map.fitBounds(bounds);
 
12798 var Rectangle = Polygon.extend({
 
12799         initialize: function (latLngBounds, options) {
 
12800                 Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
 
12803         // @method setBounds(latLngBounds: LatLngBounds): this
 
12804         // Redraws the rectangle with the passed bounds.
 
12805         setBounds: function (latLngBounds) {
 
12806                 return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
 
12809         _boundsToLatLngs: function (latLngBounds) {
 
12810                 latLngBounds = toLatLngBounds(latLngBounds);
 
12812                         latLngBounds.getSouthWest(),
 
12813                         latLngBounds.getNorthWest(),
 
12814                         latLngBounds.getNorthEast(),
 
12815                         latLngBounds.getSouthEast()
 
12821 // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
 
12822 function rectangle(latLngBounds, options) {
 
12823         return new Rectangle(latLngBounds, options);
 
12826 SVG.create = create$2;
 
12827 SVG.pointsToPath = pointsToPath;
 
12829 GeoJSON.geometryToLayer = geometryToLayer;
 
12830 GeoJSON.coordsToLatLng = coordsToLatLng;
 
12831 GeoJSON.coordsToLatLngs = coordsToLatLngs;
 
12832 GeoJSON.latLngToCoords = latLngToCoords;
 
12833 GeoJSON.latLngsToCoords = latLngsToCoords;
 
12834 GeoJSON.getFeature = getFeature;
 
12835 GeoJSON.asFeature = asFeature;
 
12838  * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
 
12839  * (zoom to a selected bounding box), enabled by default.
 
12843 // @section Interaction Options
 
12845         // @option boxZoom: Boolean = true
 
12846         // Whether the map can be zoomed to a rectangular area specified by
 
12847         // dragging the mouse while pressing the shift key.
 
12851 var BoxZoom = Handler.extend({
 
12852         initialize: function (map) {
 
12854                 this._container = map._container;
 
12855                 this._pane = map._panes.overlayPane;
 
12856                 this._resetStateTimeout = 0;
 
12857                 map.on('unload', this._destroy, this);
 
12860         addHooks: function () {
 
12861                 on(this._container, 'mousedown', this._onMouseDown, this);
 
12864         removeHooks: function () {
 
12865                 off(this._container, 'mousedown', this._onMouseDown, this);
 
12868         moved: function () {
 
12869                 return this._moved;
 
12872         _destroy: function () {
 
12873                 remove(this._pane);
 
12877         _resetState: function () {
 
12878                 this._resetStateTimeout = 0;
 
12879                 this._moved = false;
 
12882         _clearDeferredResetState: function () {
 
12883                 if (this._resetStateTimeout !== 0) {
 
12884                         clearTimeout(this._resetStateTimeout);
 
12885                         this._resetStateTimeout = 0;
 
12889         _onMouseDown: function (e) {
 
12890                 if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
 
12892                 // Clear the deferred resetState if it hasn't executed yet, otherwise it
 
12893                 // will interrupt the interaction and orphan a box element in the container.
 
12894                 this._clearDeferredResetState();
 
12895                 this._resetState();
 
12897                 disableTextSelection();
 
12898                 disableImageDrag();
 
12900                 this._startPoint = this._map.mouseEventToContainerPoint(e);
 
12904                         mousemove: this._onMouseMove,
 
12905                         mouseup: this._onMouseUp,
 
12906                         keydown: this._onKeyDown
 
12910         _onMouseMove: function (e) {
 
12911                 if (!this._moved) {
 
12912                         this._moved = true;
 
12914                         this._box = create$1('div', 'leaflet-zoom-box', this._container);
 
12915                         addClass(this._container, 'leaflet-crosshair');
 
12917                         this._map.fire('boxzoomstart');
 
12920                 this._point = this._map.mouseEventToContainerPoint(e);
 
12922                 var bounds = new Bounds(this._point, this._startPoint),
 
12923                     size = bounds.getSize();
 
12925                 setPosition(this._box, bounds.min);
 
12927                 this._box.style.width  = size.x + 'px';
 
12928                 this._box.style.height = size.y + 'px';
 
12931         _finish: function () {
 
12934                         removeClass(this._container, 'leaflet-crosshair');
 
12937                 enableTextSelection();
 
12942                         mousemove: this._onMouseMove,
 
12943                         mouseup: this._onMouseUp,
 
12944                         keydown: this._onKeyDown
 
12948         _onMouseUp: function (e) {
 
12949                 if ((e.which !== 1) && (e.button !== 1)) { return; }
 
12953                 if (!this._moved) { return; }
 
12954                 // Postpone to next JS tick so internal click event handling
 
12955                 // still see it as "moved".
 
12956                 this._clearDeferredResetState();
 
12957                 this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
 
12959                 var bounds = new LatLngBounds(
 
12960                         this._map.containerPointToLatLng(this._startPoint),
 
12961                         this._map.containerPointToLatLng(this._point));
 
12965                         .fire('boxzoomend', {boxZoomBounds: bounds});
 
12968         _onKeyDown: function (e) {
 
12969                 if (e.keyCode === 27) {
 
12975 // @section Handlers
 
12976 // @property boxZoom: Handler
 
12977 // Box (shift-drag with mouse) zoom handler.
 
12978 Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
 
12981  * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
 
12985 // @section Interaction Options
 
12988         // @option doubleClickZoom: Boolean|String = true
 
12989         // Whether the map can be zoomed in by double clicking on it and
 
12990         // zoomed out by double clicking while holding shift. If passed
 
12991         // `'center'`, double-click zoom will zoom to the center of the
 
12992         //  view regardless of where the mouse was.
 
12993         doubleClickZoom: true
 
12996 var DoubleClickZoom = Handler.extend({
 
12997         addHooks: function () {
 
12998                 this._map.on('dblclick', this._onDoubleClick, this);
 
13001         removeHooks: function () {
 
13002                 this._map.off('dblclick', this._onDoubleClick, this);
 
13005         _onDoubleClick: function (e) {
 
13006                 var map = this._map,
 
13007                     oldZoom = map.getZoom(),
 
13008                     delta = map.options.zoomDelta,
 
13009                     zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
 
13011                 if (map.options.doubleClickZoom === 'center') {
 
13014                         map.setZoomAround(e.containerPoint, zoom);
 
13019 // @section Handlers
 
13021 // Map properties include interaction handlers that allow you to control
 
13022 // interaction behavior in runtime, enabling or disabling certain features such
 
13023 // as dragging or touch zoom (see `Handler` methods). For example:
 
13026 // map.doubleClickZoom.disable();
 
13029 // @property doubleClickZoom: Handler
 
13030 // Double click zoom handler.
 
13031 Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
 
13034  * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
 
13038 // @section Interaction Options
 
13040         // @option dragging: Boolean = true
 
13041         // Whether the map be draggable with mouse/touch or not.
 
13044         // @section Panning Inertia Options
 
13045         // @option inertia: Boolean = *
 
13046         // If enabled, panning of the map will have an inertia effect where
 
13047         // the map builds momentum while dragging and continues moving in
 
13048         // the same direction for some time. Feels especially nice on touch
 
13049         // devices. Enabled by default unless running on old Android devices.
 
13050         inertia: !android23,
 
13052         // @option inertiaDeceleration: Number = 3000
 
13053         // The rate with which the inertial movement slows down, in pixels/second².
 
13054         inertiaDeceleration: 3400, // px/s^2
 
13056         // @option inertiaMaxSpeed: Number = Infinity
 
13057         // Max speed of the inertial movement, in pixels/second.
 
13058         inertiaMaxSpeed: Infinity, // px/s
 
13060         // @option easeLinearity: Number = 0.2
 
13061         easeLinearity: 0.2,
 
13063         // TODO refactor, move to CRS
 
13064         // @option worldCopyJump: Boolean = false
 
13065         // With this option enabled, the map tracks when you pan to another "copy"
 
13066         // of the world and seamlessly jumps to the original one so that all overlays
 
13067         // like markers and vector layers are still visible.
 
13068         worldCopyJump: false,
 
13070         // @option maxBoundsViscosity: Number = 0.0
 
13071         // If `maxBounds` is set, this option will control how solid the bounds
 
13072         // are when dragging the map around. The default value of `0.0` allows the
 
13073         // user to drag outside the bounds at normal speed, higher values will
 
13074         // slow down map dragging outside bounds, and `1.0` makes the bounds fully
 
13075         // solid, preventing the user from dragging outside the bounds.
 
13076         maxBoundsViscosity: 0.0
 
13079 var Drag = Handler.extend({
 
13080         addHooks: function () {
 
13081                 if (!this._draggable) {
 
13082                         var map = this._map;
 
13084                         this._draggable = new Draggable(map._mapPane, map._container);
 
13086                         this._draggable.on({
 
13087                                 dragstart: this._onDragStart,
 
13088                                 drag: this._onDrag,
 
13089                                 dragend: this._onDragEnd
 
13092                         this._draggable.on('predrag', this._onPreDragLimit, this);
 
13093                         if (map.options.worldCopyJump) {
 
13094                                 this._draggable.on('predrag', this._onPreDragWrap, this);
 
13095                                 map.on('zoomend', this._onZoomEnd, this);
 
13097                                 map.whenReady(this._onZoomEnd, this);
 
13100                 addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
 
13101                 this._draggable.enable();
 
13102                 this._positions = [];
 
13106         removeHooks: function () {
 
13107                 removeClass(this._map._container, 'leaflet-grab');
 
13108                 removeClass(this._map._container, 'leaflet-touch-drag');
 
13109                 this._draggable.disable();
 
13112         moved: function () {
 
13113                 return this._draggable && this._draggable._moved;
 
13116         moving: function () {
 
13117                 return this._draggable && this._draggable._moving;
 
13120         _onDragStart: function () {
 
13121                 var map = this._map;
 
13124                 if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
 
13125                         var bounds = toLatLngBounds(this._map.options.maxBounds);
 
13127                         this._offsetLimit = toBounds(
 
13128                                 this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
 
13129                                 this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
 
13130                                         .add(this._map.getSize()));
 
13132                         this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
 
13134                         this._offsetLimit = null;
 
13139                     .fire('dragstart');
 
13141                 if (map.options.inertia) {
 
13142                         this._positions = [];
 
13147         _onDrag: function (e) {
 
13148                 if (this._map.options.inertia) {
 
13149                         var time = this._lastTime = +new Date(),
 
13150                             pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
 
13152                         this._positions.push(pos);
 
13153                         this._times.push(time);
 
13155                         this._prunePositions(time);
 
13163         _prunePositions: function (time) {
 
13164                 while (this._positions.length > 1 && time - this._times[0] > 50) {
 
13165                         this._positions.shift();
 
13166                         this._times.shift();
 
13170         _onZoomEnd: function () {
 
13171                 var pxCenter = this._map.getSize().divideBy(2),
 
13172                     pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
 
13174                 this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
 
13175                 this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
 
13178         _viscousLimit: function (value, threshold) {
 
13179                 return value - (value - threshold) * this._viscosity;
 
13182         _onPreDragLimit: function () {
 
13183                 if (!this._viscosity || !this._offsetLimit) { return; }
 
13185                 var offset = this._draggable._newPos.subtract(this._draggable._startPos);
 
13187                 var limit = this._offsetLimit;
 
13188                 if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
 
13189                 if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
 
13190                 if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
 
13191                 if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
 
13193                 this._draggable._newPos = this._draggable._startPos.add(offset);
 
13196         _onPreDragWrap: function () {
 
13197                 // TODO refactor to be able to adjust map pane position after zoom
 
13198                 var worldWidth = this._worldWidth,
 
13199                     halfWidth = Math.round(worldWidth / 2),
 
13200                     dx = this._initialWorldOffset,
 
13201                     x = this._draggable._newPos.x,
 
13202                     newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
 
13203                     newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
 
13204                     newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
 
13206                 this._draggable._absPos = this._draggable._newPos.clone();
 
13207                 this._draggable._newPos.x = newX;
 
13210         _onDragEnd: function (e) {
 
13211                 var map = this._map,
 
13212                     options = map.options,
 
13214                     noInertia = !options.inertia || this._times.length < 2;
 
13216                 map.fire('dragend', e);
 
13219                         map.fire('moveend');
 
13222                         this._prunePositions(+new Date());
 
13224                         var direction = this._lastPos.subtract(this._positions[0]),
 
13225                             duration = (this._lastTime - this._times[0]) / 1000,
 
13226                             ease = options.easeLinearity,
 
13228                             speedVector = direction.multiplyBy(ease / duration),
 
13229                             speed = speedVector.distanceTo([0, 0]),
 
13231                             limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
 
13232                             limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
 
13234                             decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
 
13235                             offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
 
13237                         if (!offset.x && !offset.y) {
 
13238                                 map.fire('moveend');
 
13241                                 offset = map._limitOffset(offset, map.options.maxBounds);
 
13243                                 requestAnimFrame(function () {
 
13244                                         map.panBy(offset, {
 
13245                                                 duration: decelerationDuration,
 
13246                                                 easeLinearity: ease,
 
13256 // @section Handlers
 
13257 // @property dragging: Handler
 
13258 // Map dragging handler (by both mouse and touch).
 
13259 Map.addInitHook('addHandler', 'dragging', Drag);
 
13262  * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
 
13266 // @section Keyboard Navigation Options
 
13268         // @option keyboard: Boolean = true
 
13269         // Makes the map focusable and allows users to navigate the map with keyboard
 
13270         // arrows and `+`/`-` keys.
 
13273         // @option keyboardPanDelta: Number = 80
 
13274         // Amount of pixels to pan when pressing an arrow key.
 
13275         keyboardPanDelta: 80
 
13278 var Keyboard = Handler.extend({
 
13285                 zoomIn:  [187, 107, 61, 171],
 
13286                 zoomOut: [189, 109, 54, 173]
 
13289         initialize: function (map) {
 
13292                 this._setPanDelta(map.options.keyboardPanDelta);
 
13293                 this._setZoomDelta(map.options.zoomDelta);
 
13296         addHooks: function () {
 
13297                 var container = this._map._container;
 
13299                 // make the container focusable by tabbing
 
13300                 if (container.tabIndex <= 0) {
 
13301                         container.tabIndex = '0';
 
13305                         focus: this._onFocus,
 
13306                         blur: this._onBlur,
 
13307                         mousedown: this._onMouseDown
 
13311                         focus: this._addHooks,
 
13312                         blur: this._removeHooks
 
13316         removeHooks: function () {
 
13317                 this._removeHooks();
 
13319                 off(this._map._container, {
 
13320                         focus: this._onFocus,
 
13321                         blur: this._onBlur,
 
13322                         mousedown: this._onMouseDown
 
13326                         focus: this._addHooks,
 
13327                         blur: this._removeHooks
 
13331         _onMouseDown: function () {
 
13332                 if (this._focused) { return; }
 
13334                 var body = document.body,
 
13335                     docEl = document.documentElement,
 
13336                     top = body.scrollTop || docEl.scrollTop,
 
13337                     left = body.scrollLeft || docEl.scrollLeft;
 
13339                 this._map._container.focus();
 
13341                 window.scrollTo(left, top);
 
13344         _onFocus: function () {
 
13345                 this._focused = true;
 
13346                 this._map.fire('focus');
 
13349         _onBlur: function () {
 
13350                 this._focused = false;
 
13351                 this._map.fire('blur');
 
13354         _setPanDelta: function (panDelta) {
 
13355                 var keys = this._panKeys = {},
 
13356                     codes = this.keyCodes,
 
13359                 for (i = 0, len = codes.left.length; i < len; i++) {
 
13360                         keys[codes.left[i]] = [-1 * panDelta, 0];
 
13362                 for (i = 0, len = codes.right.length; i < len; i++) {
 
13363                         keys[codes.right[i]] = [panDelta, 0];
 
13365                 for (i = 0, len = codes.down.length; i < len; i++) {
 
13366                         keys[codes.down[i]] = [0, panDelta];
 
13368                 for (i = 0, len = codes.up.length; i < len; i++) {
 
13369                         keys[codes.up[i]] = [0, -1 * panDelta];
 
13373         _setZoomDelta: function (zoomDelta) {
 
13374                 var keys = this._zoomKeys = {},
 
13375                     codes = this.keyCodes,
 
13378                 for (i = 0, len = codes.zoomIn.length; i < len; i++) {
 
13379                         keys[codes.zoomIn[i]] = zoomDelta;
 
13381                 for (i = 0, len = codes.zoomOut.length; i < len; i++) {
 
13382                         keys[codes.zoomOut[i]] = -zoomDelta;
 
13386         _addHooks: function () {
 
13387                 on(document, 'keydown', this._onKeyDown, this);
 
13390         _removeHooks: function () {
 
13391                 off(document, 'keydown', this._onKeyDown, this);
 
13394         _onKeyDown: function (e) {
 
13395                 if (e.altKey || e.ctrlKey || e.metaKey) { return; }
 
13397                 var key = e.keyCode,
 
13401                 if (key in this._panKeys) {
 
13402                         if (!map._panAnim || !map._panAnim._inProgress) {
 
13403                                 offset = this._panKeys[key];
 
13405                                         offset = toPoint(offset).multiplyBy(3);
 
13410                                 if (map.options.maxBounds) {
 
13411                                         map.panInsideBounds(map.options.maxBounds);
 
13414                 } else if (key in this._zoomKeys) {
 
13415                         map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
 
13417                 } else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) {
 
13428 // @section Handlers
 
13429 // @section Handlers
 
13430 // @property keyboard: Handler
 
13431 // Keyboard navigation handler.
 
13432 Map.addInitHook('addHandler', 'keyboard', Keyboard);
 
13435  * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
 
13439 // @section Interaction Options
 
13441         // @section Mousewheel options
 
13442         // @option scrollWheelZoom: Boolean|String = true
 
13443         // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
 
13444         // it will zoom to the center of the view regardless of where the mouse was.
 
13445         scrollWheelZoom: true,
 
13447         // @option wheelDebounceTime: Number = 40
 
13448         // Limits the rate at which a wheel can fire (in milliseconds). By default
 
13449         // user can't zoom via wheel more often than once per 40 ms.
 
13450         wheelDebounceTime: 40,
 
13452         // @option wheelPxPerZoomLevel: Number = 60
 
13453         // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
 
13454         // mean a change of one full zoom level. Smaller values will make wheel-zooming
 
13455         // faster (and vice versa).
 
13456         wheelPxPerZoomLevel: 60
 
13459 var ScrollWheelZoom = Handler.extend({
 
13460         addHooks: function () {
 
13461                 on(this._map._container, 'mousewheel', this._onWheelScroll, this);
 
13466         removeHooks: function () {
 
13467                 off(this._map._container, 'mousewheel', this._onWheelScroll, this);
 
13470         _onWheelScroll: function (e) {
 
13471                 var delta = getWheelDelta(e);
 
13473                 var debounce = this._map.options.wheelDebounceTime;
 
13475                 this._delta += delta;
 
13476                 this._lastMousePos = this._map.mouseEventToContainerPoint(e);
 
13478                 if (!this._startTime) {
 
13479                         this._startTime = +new Date();
 
13482                 var left = Math.max(debounce - (+new Date() - this._startTime), 0);
 
13484                 clearTimeout(this._timer);
 
13485                 this._timer = setTimeout(bind(this._performZoom, this), left);
 
13490         _performZoom: function () {
 
13491                 var map = this._map,
 
13492                     zoom = map.getZoom(),
 
13493                     snap = this._map.options.zoomSnap || 0;
 
13495                 map._stop(); // stop panning and fly animations if any
 
13497                 // map the delta with a sigmoid function to -4..4 range leaning on -1..1
 
13498                 var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
 
13499                     d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
 
13500                     d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
 
13501                     delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
 
13504                 this._startTime = null;
 
13506                 if (!delta) { return; }
 
13508                 if (map.options.scrollWheelZoom === 'center') {
 
13509                         map.setZoom(zoom + delta);
 
13511                         map.setZoomAround(this._lastMousePos, zoom + delta);
 
13516 // @section Handlers
 
13517 // @property scrollWheelZoom: Handler
 
13518 // Scroll wheel zoom handler.
 
13519 Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
 
13522  * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
 
13526 // @section Interaction Options
 
13528         // @section Touch interaction options
 
13529         // @option tap: Boolean = true
 
13530         // Enables mobile hacks for supporting instant taps (fixing 200ms click
 
13531         // delay on iOS/Android) and touch holds (fired as `contextmenu` events).
 
13534         // @option tapTolerance: Number = 15
 
13535         // The max number of pixels a user can shift his finger during touch
 
13536         // for it to be considered a valid tap.
 
13540 var Tap = Handler.extend({
 
13541         addHooks: function () {
 
13542                 on(this._map._container, 'touchstart', this._onDown, this);
 
13545         removeHooks: function () {
 
13546                 off(this._map._container, 'touchstart', this._onDown, this);
 
13549         _onDown: function (e) {
 
13550                 if (!e.touches) { return; }
 
13554                 this._fireClick = true;
 
13556                 // don't simulate click or track longpress if more than 1 touch
 
13557                 if (e.touches.length > 1) {
 
13558                         this._fireClick = false;
 
13559                         clearTimeout(this._holdTimeout);
 
13563                 var first = e.touches[0],
 
13566                 this._startPos = this._newPos = new Point(first.clientX, first.clientY);
 
13568                 // if touching a link, highlight it
 
13569                 if (el.tagName && el.tagName.toLowerCase() === 'a') {
 
13570                         addClass(el, 'leaflet-active');
 
13573                 // simulate long hold but setting a timeout
 
13574                 this._holdTimeout = setTimeout(bind(function () {
 
13575                         if (this._isTapValid()) {
 
13576                                 this._fireClick = false;
 
13578                                 this._simulateEvent('contextmenu', first);
 
13582                 this._simulateEvent('mousedown', first);
 
13585                         touchmove: this._onMove,
 
13586                         touchend: this._onUp
 
13590         _onUp: function (e) {
 
13591                 clearTimeout(this._holdTimeout);
 
13594                         touchmove: this._onMove,
 
13595                         touchend: this._onUp
 
13598                 if (this._fireClick && e && e.changedTouches) {
 
13600                         var first = e.changedTouches[0],
 
13603                         if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
 
13604                                 removeClass(el, 'leaflet-active');
 
13607                         this._simulateEvent('mouseup', first);
 
13609                         // simulate click if the touch didn't move too much
 
13610                         if (this._isTapValid()) {
 
13611                                 this._simulateEvent('click', first);
 
13616         _isTapValid: function () {
 
13617                 return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
 
13620         _onMove: function (e) {
 
13621                 var first = e.touches[0];
 
13622                 this._newPos = new Point(first.clientX, first.clientY);
 
13623                 this._simulateEvent('mousemove', first);
 
13626         _simulateEvent: function (type, e) {
 
13627                 var simulatedEvent = document.createEvent('MouseEvents');
 
13629                 simulatedEvent._simulated = true;
 
13630                 e.target._simulatedClick = true;
 
13632                 simulatedEvent.initMouseEvent(
 
13633                         type, true, true, window, 1,
 
13634                         e.screenX, e.screenY,
 
13635                         e.clientX, e.clientY,
 
13636                         false, false, false, false, 0, null);
 
13638                 e.target.dispatchEvent(simulatedEvent);
 
13642 // @section Handlers
 
13643 // @property tap: Handler
 
13644 // Mobile touch hacks (quick tap and touch hold) handler.
 
13645 if (touch && !pointer) {
 
13646         Map.addInitHook('addHandler', 'tap', Tap);
 
13650  * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
 
13654 // @section Interaction Options
 
13656         // @section Touch interaction options
 
13657         // @option touchZoom: Boolean|String = *
 
13658         // Whether the map can be zoomed by touch-dragging with two fingers. If
 
13659         // passed `'center'`, it will zoom to the center of the view regardless of
 
13660         // where the touch events (fingers) were. Enabled for touch-capable web
 
13661         // browsers except for old Androids.
 
13662         touchZoom: touch && !android23,
 
13664         // @option bounceAtZoomLimits: Boolean = true
 
13665         // Set it to false if you don't want the map to zoom beyond min/max zoom
 
13666         // and then bounce back when pinch-zooming.
 
13667         bounceAtZoomLimits: true
 
13670 var TouchZoom = Handler.extend({
 
13671         addHooks: function () {
 
13672                 addClass(this._map._container, 'leaflet-touch-zoom');
 
13673                 on(this._map._container, 'touchstart', this._onTouchStart, this);
 
13676         removeHooks: function () {
 
13677                 removeClass(this._map._container, 'leaflet-touch-zoom');
 
13678                 off(this._map._container, 'touchstart', this._onTouchStart, this);
 
13681         _onTouchStart: function (e) {
 
13682                 var map = this._map;
 
13683                 if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
 
13685                 var p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
13686                     p2 = map.mouseEventToContainerPoint(e.touches[1]);
 
13688                 this._centerPoint = map.getSize()._divideBy(2);
 
13689                 this._startLatLng = map.containerPointToLatLng(this._centerPoint);
 
13690                 if (map.options.touchZoom !== 'center') {
 
13691                         this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
 
13694                 this._startDist = p1.distanceTo(p2);
 
13695                 this._startZoom = map.getZoom();
 
13697                 this._moved = false;
 
13698                 this._zooming = true;
 
13702                 on(document, 'touchmove', this._onTouchMove, this);
 
13703                 on(document, 'touchend', this._onTouchEnd, this);
 
13708         _onTouchMove: function (e) {
 
13709                 if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
 
13711                 var map = this._map,
 
13712                     p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
13713                     p2 = map.mouseEventToContainerPoint(e.touches[1]),
 
13714                     scale = p1.distanceTo(p2) / this._startDist;
 
13716                 this._zoom = map.getScaleZoom(scale, this._startZoom);
 
13718                 if (!map.options.bounceAtZoomLimits && (
 
13719                         (this._zoom < map.getMinZoom() && scale < 1) ||
 
13720                         (this._zoom > map.getMaxZoom() && scale > 1))) {
 
13721                         this._zoom = map._limitZoom(this._zoom);
 
13724                 if (map.options.touchZoom === 'center') {
 
13725                         this._center = this._startLatLng;
 
13726                         if (scale === 1) { return; }
 
13728                         // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
 
13729                         var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
 
13730                         if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
 
13731                         this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
 
13734                 if (!this._moved) {
 
13735                         map._moveStart(true, false);
 
13736                         this._moved = true;
 
13739                 cancelAnimFrame(this._animRequest);
 
13741                 var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
 
13742                 this._animRequest = requestAnimFrame(moveFn, this, true);
 
13747         _onTouchEnd: function () {
 
13748                 if (!this._moved || !this._zooming) {
 
13749                         this._zooming = false;
 
13753                 this._zooming = false;
 
13754                 cancelAnimFrame(this._animRequest);
 
13756                 off(document, 'touchmove', this._onTouchMove);
 
13757                 off(document, 'touchend', this._onTouchEnd);
 
13759                 // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
 
13760                 if (this._map.options.zoomAnimation) {
 
13761                         this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
 
13763                         this._map._resetView(this._center, this._map._limitZoom(this._zoom));
 
13768 // @section Handlers
 
13769 // @property touchZoom: Handler
 
13770 // Touch zoom handler.
 
13771 Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
 
13773 Map.BoxZoom = BoxZoom;
 
13774 Map.DoubleClickZoom = DoubleClickZoom;
 
13776 Map.Keyboard = Keyboard;
 
13777 Map.ScrollWheelZoom = ScrollWheelZoom;
 
13779 Map.TouchZoom = TouchZoom;
 
13781 Object.freeze = freeze;
 
13783 exports.version = version;
 
13784 exports.Control = Control;
 
13785 exports.control = control;
 
13786 exports.Browser = Browser;
 
13787 exports.Evented = Evented;
 
13788 exports.Mixin = Mixin;
 
13789 exports.Util = Util;
 
13790 exports.Class = Class;
 
13791 exports.Handler = Handler;
 
13792 exports.extend = extend;
 
13793 exports.bind = bind;
 
13794 exports.stamp = stamp;
 
13795 exports.setOptions = setOptions;
 
13796 exports.DomEvent = DomEvent;
 
13797 exports.DomUtil = DomUtil;
 
13798 exports.PosAnimation = PosAnimation;
 
13799 exports.Draggable = Draggable;
 
13800 exports.LineUtil = LineUtil;
 
13801 exports.PolyUtil = PolyUtil;
 
13802 exports.Point = Point;
 
13803 exports.point = toPoint;
 
13804 exports.Bounds = Bounds;
 
13805 exports.bounds = toBounds;
 
13806 exports.Transformation = Transformation;
 
13807 exports.transformation = toTransformation;
 
13808 exports.Projection = index;
 
13809 exports.LatLng = LatLng;
 
13810 exports.latLng = toLatLng;
 
13811 exports.LatLngBounds = LatLngBounds;
 
13812 exports.latLngBounds = toLatLngBounds;
 
13814 exports.GeoJSON = GeoJSON;
 
13815 exports.geoJSON = geoJSON;
 
13816 exports.geoJson = geoJson;
 
13817 exports.Layer = Layer;
 
13818 exports.LayerGroup = LayerGroup;
 
13819 exports.layerGroup = layerGroup;
 
13820 exports.FeatureGroup = FeatureGroup;
 
13821 exports.featureGroup = featureGroup;
 
13822 exports.ImageOverlay = ImageOverlay;
 
13823 exports.imageOverlay = imageOverlay;
 
13824 exports.VideoOverlay = VideoOverlay;
 
13825 exports.videoOverlay = videoOverlay;
 
13826 exports.DivOverlay = DivOverlay;
 
13827 exports.Popup = Popup;
 
13828 exports.popup = popup;
 
13829 exports.Tooltip = Tooltip;
 
13830 exports.tooltip = tooltip;
 
13831 exports.Icon = Icon;
 
13832 exports.icon = icon;
 
13833 exports.DivIcon = DivIcon;
 
13834 exports.divIcon = divIcon;
 
13835 exports.Marker = Marker;
 
13836 exports.marker = marker;
 
13837 exports.TileLayer = TileLayer;
 
13838 exports.tileLayer = tileLayer;
 
13839 exports.GridLayer = GridLayer;
 
13840 exports.gridLayer = gridLayer;
 
13842 exports.svg = svg$1;
 
13843 exports.Renderer = Renderer;
 
13844 exports.Canvas = Canvas;
 
13845 exports.canvas = canvas$1;
 
13846 exports.Path = Path;
 
13847 exports.CircleMarker = CircleMarker;
 
13848 exports.circleMarker = circleMarker;
 
13849 exports.Circle = Circle;
 
13850 exports.circle = circle;
 
13851 exports.Polyline = Polyline;
 
13852 exports.polyline = polyline;
 
13853 exports.Polygon = Polygon;
 
13854 exports.polygon = polygon;
 
13855 exports.Rectangle = Rectangle;
 
13856 exports.rectangle = rectangle;
 
13858 exports.map = createMap;
 
13860 var oldL = window.L;
 
13861 exports.noConflict = function() {
 
13866 // Always export us to window global (see #2364)
 
13867 window.L = exports;
 
13870 //# sourceMappingURL=leaflet-src.js.map