2  Leaflet 1.0.3, a JS library for interactive maps. http://leafletjs.com
 
   3  (c) 2010-2016 Vladimir Agafonkin, (c) 2010-2011 CloudMade
 
   5 (function (window, document, undefined) {
 
  13         L.noConflict = function () {
 
  21 // define Leaflet for Node module pattern loaders, including Browserify
 
  22 if (typeof module === 'object' && typeof module.exports === 'object') {
 
  25 // define Leaflet as an AMD module
 
  26 } else if (typeof define === 'function' && define.amd) {
 
  30 // define Leaflet as a global L variable, saving the original L to restore later if needed
 
  31 if (typeof window !== 'undefined') {
 
  40  * Various utility functions, used by Leaflet internally.
 
  45         // @function extend(dest: Object, src?: Object): Object
 
  46         // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
 
  47         extend: function (dest) {
 
  50                 for (j = 1, len = arguments.length; j < len; j++) {
 
  59         // @function create(proto: Object, properties?: Object): Object
 
  60         // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
 
  61         create: Object.create || (function () {
 
  63                 return function (proto) {
 
  69         // @function bind(fn: Function, …): Function
 
  70         // 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).
 
  71         // Has a `L.bind()` shortcut.
 
  72         bind: function (fn, obj) {
 
  73                 var slice = Array.prototype.slice;
 
  76                         return fn.bind.apply(fn, slice.call(arguments, 1));
 
  79                 var args = slice.call(arguments, 2);
 
  82                         return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
 
  86         // @function stamp(obj: Object): Number
 
  87         // Returns the unique ID of an object, assiging it one if it doesn't have it.
 
  88         stamp: function (obj) {
 
  90                 obj._leaflet_id = obj._leaflet_id || ++L.Util.lastId;
 
  91                 return obj._leaflet_id;
 
  95         // @property lastId: Number
 
  96         // Last unique ID used by [`stamp()`](#util-stamp)
 
  99         // @function throttle(fn: Function, time: Number, context: Object): Function
 
 100         // Returns a function which executes function `fn` with the given scope `context`
 
 101         // (so that the `this` keyword refers to `context` inside `fn`'s code). The function
 
 102         // `fn` will be called no more than one time per given amount of `time`. The arguments
 
 103         // received by the bound function will be any arguments passed when binding the
 
 104         // function, followed by any arguments passed when invoking the bound function.
 
 105         // Has an `L.bind` shortcut.
 
 106         throttle: function (fn, time, context) {
 
 107                 var lock, args, wrapperFn, later;
 
 109                 later = function () {
 
 110                         // reset lock and call if queued
 
 113                                 wrapperFn.apply(context, args);
 
 118                 wrapperFn = function () {
 
 120                                 // called too soon, queue to call later
 
 124                                 // call and lock until later
 
 125                                 fn.apply(context, arguments);
 
 126                                 setTimeout(later, time);
 
 134         // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
 
 135         // Returns the number `num` modulo `range` in such a way so it lies within
 
 136         // `range[0]` and `range[1]`. The returned value will be always smaller than
 
 137         // `range[1]` unless `includeMax` is set to `true`.
 
 138         wrapNum: function (x, range, includeMax) {
 
 142                 return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
 
 145         // @function falseFn(): Function
 
 146         // Returns a function which always returns `false`.
 
 147         falseFn: function () { return false; },
 
 149         // @function formatNum(num: Number, digits?: Number): Number
 
 150         // Returns the number `num` rounded to `digits` decimals, or to 5 decimals by default.
 
 151         formatNum: function (num, digits) {
 
 152                 var pow = Math.pow(10, digits || 5);
 
 153                 return Math.round(num * pow) / pow;
 
 156         // @function trim(str: String): String
 
 157         // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
 
 158         trim: function (str) {
 
 159                 return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
 
 162         // @function splitWords(str: String): String[]
 
 163         // Trims and splits the string on whitespace and returns the array of parts.
 
 164         splitWords: function (str) {
 
 165                 return L.Util.trim(str).split(/\s+/);
 
 168         // @function setOptions(obj: Object, options: Object): Object
 
 169         // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
 
 170         setOptions: function (obj, options) {
 
 171                 if (!obj.hasOwnProperty('options')) {
 
 172                         obj.options = obj.options ? L.Util.create(obj.options) : {};
 
 174                 for (var i in options) {
 
 175                         obj.options[i] = options[i];
 
 180         // @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
 
 181         // Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
 
 182         // translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
 
 183         // be appended at the end. If `uppercase` is `true`, the parameter names will
 
 184         // be uppercased (e.g. `'?A=foo&B=bar'`)
 
 185         getParamString: function (obj, existingUrl, uppercase) {
 
 188                         params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
 
 190                 return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
 
 193         // @function template(str: String, data: Object): String
 
 194         // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
 
 195         // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
 
 196         // `('Hello foo, bar')`. You can also specify functions instead of strings for
 
 197         // data values — they will be evaluated passing `data` as an argument.
 
 198         template: function (str, data) {
 
 199                 return str.replace(L.Util.templateRe, function (str, key) {
 
 200                         var value = data[key];
 
 202                         if (value === undefined) {
 
 203                                 throw new Error('No value provided for variable ' + str);
 
 205                         } else if (typeof value === 'function') {
 
 212         templateRe: /\{ *([\w_\-]+) *\}/g,
 
 214         // @function isArray(obj): Boolean
 
 215         // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
 
 216         isArray: Array.isArray || function (obj) {
 
 217                 return (Object.prototype.toString.call(obj) === '[object Array]');
 
 220         // @function indexOf(array: Array, el: Object): Number
 
 221         // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
 
 222         indexOf: function (array, el) {
 
 223                 for (var i = 0; i < array.length; i++) {
 
 224                         if (array[i] === el) { return i; }
 
 229         // @property emptyImageUrl: String
 
 230         // Data URI string containing a base64-encoded empty GIF image.
 
 231         // Used as a hack to free memory from unused images on WebKit-powered
 
 232         // mobile devices (by setting image `src` to this string).
 
 233         emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='
 
 237         // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
 
 239         function getPrefixed(name) {
 
 240                 return window['webkit' + name] || window['moz' + name] || window['ms' + name];
 
 245         // fallback for IE 7-8
 
 246         function timeoutDefer(fn) {
 
 247                 var time = +new Date(),
 
 248                     timeToCall = Math.max(0, 16 - (time - lastTime));
 
 250                 lastTime = time + timeToCall;
 
 251                 return window.setTimeout(fn, timeToCall);
 
 254         var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer,
 
 255             cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
 
 256                        getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
 
 259         // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
 
 260         // Schedules `fn` to be executed when the browser repaints. `fn` is bound to
 
 261         // `context` if given. When `immediate` is set, `fn` is called immediately if
 
 262         // the browser doesn't have native support for
 
 263         // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
 
 264         // otherwise it's delayed. Returns a request ID that can be used to cancel the request.
 
 265         L.Util.requestAnimFrame = function (fn, context, immediate) {
 
 266                 if (immediate && requestFn === timeoutDefer) {
 
 269                         return requestFn.call(window, L.bind(fn, context));
 
 273         // @function cancelAnimFrame(id: Number): undefined
 
 274         // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
 
 275         L.Util.cancelAnimFrame = function (id) {
 
 277                         cancelFn.call(window, id);
 
 282 // shortcuts for most used utility functions
 
 283 L.extend = L.Util.extend;
 
 284 L.bind = L.Util.bind;
 
 285 L.stamp = L.Util.stamp;
 
 286 L.setOptions = L.Util.setOptions;
 
 297 // Thanks to John Resig and Dean Edwards for inspiration!
 
 299 L.Class = function () {};
 
 301 L.Class.extend = function (props) {
 
 303         // @function extend(props: Object): Function
 
 304         // [Extends the current class](#class-inheritance) given the properties to be included.
 
 305         // Returns a Javascript function that is a class constructor (to be called with `new`).
 
 306         var NewClass = function () {
 
 308                 // call the constructor
 
 309                 if (this.initialize) {
 
 310                         this.initialize.apply(this, arguments);
 
 313                 // call all constructor hooks
 
 314                 this.callInitHooks();
 
 317         var parentProto = NewClass.__super__ = this.prototype;
 
 319         var proto = L.Util.create(parentProto);
 
 320         proto.constructor = NewClass;
 
 322         NewClass.prototype = proto;
 
 324         // inherit parent's statics
 
 325         for (var i in this) {
 
 326                 if (this.hasOwnProperty(i) && i !== 'prototype') {
 
 327                         NewClass[i] = this[i];
 
 331         // mix static properties into the class
 
 333                 L.extend(NewClass, props.statics);
 
 334                 delete props.statics;
 
 337         // mix includes into the prototype
 
 338         if (props.includes) {
 
 339                 L.Util.extend.apply(null, [proto].concat(props.includes));
 
 340                 delete props.includes;
 
 345                 props.options = L.Util.extend(L.Util.create(proto.options), props.options);
 
 348         // mix given properties into the prototype
 
 349         L.extend(proto, props);
 
 351         proto._initHooks = [];
 
 353         // add method for calling all hooks
 
 354         proto.callInitHooks = function () {
 
 356                 if (this._initHooksCalled) { return; }
 
 358                 if (parentProto.callInitHooks) {
 
 359                         parentProto.callInitHooks.call(this);
 
 362                 this._initHooksCalled = true;
 
 364                 for (var i = 0, len = proto._initHooks.length; i < len; i++) {
 
 365                         proto._initHooks[i].call(this);
 
 373 // @function include(properties: Object): this
 
 374 // [Includes a mixin](#class-includes) into the current class.
 
 375 L.Class.include = function (props) {
 
 376         L.extend(this.prototype, props);
 
 380 // @function mergeOptions(options: Object): this
 
 381 // [Merges `options`](#class-options) into the defaults of the class.
 
 382 L.Class.mergeOptions = function (options) {
 
 383         L.extend(this.prototype.options, options);
 
 387 // @function addInitHook(fn: Function): this
 
 388 // Adds a [constructor hook](#class-constructor-hooks) to the class.
 
 389 L.Class.addInitHook = function (fn) { // (Function) || (String, args...)
 
 390         var args = Array.prototype.slice.call(arguments, 1);
 
 392         var init = typeof fn === 'function' ? fn : function () {
 
 393                 this[fn].apply(this, args);
 
 396         this.prototype._initHooks = this.prototype._initHooks || [];
 
 397         this.prototype._initHooks.push(init);
 
 408  * 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).
 
 413  * map.on('click', function(e) {
 
 418  * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
 
 421  * function onClick(e) { ... }
 
 423  * map.on('click', onClick);
 
 424  * map.off('click', onClick);
 
 429 L.Evented = L.Class.extend({
 
 431         /* @method on(type: String, fn: Function, context?: Object): this
 
 432          * 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'`).
 
 435          * @method on(eventMap: Object): this
 
 436          * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
 438         on: function (types, fn, context) {
 
 440                 // types can be a map of types/handlers
 
 441                 if (typeof types === 'object') {
 
 442                         for (var type in types) {
 
 443                                 // we don't process space-separated events here for performance;
 
 444                                 // it's a hot path since Layer uses the on(obj) syntax
 
 445                                 this._on(type, types[type], fn);
 
 449                         // types can be a string of space-separated words
 
 450                         types = L.Util.splitWords(types);
 
 452                         for (var i = 0, len = types.length; i < len; i++) {
 
 453                                 this._on(types[i], fn, context);
 
 460         /* @method off(type: String, fn?: Function, context?: Object): this
 
 461          * 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.
 
 464          * @method off(eventMap: Object): this
 
 465          * Removes a set of type/listener pairs.
 
 469          * Removes all listeners to all events on the object.
 
 471         off: function (types, fn, context) {
 
 474                         // clear all listeners if called without arguments
 
 477                 } else if (typeof types === 'object') {
 
 478                         for (var type in types) {
 
 479                                 this._off(type, types[type], fn);
 
 483                         types = L.Util.splitWords(types);
 
 485                         for (var i = 0, len = types.length; i < len; i++) {
 
 486                                 this._off(types[i], fn, context);
 
 493         // attach listener (without syntactic sugar now)
 
 494         _on: function (type, fn, context) {
 
 495                 this._events = this._events || {};
 
 497                 /* get/init listeners for type */
 
 498                 var typeListeners = this._events[type];
 
 499                 if (!typeListeners) {
 
 501                         this._events[type] = typeListeners;
 
 504                 if (context === this) {
 
 505                         // Less memory footprint.
 
 508                 var newListener = {fn: fn, ctx: context},
 
 509                     listeners = typeListeners;
 
 511                 // check if fn already there
 
 512                 for (var i = 0, len = listeners.length; i < len; i++) {
 
 513                         if (listeners[i].fn === fn && listeners[i].ctx === context) {
 
 518                 listeners.push(newListener);
 
 521         _off: function (type, fn, context) {
 
 526                 if (!this._events) { return; }
 
 528                 listeners = this._events[type];
 
 535                         // Set all removed listeners to noop so they are not called if remove happens in fire
 
 536                         for (i = 0, len = listeners.length; i < len; i++) {
 
 537                                 listeners[i].fn = L.Util.falseFn;
 
 539                         // clear all listeners for a type if function isn't specified
 
 540                         delete this._events[type];
 
 544                 if (context === this) {
 
 550                         // find fn and remove it
 
 551                         for (i = 0, len = listeners.length; i < len; i++) {
 
 552                                 var l = listeners[i];
 
 553                                 if (l.ctx !== context) { continue; }
 
 556                                         // set the removed listener to noop so that's not called if remove happens in fire
 
 557                                         l.fn = L.Util.falseFn;
 
 559                                         if (this._firingCount) {
 
 560                                                 /* copy array in case events are being fired */
 
 561                                                 this._events[type] = listeners = listeners.slice();
 
 563                                         listeners.splice(i, 1);
 
 571         // @method fire(type: String, data?: Object, propagate?: Boolean): this
 
 572         // Fires an event of the specified type. You can optionally provide an data
 
 573         // object — the first argument of the listener function will contain its
 
 574         // properties. The event can optionally be propagated to event parents.
 
 575         fire: function (type, data, propagate) {
 
 576                 if (!this.listens(type, propagate)) { return this; }
 
 578                 var event = L.Util.extend({}, data, {type: type, target: this});
 
 581                         var listeners = this._events[type];
 
 584                                 this._firingCount = (this._firingCount + 1) || 1;
 
 585                                 for (var i = 0, len = listeners.length; i < len; i++) {
 
 586                                         var l = listeners[i];
 
 587                                         l.fn.call(l.ctx || this, event);
 
 595                         // propagate the event to parents (set with addEventParent)
 
 596                         this._propagateEvent(event);
 
 602         // @method listens(type: String): Boolean
 
 603         // Returns `true` if a particular event type has any listeners attached to it.
 
 604         listens: function (type, propagate) {
 
 605                 var listeners = this._events && this._events[type];
 
 606                 if (listeners && listeners.length) { return true; }
 
 609                         // also check parents for listeners if event propagates
 
 610                         for (var id in this._eventParents) {
 
 611                                 if (this._eventParents[id].listens(type, propagate)) { return true; }
 
 617         // @method once(…): this
 
 618         // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
 
 619         once: function (types, fn, context) {
 
 621                 if (typeof types === 'object') {
 
 622                         for (var type in types) {
 
 623                                 this.once(type, types[type], fn);
 
 628                 var handler = L.bind(function () {
 
 630                             .off(types, fn, context)
 
 631                             .off(types, handler, context);
 
 634                 // add a listener that's executed once and removed after that
 
 636                     .on(types, fn, context)
 
 637                     .on(types, handler, context);
 
 640         // @method addEventParent(obj: Evented): this
 
 641         // Adds an event parent - an `Evented` that will receive propagated events
 
 642         addEventParent: function (obj) {
 
 643                 this._eventParents = this._eventParents || {};
 
 644                 this._eventParents[L.stamp(obj)] = obj;
 
 648         // @method removeEventParent(obj: Evented): this
 
 649         // Removes an event parent, so it will stop receiving propagated events
 
 650         removeEventParent: function (obj) {
 
 651                 if (this._eventParents) {
 
 652                         delete this._eventParents[L.stamp(obj)];
 
 657         _propagateEvent: function (e) {
 
 658                 for (var id in this._eventParents) {
 
 659                         this._eventParents[id].fire(e.type, L.extend({layer: e.target}, e), true);
 
 664 var proto = L.Evented.prototype;
 
 666 // aliases; we should ditch those eventually
 
 668 // @method addEventListener(…): this
 
 669 // Alias to [`on(…)`](#evented-on)
 
 670 proto.addEventListener = proto.on;
 
 672 // @method removeEventListener(…): this
 
 673 // Alias to [`off(…)`](#evented-off)
 
 675 // @method clearAllEventListeners(…): this
 
 676 // Alias to [`off()`](#evented-off)
 
 677 proto.removeEventListener = proto.clearAllEventListeners = proto.off;
 
 679 // @method addOneTimeEventListener(…): this
 
 680 // Alias to [`once(…)`](#evented-once)
 
 681 proto.addOneTimeEventListener = proto.once;
 
 683 // @method fireEvent(…): this
 
 684 // Alias to [`fire(…)`](#evented-fire)
 
 685 proto.fireEvent = proto.fire;
 
 687 // @method hasEventListeners(…): Boolean
 
 688 // Alias to [`listens(…)`](#evented-listens)
 
 689 proto.hasEventListeners = proto.listens;
 
 691 L.Mixin = {Events: proto};
 
 699  * A namespace with static properties for browser/feature detection used by Leaflet internally.
 
 704  * if (L.Browser.ielt9) {
 
 705  *   alert('Upgrade your browser, dude!');
 
 712         var ua = navigator.userAgent.toLowerCase(),
 
 713             doc = document.documentElement,
 
 715             ie = 'ActiveXObject' in window,
 
 717             webkit    = ua.indexOf('webkit') !== -1,
 
 718             phantomjs = ua.indexOf('phantom') !== -1,
 
 719             android23 = ua.search('android [23]') !== -1,
 
 720             chrome    = ua.indexOf('chrome') !== -1,
 
 721             gecko     = ua.indexOf('gecko') !== -1  && !webkit && !window.opera && !ie,
 
 723             win = navigator.platform.indexOf('Win') === 0,
 
 725             mobile = typeof orientation !== 'undefined' || ua.indexOf('mobile') !== -1,
 
 726             msPointer = !window.PointerEvent && window.MSPointerEvent,
 
 727             pointer = window.PointerEvent || msPointer,
 
 729             ie3d = ie && ('transition' in doc.style),
 
 730             webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23,
 
 731             gecko3d = 'MozPerspective' in doc.style,
 
 732             opera12 = 'OTransition' in doc.style;
 
 735         var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
 
 736                         (window.DocumentTouch && document instanceof window.DocumentTouch));
 
 740                 // @property ie: Boolean
 
 741                 // `true` for all Internet Explorer versions (not Edge).
 
 744                 // @property ielt9: Boolean
 
 745                 // `true` for Internet Explorer versions less than 9.
 
 746                 ielt9: ie && !document.addEventListener,
 
 748                 // @property edge: Boolean
 
 749                 // `true` for the Edge web browser.
 
 750                 edge: 'msLaunchUri' in navigator && !('documentMode' in document),
 
 752                 // @property webkit: Boolean
 
 753                 // `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
 
 756                 // @property gecko: Boolean
 
 757                 // `true` for gecko-based browsers like Firefox.
 
 760                 // @property android: Boolean
 
 761                 // `true` for any browser running on an Android platform.
 
 762                 android: ua.indexOf('android') !== -1,
 
 764                 // @property android23: Boolean
 
 765                 // `true` for browsers running on Android 2 or Android 3.
 
 766                 android23: android23,
 
 768                 // @property chrome: Boolean
 
 769                 // `true` for the Chrome browser.
 
 772                 // @property safari: Boolean
 
 773                 // `true` for the Safari browser.
 
 774                 safari: !chrome && ua.indexOf('safari') !== -1,
 
 777                 // @property win: Boolean
 
 778                 // `true` when the browser is running in a Windows platform
 
 782                 // @property ie3d: Boolean
 
 783                 // `true` for all Internet Explorer versions supporting CSS transforms.
 
 786                 // @property webkit3d: Boolean
 
 787                 // `true` for webkit-based browsers supporting CSS transforms.
 
 790                 // @property gecko3d: Boolean
 
 791                 // `true` for gecko-based browsers supporting CSS transforms.
 
 794                 // @property opera12: Boolean
 
 795                 // `true` for the Opera browser supporting CSS transforms (version 12 or later).
 
 798                 // @property any3d: Boolean
 
 799                 // `true` for all browsers supporting CSS transforms.
 
 800                 any3d: !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantomjs,
 
 803                 // @property mobile: Boolean
 
 804                 // `true` for all browsers running in a mobile device.
 
 807                 // @property mobileWebkit: Boolean
 
 808                 // `true` for all webkit-based browsers in a mobile device.
 
 809                 mobileWebkit: mobile && webkit,
 
 811                 // @property mobileWebkit3d: Boolean
 
 812                 // `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
 
 813                 mobileWebkit3d: mobile && webkit3d,
 
 815                 // @property mobileOpera: Boolean
 
 816                 // `true` for the Opera browser in a mobile device.
 
 817                 mobileOpera: mobile && window.opera,
 
 819                 // @property mobileGecko: Boolean
 
 820                 // `true` for gecko-based browsers running in a mobile device.
 
 821                 mobileGecko: mobile && gecko,
 
 824                 // @property touch: Boolean
 
 825                 // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
 
 826                 // This does not necessarily mean that the browser is running in a computer with
 
 827                 // a touchscreen, it only means that the browser is capable of understanding
 
 831                 // @property msPointer: Boolean
 
 832                 // `true` for browsers implementing the Microsoft touch events model (notably IE10).
 
 833                 msPointer: !!msPointer,
 
 835                 // @property pointer: Boolean
 
 836                 // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
 
 840                 // @property retina: Boolean
 
 841                 // `true` for browsers on a high-resolution "retina" screen.
 
 842                 retina: (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1
 
 853  * Represents a point with `x` and `y` coordinates in pixels.
 
 858  * var point = L.point(200, 300);
 
 861  * 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:
 
 864  * map.panBy([200, 300]);
 
 865  * map.panBy(L.point(200, 300));
 
 869 L.Point = function (x, y, round) {
 
 870         // @property x: Number; The `x` coordinate of the point
 
 871         this.x = (round ? Math.round(x) : x);
 
 872         // @property y: Number; The `y` coordinate of the point
 
 873         this.y = (round ? Math.round(y) : y);
 
 876 L.Point.prototype = {
 
 878         // @method clone(): Point
 
 879         // Returns a copy of the current point.
 
 881                 return new L.Point(this.x, this.y);
 
 884         // @method add(otherPoint: Point): Point
 
 885         // Returns the result of addition of the current and the given points.
 
 886         add: function (point) {
 
 887                 // non-destructive, returns a new point
 
 888                 return this.clone()._add(L.point(point));
 
 891         _add: function (point) {
 
 892                 // destructive, used directly for performance in situations where it's safe to modify existing point
 
 898         // @method subtract(otherPoint: Point): Point
 
 899         // Returns the result of subtraction of the given point from the current.
 
 900         subtract: function (point) {
 
 901                 return this.clone()._subtract(L.point(point));
 
 904         _subtract: function (point) {
 
 910         // @method divideBy(num: Number): Point
 
 911         // Returns the result of division of the current point by the given number.
 
 912         divideBy: function (num) {
 
 913                 return this.clone()._divideBy(num);
 
 916         _divideBy: function (num) {
 
 922         // @method multiplyBy(num: Number): Point
 
 923         // Returns the result of multiplication of the current point by the given number.
 
 924         multiplyBy: function (num) {
 
 925                 return this.clone()._multiplyBy(num);
 
 928         _multiplyBy: function (num) {
 
 934         // @method scaleBy(scale: Point): Point
 
 935         // Multiply each coordinate of the current point by each coordinate of
 
 936         // `scale`. In linear algebra terms, multiply the point by the
 
 937         // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
 
 938         // defined by `scale`.
 
 939         scaleBy: function (point) {
 
 940                 return new L.Point(this.x * point.x, this.y * point.y);
 
 943         // @method unscaleBy(scale: Point): Point
 
 944         // Inverse of `scaleBy`. Divide each coordinate of the current point by
 
 945         // each coordinate of `scale`.
 
 946         unscaleBy: function (point) {
 
 947                 return new L.Point(this.x / point.x, this.y / point.y);
 
 950         // @method round(): Point
 
 951         // Returns a copy of the current point with rounded coordinates.
 
 953                 return this.clone()._round();
 
 956         _round: function () {
 
 957                 this.x = Math.round(this.x);
 
 958                 this.y = Math.round(this.y);
 
 962         // @method floor(): Point
 
 963         // Returns a copy of the current point with floored coordinates (rounded down).
 
 965                 return this.clone()._floor();
 
 968         _floor: function () {
 
 969                 this.x = Math.floor(this.x);
 
 970                 this.y = Math.floor(this.y);
 
 974         // @method ceil(): Point
 
 975         // Returns a copy of the current point with ceiled coordinates (rounded up).
 
 977                 return this.clone()._ceil();
 
 981                 this.x = Math.ceil(this.x);
 
 982                 this.y = Math.ceil(this.y);
 
 986         // @method distanceTo(otherPoint: Point): Number
 
 987         // Returns the cartesian distance between the current and the given points.
 
 988         distanceTo: function (point) {
 
 989                 point = L.point(point);
 
 991                 var x = point.x - this.x,
 
 992                     y = point.y - this.y;
 
 994                 return Math.sqrt(x * x + y * y);
 
 997         // @method equals(otherPoint: Point): Boolean
 
 998         // Returns `true` if the given point has the same coordinates.
 
 999         equals: function (point) {
 
1000                 point = L.point(point);
 
1002                 return point.x === this.x &&
 
1006         // @method contains(otherPoint: Point): Boolean
 
1007         // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
 
1008         contains: function (point) {
 
1009                 point = L.point(point);
 
1011                 return Math.abs(point.x) <= Math.abs(this.x) &&
 
1012                        Math.abs(point.y) <= Math.abs(this.y);
 
1015         // @method toString(): String
 
1016         // Returns a string representation of the point for debugging purposes.
 
1017         toString: function () {
 
1019                         L.Util.formatNum(this.x) + ', ' +
 
1020                         L.Util.formatNum(this.y) + ')';
 
1024 // @factory L.point(x: Number, y: Number, round?: Boolean)
 
1025 // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
 
1028 // @factory L.point(coords: Number[])
 
1029 // Expects an array of the form `[x, y]` instead.
 
1032 // @factory L.point(coords: Object)
 
1033 // Expects a plain object of the form `{x: Number, y: Number}` instead.
 
1034 L.point = function (x, y, round) {
 
1035         if (x instanceof L.Point) {
 
1038         if (L.Util.isArray(x)) {
 
1039                 return new L.Point(x[0], x[1]);
 
1041         if (x === undefined || x === null) {
 
1044         if (typeof x === 'object' && 'x' in x && 'y' in x) {
 
1045                 return new L.Point(x.x, x.y);
 
1047         return new L.Point(x, y, round);
 
1056  * Represents a rectangular area in pixel coordinates.
 
1061  * var p1 = L.point(10, 10),
 
1062  * p2 = L.point(40, 60),
 
1063  * bounds = L.bounds(p1, p2);
 
1066  * 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:
 
1069  * otherBounds.intersects([[10, 10], [40, 60]]);
 
1073 L.Bounds = function (a, b) {
 
1076         var points = b ? [a, b] : a;
 
1078         for (var i = 0, len = points.length; i < len; i++) {
 
1079                 this.extend(points[i]);
 
1083 L.Bounds.prototype = {
 
1084         // @method extend(point: Point): this
 
1085         // Extends the bounds to contain the given point.
 
1086         extend: function (point) { // (Point)
 
1087                 point = L.point(point);
 
1089                 // @property min: Point
 
1090                 // The top left corner of the rectangle.
 
1091                 // @property max: Point
 
1092                 // The bottom right corner of the rectangle.
 
1093                 if (!this.min && !this.max) {
 
1094                         this.min = point.clone();
 
1095                         this.max = point.clone();
 
1097                         this.min.x = Math.min(point.x, this.min.x);
 
1098                         this.max.x = Math.max(point.x, this.max.x);
 
1099                         this.min.y = Math.min(point.y, this.min.y);
 
1100                         this.max.y = Math.max(point.y, this.max.y);
 
1105         // @method getCenter(round?: Boolean): Point
 
1106         // Returns the center point of the bounds.
 
1107         getCenter: function (round) {
 
1109                         (this.min.x + this.max.x) / 2,
 
1110                         (this.min.y + this.max.y) / 2, round);
 
1113         // @method getBottomLeft(): Point
 
1114         // Returns the bottom-left point of the bounds.
 
1115         getBottomLeft: function () {
 
1116                 return new L.Point(this.min.x, this.max.y);
 
1119         // @method getTopRight(): Point
 
1120         // Returns the top-right point of the bounds.
 
1121         getTopRight: function () { // -> Point
 
1122                 return new L.Point(this.max.x, this.min.y);
 
1125         // @method getSize(): Point
 
1126         // Returns the size of the given bounds
 
1127         getSize: function () {
 
1128                 return this.max.subtract(this.min);
 
1131         // @method contains(otherBounds: Bounds): Boolean
 
1132         // Returns `true` if the rectangle contains the given one.
 
1134         // @method contains(point: Point): Boolean
 
1135         // Returns `true` if the rectangle contains the given point.
 
1136         contains: function (obj) {
 
1139                 if (typeof obj[0] === 'number' || obj instanceof L.Point) {
 
1142                         obj = L.bounds(obj);
 
1145                 if (obj instanceof L.Bounds) {
 
1152                 return (min.x >= this.min.x) &&
 
1153                        (max.x <= this.max.x) &&
 
1154                        (min.y >= this.min.y) &&
 
1155                        (max.y <= this.max.y);
 
1158         // @method intersects(otherBounds: Bounds): Boolean
 
1159         // Returns `true` if the rectangle intersects the given bounds. Two bounds
 
1160         // intersect if they have at least one point in common.
 
1161         intersects: function (bounds) { // (Bounds) -> Boolean
 
1162                 bounds = L.bounds(bounds);
 
1168                     xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
 
1169                     yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
 
1171                 return xIntersects && yIntersects;
 
1174         // @method overlaps(otherBounds: Bounds): Boolean
 
1175         // Returns `true` if the rectangle overlaps the given bounds. Two bounds
 
1176         // overlap if their intersection is an area.
 
1177         overlaps: function (bounds) { // (Bounds) -> Boolean
 
1178                 bounds = L.bounds(bounds);
 
1184                     xOverlaps = (max2.x > min.x) && (min2.x < max.x),
 
1185                     yOverlaps = (max2.y > min.y) && (min2.y < max.y);
 
1187                 return xOverlaps && yOverlaps;
 
1190         isValid: function () {
 
1191                 return !!(this.min && this.max);
 
1196 // @factory L.bounds(topLeft: Point, bottomRight: Point)
 
1197 // Creates a Bounds object from two coordinates (usually top-left and bottom-right corners).
 
1199 // @factory L.bounds(points: Point[])
 
1200 // Creates a Bounds object from the points it contains
 
1201 L.bounds = function (a, b) {
 
1202         if (!a || a instanceof L.Bounds) {
 
1205         return new L.Bounds(a, b);
 
1211  * @class Transformation
 
1212  * @aka L.Transformation
 
1214  * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
 
1215  * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
 
1216  * the reverse. Used by Leaflet in its projections code.
 
1221  * var transformation = new L.Transformation(2, 5, -1, 10),
 
1222  *      p = L.point(1, 2),
 
1223  *      p2 = transformation.transform(p), //  L.point(7, 8)
 
1224  *      p3 = transformation.untransform(p2); //  L.point(1, 2)
 
1229 // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
 
1230 // Creates a `Transformation` object with the given coefficients.
 
1231 L.Transformation = function (a, b, c, d) {
 
1238 L.Transformation.prototype = {
 
1239         // @method transform(point: Point, scale?: Number): Point
 
1240         // Returns a transformed point, optionally multiplied by the given scale.
 
1241         // Only accepts actual `L.Point` instances, not arrays.
 
1242         transform: function (point, scale) { // (Point, Number) -> Point
 
1243                 return this._transform(point.clone(), scale);
 
1246         // destructive transform (faster)
 
1247         _transform: function (point, scale) {
 
1249                 point.x = scale * (this._a * point.x + this._b);
 
1250                 point.y = scale * (this._c * point.y + this._d);
 
1254         // @method untransform(point: Point, scale?: Number): Point
 
1255         // Returns the reverse transformation of the given point, optionally divided
 
1256         // by the given scale. Only accepts actual `L.Point` instances, not arrays.
 
1257         untransform: function (point, scale) {
 
1260                         (point.x / scale - this._b) / this._a,
 
1261                         (point.y / scale - this._d) / this._c);
 
1268  * @namespace DomUtil
 
1270  * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
 
1271  * tree, used by Leaflet internally.
 
1273  * Most functions expecting or returning a `HTMLElement` also work for
 
1274  * SVG elements. The only difference is that classes refer to CSS classes
 
1275  * in HTML and SVG classes in SVG.
 
1280         // @function get(id: String|HTMLElement): HTMLElement
 
1281         // Returns an element given its DOM id, or returns the element itself
 
1282         // if it was passed directly.
 
1283         get: function (id) {
 
1284                 return typeof id === 'string' ? document.getElementById(id) : id;
 
1287         // @function getStyle(el: HTMLElement, styleAttrib: String): String
 
1288         // Returns the value for a certain style attribute on an element,
 
1289         // including computed values or values set through CSS.
 
1290         getStyle: function (el, style) {
 
1292                 var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
 
1294                 if ((!value || value === 'auto') && document.defaultView) {
 
1295                         var css = document.defaultView.getComputedStyle(el, null);
 
1296                         value = css ? css[style] : null;
 
1299                 return value === 'auto' ? null : value;
 
1302         // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
 
1303         // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
 
1304         create: function (tagName, className, container) {
 
1306                 var el = document.createElement(tagName);
 
1307                 el.className = className || '';
 
1310                         container.appendChild(el);
 
1316         // @function remove(el: HTMLElement)
 
1317         // Removes `el` from its parent element
 
1318         remove: function (el) {
 
1319                 var parent = el.parentNode;
 
1321                         parent.removeChild(el);
 
1325         // @function empty(el: HTMLElement)
 
1326         // Removes all of `el`'s children elements from `el`
 
1327         empty: function (el) {
 
1328                 while (el.firstChild) {
 
1329                         el.removeChild(el.firstChild);
 
1333         // @function toFront(el: HTMLElement)
 
1334         // Makes `el` the last children of its parent, so it renders in front of the other children.
 
1335         toFront: function (el) {
 
1336                 el.parentNode.appendChild(el);
 
1339         // @function toBack(el: HTMLElement)
 
1340         // Makes `el` the first children of its parent, so it renders back from the other children.
 
1341         toBack: function (el) {
 
1342                 var parent = el.parentNode;
 
1343                 parent.insertBefore(el, parent.firstChild);
 
1346         // @function hasClass(el: HTMLElement, name: String): Boolean
 
1347         // Returns `true` if the element's class attribute contains `name`.
 
1348         hasClass: function (el, name) {
 
1349                 if (el.classList !== undefined) {
 
1350                         return el.classList.contains(name);
 
1352                 var className = L.DomUtil.getClass(el);
 
1353                 return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
 
1356         // @function addClass(el: HTMLElement, name: String)
 
1357         // Adds `name` to the element's class attribute.
 
1358         addClass: function (el, name) {
 
1359                 if (el.classList !== undefined) {
 
1360                         var classes = L.Util.splitWords(name);
 
1361                         for (var i = 0, len = classes.length; i < len; i++) {
 
1362                                 el.classList.add(classes[i]);
 
1364                 } else if (!L.DomUtil.hasClass(el, name)) {
 
1365                         var className = L.DomUtil.getClass(el);
 
1366                         L.DomUtil.setClass(el, (className ? className + ' ' : '') + name);
 
1370         // @function removeClass(el: HTMLElement, name: String)
 
1371         // Removes `name` from the element's class attribute.
 
1372         removeClass: function (el, name) {
 
1373                 if (el.classList !== undefined) {
 
1374                         el.classList.remove(name);
 
1376                         L.DomUtil.setClass(el, L.Util.trim((' ' + L.DomUtil.getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
 
1380         // @function setClass(el: HTMLElement, name: String)
 
1381         // Sets the element's class.
 
1382         setClass: function (el, name) {
 
1383                 if (el.className.baseVal === undefined) {
 
1384                         el.className = name;
 
1386                         // in case of SVG element
 
1387                         el.className.baseVal = name;
 
1391         // @function getClass(el: HTMLElement): String
 
1392         // Returns the element's class.
 
1393         getClass: function (el) {
 
1394                 return el.className.baseVal === undefined ? el.className : el.className.baseVal;
 
1397         // @function setOpacity(el: HTMLElement, opacity: Number)
 
1398         // Set the opacity of an element (including old IE support).
 
1399         // `opacity` must be a number from `0` to `1`.
 
1400         setOpacity: function (el, value) {
 
1402                 if ('opacity' in el.style) {
 
1403                         el.style.opacity = value;
 
1405                 } else if ('filter' in el.style) {
 
1406                         L.DomUtil._setOpacityIE(el, value);
 
1410         _setOpacityIE: function (el, value) {
 
1412                     filterName = 'DXImageTransform.Microsoft.Alpha';
 
1414                 // filters collection throws an error if we try to retrieve a filter that doesn't exist
 
1416                         filter = el.filters.item(filterName);
 
1418                         // don't set opacity to 1 if we haven't already set an opacity,
 
1419                         // it isn't needed and breaks transparent pngs.
 
1420                         if (value === 1) { return; }
 
1423                 value = Math.round(value * 100);
 
1426                         filter.Enabled = (value !== 100);
 
1427                         filter.Opacity = value;
 
1429                         el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
 
1433         // @function testProp(props: String[]): String|false
 
1434         // Goes through the array of style names and returns the first name
 
1435         // that is a valid style name for an element. If no such name is found,
 
1436         // it returns false. Useful for vendor-prefixed styles like `transform`.
 
1437         testProp: function (props) {
 
1439                 var style = document.documentElement.style;
 
1441                 for (var i = 0; i < props.length; i++) {
 
1442                         if (props[i] in style) {
 
1449         // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
 
1450         // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
 
1451         // and optionally scaled by `scale`. Does not have an effect if the
 
1452         // browser doesn't support 3D CSS transforms.
 
1453         setTransform: function (el, offset, scale) {
 
1454                 var pos = offset || new L.Point(0, 0);
 
1456                 el.style[L.DomUtil.TRANSFORM] =
 
1458                                 'translate(' + pos.x + 'px,' + pos.y + 'px)' :
 
1459                                 'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
 
1460                         (scale ? ' scale(' + scale + ')' : '');
 
1463         // @function setPosition(el: HTMLElement, position: Point)
 
1464         // Sets the position of `el` to coordinates specified by `position`,
 
1465         // using CSS translate or top/left positioning depending on the browser
 
1466         // (used by Leaflet internally to position its layers).
 
1467         setPosition: function (el, point) { // (HTMLElement, Point[, Boolean])
 
1470                 el._leaflet_pos = point;
 
1473                 if (L.Browser.any3d) {
 
1474                         L.DomUtil.setTransform(el, point);
 
1476                         el.style.left = point.x + 'px';
 
1477                         el.style.top = point.y + 'px';
 
1481         // @function getPosition(el: HTMLElement): Point
 
1482         // Returns the coordinates of an element previously positioned with setPosition.
 
1483         getPosition: function (el) {
 
1484                 // this method is only used for elements previously positioned using setPosition,
 
1485                 // so it's safe to cache the position for performance
 
1487                 return el._leaflet_pos || new L.Point(0, 0);
 
1493         // prefix style property names
 
1495         // @property TRANSFORM: String
 
1496         // Vendor-prefixed fransform style name (e.g. `'webkitTransform'` for WebKit).
 
1497         L.DomUtil.TRANSFORM = L.DomUtil.testProp(
 
1498                         ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
 
1501         // webkitTransition comes first because some browser versions that drop vendor prefix don't do
 
1502         // the same for the transitionend event, in particular the Android 4.1 stock browser
 
1504         // @property TRANSITION: String
 
1505         // Vendor-prefixed transform style name.
 
1506         var transition = L.DomUtil.TRANSITION = L.DomUtil.testProp(
 
1507                         ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
 
1509         L.DomUtil.TRANSITION_END =
 
1510                         transition === 'webkitTransition' || transition === 'OTransition' ? transition + 'End' : 'transitionend';
 
1512         // @function disableTextSelection()
 
1513         // Prevents the user from generating `selectstart` DOM events, usually generated
 
1514         // when the user drags the mouse through a page with text. Used internally
 
1515         // by Leaflet to override the behaviour of any click-and-drag interaction on
 
1516         // the map. Affects drag interactions on the whole document.
 
1518         // @function enableTextSelection()
 
1519         // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
 
1520         if ('onselectstart' in document) {
 
1521                 L.DomUtil.disableTextSelection = function () {
 
1522                         L.DomEvent.on(window, 'selectstart', L.DomEvent.preventDefault);
 
1524                 L.DomUtil.enableTextSelection = function () {
 
1525                         L.DomEvent.off(window, 'selectstart', L.DomEvent.preventDefault);
 
1529                 var userSelectProperty = L.DomUtil.testProp(
 
1530                         ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
 
1532                 L.DomUtil.disableTextSelection = function () {
 
1533                         if (userSelectProperty) {
 
1534                                 var style = document.documentElement.style;
 
1535                                 this._userSelect = style[userSelectProperty];
 
1536                                 style[userSelectProperty] = 'none';
 
1539                 L.DomUtil.enableTextSelection = function () {
 
1540                         if (userSelectProperty) {
 
1541                                 document.documentElement.style[userSelectProperty] = this._userSelect;
 
1542                                 delete this._userSelect;
 
1547         // @function disableImageDrag()
 
1548         // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
 
1549         // for `dragstart` DOM events, usually generated when the user drags an image.
 
1550         L.DomUtil.disableImageDrag = function () {
 
1551                 L.DomEvent.on(window, 'dragstart', L.DomEvent.preventDefault);
 
1554         // @function enableImageDrag()
 
1555         // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
 
1556         L.DomUtil.enableImageDrag = function () {
 
1557                 L.DomEvent.off(window, 'dragstart', L.DomEvent.preventDefault);
 
1560         // @function preventOutline(el: HTMLElement)
 
1561         // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
 
1562         // of the element `el` invisible. Used internally by Leaflet to prevent
 
1563         // focusable elements from displaying an outline when the user performs a
 
1564         // drag interaction on them.
 
1565         L.DomUtil.preventOutline = function (element) {
 
1566                 while (element.tabIndex === -1) {
 
1567                         element = element.parentNode;
 
1569                 if (!element || !element.style) { return; }
 
1570                 L.DomUtil.restoreOutline();
 
1571                 this._outlineElement = element;
 
1572                 this._outlineStyle = element.style.outline;
 
1573                 element.style.outline = 'none';
 
1574                 L.DomEvent.on(window, 'keydown', L.DomUtil.restoreOutline, this);
 
1577         // @function restoreOutline()
 
1578         // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
 
1579         L.DomUtil.restoreOutline = function () {
 
1580                 if (!this._outlineElement) { return; }
 
1581                 this._outlineElement.style.outline = this._outlineStyle;
 
1582                 delete this._outlineElement;
 
1583                 delete this._outlineStyle;
 
1584                 L.DomEvent.off(window, 'keydown', L.DomUtil.restoreOutline, this);
 
1593  * Represents a geographical point with a certain latitude and longitude.
 
1598  * var latlng = L.latLng(50.5, 30.5);
 
1601  * 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:
 
1604  * map.panTo([50, 30]);
 
1605  * map.panTo({lon: 30, lat: 50});
 
1606  * map.panTo({lat: 50, lng: 30});
 
1607  * map.panTo(L.latLng(50, 30));
 
1611 L.LatLng = function (lat, lng, alt) {
 
1612         if (isNaN(lat) || isNaN(lng)) {
 
1613                 throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
 
1616         // @property lat: Number
 
1617         // Latitude in degrees
 
1620         // @property lng: Number
 
1621         // Longitude in degrees
 
1624         // @property alt: Number
 
1625         // Altitude in meters (optional)
 
1626         if (alt !== undefined) {
 
1631 L.LatLng.prototype = {
 
1632         // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
 
1633         // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overriden by setting `maxMargin` to a small number.
 
1634         equals: function (obj, maxMargin) {
 
1635                 if (!obj) { return false; }
 
1637                 obj = L.latLng(obj);
 
1639                 var margin = Math.max(
 
1640                         Math.abs(this.lat - obj.lat),
 
1641                         Math.abs(this.lng - obj.lng));
 
1643                 return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
 
1646         // @method toString(): String
 
1647         // Returns a string representation of the point (for debugging purposes).
 
1648         toString: function (precision) {
 
1650                         L.Util.formatNum(this.lat, precision) + ', ' +
 
1651                         L.Util.formatNum(this.lng, precision) + ')';
 
1654         // @method distanceTo(otherLatLng: LatLng): Number
 
1655         // Returns the distance (in meters) to the given `LatLng` calculated using the [Haversine formula](http://en.wikipedia.org/wiki/Haversine_formula).
 
1656         distanceTo: function (other) {
 
1657                 return L.CRS.Earth.distance(this, L.latLng(other));
 
1660         // @method wrap(): LatLng
 
1661         // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
 
1663                 return L.CRS.Earth.wrapLatLng(this);
 
1666         // @method toBounds(sizeInMeters: Number): LatLngBounds
 
1667         // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
 
1668         toBounds: function (sizeInMeters) {
 
1669                 var latAccuracy = 180 * sizeInMeters / 40075017,
 
1670                     lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
 
1672                 return L.latLngBounds(
 
1673                         [this.lat - latAccuracy, this.lng - lngAccuracy],
 
1674                         [this.lat + latAccuracy, this.lng + lngAccuracy]);
 
1677         clone: function () {
 
1678                 return new L.LatLng(this.lat, this.lng, this.alt);
 
1684 // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
 
1685 // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
 
1688 // @factory L.latLng(coords: Array): LatLng
 
1689 // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
 
1692 // @factory L.latLng(coords: Object): LatLng
 
1693 // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
 
1695 L.latLng = function (a, b, c) {
 
1696         if (a instanceof L.LatLng) {
 
1699         if (L.Util.isArray(a) && typeof a[0] !== 'object') {
 
1700                 if (a.length === 3) {
 
1701                         return new L.LatLng(a[0], a[1], a[2]);
 
1703                 if (a.length === 2) {
 
1704                         return new L.LatLng(a[0], a[1]);
 
1708         if (a === undefined || a === null) {
 
1711         if (typeof a === 'object' && 'lat' in a) {
 
1712                 return new L.LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
 
1714         if (b === undefined) {
 
1717         return new L.LatLng(a, b, c);
 
1723  * @class LatLngBounds
 
1724  * @aka L.LatLngBounds
 
1726  * Represents a rectangular geographical area on a map.
 
1731  * var corner1 = L.latLng(40.712, -74.227),
 
1732  * corner2 = L.latLng(40.774, -74.125),
 
1733  * bounds = L.latLngBounds(corner1, corner2);
 
1736  * 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:
 
1740  *      [40.712, -74.227],
 
1745  * 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.
 
1748 L.LatLngBounds = function (corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
 
1749         if (!corner1) { return; }
 
1751         var latlngs = corner2 ? [corner1, corner2] : corner1;
 
1753         for (var i = 0, len = latlngs.length; i < len; i++) {
 
1754                 this.extend(latlngs[i]);
 
1758 L.LatLngBounds.prototype = {
 
1760         // @method extend(latlng: LatLng): this
 
1761         // Extend the bounds to contain the given point
 
1764         // @method extend(otherBounds: LatLngBounds): this
 
1765         // Extend the bounds to contain the given bounds
 
1766         extend: function (obj) {
 
1767                 var sw = this._southWest,
 
1768                     ne = this._northEast,
 
1771                 if (obj instanceof L.LatLng) {
 
1775                 } else if (obj instanceof L.LatLngBounds) {
 
1776                         sw2 = obj._southWest;
 
1777                         ne2 = obj._northEast;
 
1779                         if (!sw2 || !ne2) { return this; }
 
1782                         return obj ? this.extend(L.latLng(obj) || L.latLngBounds(obj)) : this;
 
1786                         this._southWest = new L.LatLng(sw2.lat, sw2.lng);
 
1787                         this._northEast = new L.LatLng(ne2.lat, ne2.lng);
 
1789                         sw.lat = Math.min(sw2.lat, sw.lat);
 
1790                         sw.lng = Math.min(sw2.lng, sw.lng);
 
1791                         ne.lat = Math.max(ne2.lat, ne.lat);
 
1792                         ne.lng = Math.max(ne2.lng, ne.lng);
 
1798         // @method pad(bufferRatio: Number): LatLngBounds
 
1799         // Returns bigger bounds created by extending the current bounds by a given percentage in each direction.
 
1800         pad: function (bufferRatio) {
 
1801                 var sw = this._southWest,
 
1802                     ne = this._northEast,
 
1803                     heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
 
1804                     widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
 
1806                 return new L.LatLngBounds(
 
1807                         new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
 
1808                         new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
 
1811         // @method getCenter(): LatLng
 
1812         // Returns the center point of the bounds.
 
1813         getCenter: function () {
 
1814                 return new L.LatLng(
 
1815                         (this._southWest.lat + this._northEast.lat) / 2,
 
1816                         (this._southWest.lng + this._northEast.lng) / 2);
 
1819         // @method getSouthWest(): LatLng
 
1820         // Returns the south-west point of the bounds.
 
1821         getSouthWest: function () {
 
1822                 return this._southWest;
 
1825         // @method getNorthEast(): LatLng
 
1826         // Returns the north-east point of the bounds.
 
1827         getNorthEast: function () {
 
1828                 return this._northEast;
 
1831         // @method getNorthWest(): LatLng
 
1832         // Returns the north-west point of the bounds.
 
1833         getNorthWest: function () {
 
1834                 return new L.LatLng(this.getNorth(), this.getWest());
 
1837         // @method getSouthEast(): LatLng
 
1838         // Returns the south-east point of the bounds.
 
1839         getSouthEast: function () {
 
1840                 return new L.LatLng(this.getSouth(), this.getEast());
 
1843         // @method getWest(): Number
 
1844         // Returns the west longitude of the bounds
 
1845         getWest: function () {
 
1846                 return this._southWest.lng;
 
1849         // @method getSouth(): Number
 
1850         // Returns the south latitude of the bounds
 
1851         getSouth: function () {
 
1852                 return this._southWest.lat;
 
1855         // @method getEast(): Number
 
1856         // Returns the east longitude of the bounds
 
1857         getEast: function () {
 
1858                 return this._northEast.lng;
 
1861         // @method getNorth(): Number
 
1862         // Returns the north latitude of the bounds
 
1863         getNorth: function () {
 
1864                 return this._northEast.lat;
 
1867         // @method contains(otherBounds: LatLngBounds): Boolean
 
1868         // Returns `true` if the rectangle contains the given one.
 
1871         // @method contains (latlng: LatLng): Boolean
 
1872         // Returns `true` if the rectangle contains the given point.
 
1873         contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
 
1874                 if (typeof obj[0] === 'number' || obj instanceof L.LatLng || 'lat' in obj) {
 
1875                         obj = L.latLng(obj);
 
1877                         obj = L.latLngBounds(obj);
 
1880                 var sw = this._southWest,
 
1881                     ne = this._northEast,
 
1884                 if (obj instanceof L.LatLngBounds) {
 
1885                         sw2 = obj.getSouthWest();
 
1886                         ne2 = obj.getNorthEast();
 
1891                 return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
 
1892                        (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
 
1895         // @method intersects(otherBounds: LatLngBounds): Boolean
 
1896         // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
 
1897         intersects: function (bounds) {
 
1898                 bounds = L.latLngBounds(bounds);
 
1900                 var sw = this._southWest,
 
1901                     ne = this._northEast,
 
1902                     sw2 = bounds.getSouthWest(),
 
1903                     ne2 = bounds.getNorthEast(),
 
1905                     latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
 
1906                     lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
 
1908                 return latIntersects && lngIntersects;
 
1911         // @method overlaps(otherBounds: Bounds): Boolean
 
1912         // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
 
1913         overlaps: function (bounds) {
 
1914                 bounds = L.latLngBounds(bounds);
 
1916                 var sw = this._southWest,
 
1917                     ne = this._northEast,
 
1918                     sw2 = bounds.getSouthWest(),
 
1919                     ne2 = bounds.getNorthEast(),
 
1921                     latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
 
1922                     lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
 
1924                 return latOverlaps && lngOverlaps;
 
1927         // @method toBBoxString(): String
 
1928         // 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.
 
1929         toBBoxString: function () {
 
1930                 return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
 
1933         // @method equals(otherBounds: LatLngBounds): Boolean
 
1934         // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds.
 
1935         equals: function (bounds) {
 
1936                 if (!bounds) { return false; }
 
1938                 bounds = L.latLngBounds(bounds);
 
1940                 return this._southWest.equals(bounds.getSouthWest()) &&
 
1941                        this._northEast.equals(bounds.getNorthEast());
 
1944         // @method isValid(): Boolean
 
1945         // Returns `true` if the bounds are properly initialized.
 
1946         isValid: function () {
 
1947                 return !!(this._southWest && this._northEast);
 
1951 // TODO International date line?
 
1953 // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
 
1954 // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
 
1957 // @factory L.latLngBounds(latlngs: LatLng[])
 
1958 // 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).
 
1959 L.latLngBounds = function (a, b) {
 
1960         if (a instanceof L.LatLngBounds) {
 
1963         return new L.LatLngBounds(a, b);
 
1969  * @namespace Projection
 
1971  * Leaflet comes with a set of already defined Projections out of the box:
 
1973  * @projection L.Projection.LonLat
 
1975  * Equirectangular, or Plate Carree projection — the most simple projection,
 
1976  * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
 
1977  * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
 
1978  * `EPSG:3395` and `Simple` CRS.
 
1983 L.Projection.LonLat = {
 
1984         project: function (latlng) {
 
1985                 return new L.Point(latlng.lng, latlng.lat);
 
1988         unproject: function (point) {
 
1989                 return new L.LatLng(point.y, point.x);
 
1992         bounds: L.bounds([-180, -90], [180, 90])
 
1998  * @namespace Projection
 
1999  * @projection L.Projection.SphericalMercator
 
2001  * Spherical Mercator projection — the most common projection for online maps,
 
2002  * used by almost all free and commercial tile providers. Assumes that Earth is
 
2003  * a sphere. Used by the `EPSG:3857` CRS.
 
2006 L.Projection.SphericalMercator = {
 
2009         MAX_LATITUDE: 85.0511287798,
 
2011         project: function (latlng) {
 
2012                 var d = Math.PI / 180,
 
2013                     max = this.MAX_LATITUDE,
 
2014                     lat = Math.max(Math.min(max, latlng.lat), -max),
 
2015                     sin = Math.sin(lat * d);
 
2018                                 this.R * latlng.lng * d,
 
2019                                 this.R * Math.log((1 + sin) / (1 - sin)) / 2);
 
2022         unproject: function (point) {
 
2023                 var d = 180 / Math.PI;
 
2025                 return new L.LatLng(
 
2026                         (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
 
2027                         point.x * d / this.R);
 
2030         bounds: (function () {
 
2031                 var d = 6378137 * Math.PI;
 
2032                 return L.bounds([-d, -d], [d, d]);
 
2041  * Abstract class that defines coordinate reference systems for projecting
 
2042  * geographical points into pixel (screen) coordinates and back (and to
 
2043  * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
 
2044  * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
 
2046  * Leaflet defines the most usual CRSs by default. If you want to use a
 
2047  * CRS not defined by default, take a look at the
 
2048  * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
 
2052         // @method latLngToPoint(latlng: LatLng, zoom: Number): Point
 
2053         // Projects geographical coordinates into pixel coordinates for a given zoom.
 
2054         latLngToPoint: function (latlng, zoom) {
 
2055                 var projectedPoint = this.projection.project(latlng),
 
2056                     scale = this.scale(zoom);
 
2058                 return this.transformation._transform(projectedPoint, scale);
 
2061         // @method pointToLatLng(point: Point, zoom: Number): LatLng
 
2062         // The inverse of `latLngToPoint`. Projects pixel coordinates on a given
 
2063         // zoom into geographical coordinates.
 
2064         pointToLatLng: function (point, zoom) {
 
2065                 var scale = this.scale(zoom),
 
2066                     untransformedPoint = this.transformation.untransform(point, scale);
 
2068                 return this.projection.unproject(untransformedPoint);
 
2071         // @method project(latlng: LatLng): Point
 
2072         // Projects geographical coordinates into coordinates in units accepted for
 
2073         // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
 
2074         project: function (latlng) {
 
2075                 return this.projection.project(latlng);
 
2078         // @method unproject(point: Point): LatLng
 
2079         // Given a projected coordinate returns the corresponding LatLng.
 
2080         // The inverse of `project`.
 
2081         unproject: function (point) {
 
2082                 return this.projection.unproject(point);
 
2085         // @method scale(zoom: Number): Number
 
2086         // Returns the scale used when transforming projected coordinates into
 
2087         // pixel coordinates for a particular zoom. For example, it returns
 
2088         // `256 * 2^zoom` for Mercator-based CRS.
 
2089         scale: function (zoom) {
 
2090                 return 256 * Math.pow(2, zoom);
 
2093         // @method zoom(scale: Number): Number
 
2094         // Inverse of `scale()`, returns the zoom level corresponding to a scale
 
2095         // factor of `scale`.
 
2096         zoom: function (scale) {
 
2097                 return Math.log(scale / 256) / Math.LN2;
 
2100         // @method getProjectedBounds(zoom: Number): Bounds
 
2101         // Returns the projection's bounds scaled and transformed for the provided `zoom`.
 
2102         getProjectedBounds: function (zoom) {
 
2103                 if (this.infinite) { return null; }
 
2105                 var b = this.projection.bounds,
 
2106                     s = this.scale(zoom),
 
2107                     min = this.transformation.transform(b.min, s),
 
2108                     max = this.transformation.transform(b.max, s);
 
2110                 return L.bounds(min, max);
 
2113         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
 
2114         // Returns the distance between two geographical coordinates.
 
2116         // @property code: String
 
2117         // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
 
2119         // @property wrapLng: Number[]
 
2120         // An array of two numbers defining whether the longitude (horizontal) coordinate
 
2121         // axis wraps around a given range and how. Defaults to `[-180, 180]` in most
 
2122         // geographical CRSs. If `undefined`, the longitude axis does not wrap around.
 
2124         // @property wrapLat: Number[]
 
2125         // Like `wrapLng`, but for the latitude (vertical) axis.
 
2127         // wrapLng: [min, max],
 
2128         // wrapLat: [min, max],
 
2130         // @property infinite: Boolean
 
2131         // If true, the coordinate space will be unbounded (infinite in both axes)
 
2134         // @method wrapLatLng(latlng: LatLng): LatLng
 
2135         // Returns a `LatLng` where lat and lng has been wrapped according to the
 
2136         // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
 
2137         // Only accepts actual `L.LatLng` instances, not arrays.
 
2138         wrapLatLng: function (latlng) {
 
2139                 var lng = this.wrapLng ? L.Util.wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
 
2140                     lat = this.wrapLat ? L.Util.wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
 
2143                 return L.latLng(lat, lng, alt);
 
2146         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
 
2147         // Returns a `LatLngBounds` with the same size as the given one, ensuring
 
2148         // that its center is within the CRS's bounds.
 
2149         // Only accepts actual `L.LatLngBounds` instances, not arrays.
 
2150         wrapLatLngBounds: function (bounds) {
 
2151                 var center = bounds.getCenter(),
 
2152                     newCenter = this.wrapLatLng(center),
 
2153                     latShift = center.lat - newCenter.lat,
 
2154                     lngShift = center.lng - newCenter.lng;
 
2156                 if (latShift === 0 && lngShift === 0) {
 
2160                 var sw = bounds.getSouthWest(),
 
2161                     ne = bounds.getNorthEast(),
 
2162                     newSw = L.latLng({lat: sw.lat - latShift, lng: sw.lng - lngShift}),
 
2163                     newNe = L.latLng({lat: ne.lat - latShift, lng: ne.lng - lngShift});
 
2165                 return new L.LatLngBounds(newSw, newNe);
 
2175  * A simple CRS that maps longitude and latitude into `x` and `y` directly.
 
2176  * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
 
2177  * axis should still be inverted (going from bottom to top). `distance()` returns
 
2178  * simple euclidean distance.
 
2181 L.CRS.Simple = L.extend({}, L.CRS, {
 
2182         projection: L.Projection.LonLat,
 
2183         transformation: new L.Transformation(1, 0, -1, 0),
 
2185         scale: function (zoom) {
 
2186                 return Math.pow(2, zoom);
 
2189         zoom: function (scale) {
 
2190                 return Math.log(scale) / Math.LN2;
 
2193         distance: function (latlng1, latlng2) {
 
2194                 var dx = latlng2.lng - latlng1.lng,
 
2195                     dy = latlng2.lat - latlng1.lat;
 
2197                 return Math.sqrt(dx * dx + dy * dy);
 
2209  * Serves as the base for CRS that are global such that they cover the earth.
 
2210  * Can only be used as the base for other CRS and cannot be used directly,
 
2211  * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
 
2215 L.CRS.Earth = L.extend({}, L.CRS, {
 
2216         wrapLng: [-180, 180],
 
2218         // Mean Earth Radius, as recommended for use by
 
2219         // the International Union of Geodesy and Geophysics,
 
2220         // see http://rosettacode.org/wiki/Haversine_formula
 
2223         // distance between two geographical points using spherical law of cosines approximation
 
2224         distance: function (latlng1, latlng2) {
 
2225                 var rad = Math.PI / 180,
 
2226                     lat1 = latlng1.lat * rad,
 
2227                     lat2 = latlng2.lat * rad,
 
2228                     a = Math.sin(lat1) * Math.sin(lat2) +
 
2229                         Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlng2.lng - latlng1.lng) * rad);
 
2231                 return this.R * Math.acos(Math.min(a, 1));
 
2239  * @crs L.CRS.EPSG3857
 
2241  * The most common CRS for online maps, used by almost all free and commercial
 
2242  * tile providers. Uses Spherical Mercator projection. Set in by default in
 
2243  * Map's `crs` option.
 
2246 L.CRS.EPSG3857 = L.extend({}, L.CRS.Earth, {
 
2248         projection: L.Projection.SphericalMercator,
 
2250         transformation: (function () {
 
2251                 var scale = 0.5 / (Math.PI * L.Projection.SphericalMercator.R);
 
2252                 return new L.Transformation(scale, 0.5, -scale, 0.5);
 
2256 L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, {
 
2264  * @crs L.CRS.EPSG4326
 
2266  * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
 
2268  * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
 
2269  * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
 
2270  * with this CRS, ensure that there are two 256x256 pixel tiles covering the
 
2271  * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
 
2272  * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
 
2275 L.CRS.EPSG4326 = L.extend({}, L.CRS.Earth, {
 
2277         projection: L.Projection.LonLat,
 
2278         transformation: new L.Transformation(1 / 180, 1, -1 / 180, 0.5)
 
2288  * The central class of the API — it is used to create a map on a page and manipulate it.
 
2293  * // initialize the map on the "map" div with a given center and zoom
 
2294  * var map = L.map('map', {
 
2295  *      center: [51.505, -0.09],
 
2302 L.Map = L.Evented.extend({
 
2305                 // @section Map State Options
 
2306                 // @option crs: CRS = L.CRS.EPSG3857
 
2307                 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
 
2308                 // sure what it means.
 
2309                 crs: L.CRS.EPSG3857,
 
2311                 // @option center: LatLng = undefined
 
2312                 // Initial geographic center of the map
 
2315                 // @option zoom: Number = undefined
 
2316                 // Initial map zoom level
 
2319                 // @option minZoom: Number = undefined
 
2320                 // Minimum zoom level of the map. Overrides any `minZoom` option set on map layers.
 
2323                 // @option maxZoom: Number = undefined
 
2324                 // Maximum zoom level of the map. Overrides any `maxZoom` option set on map layers.
 
2327                 // @option layers: Layer[] = []
 
2328                 // Array of layers that will be added to the map initially
 
2331                 // @option maxBounds: LatLngBounds = null
 
2332                 // When this option is set, the map restricts the view to the given
 
2333                 // geographical bounds, bouncing the user back if the user tries to pan
 
2334                 // outside the view. To set the restriction dynamically, use
 
2335                 // [`setMaxBounds`](#map-setmaxbounds) method.
 
2336                 maxBounds: undefined,
 
2338                 // @option renderer: Renderer = *
 
2339                 // The default method for drawing vector layers on the map. `L.SVG`
 
2340                 // or `L.Canvas` by default depending on browser support.
 
2341                 renderer: undefined,
 
2344                 // @section Animation Options
 
2345                 // @option zoomAnimation: Boolean = true
 
2346                 // Whether the map zoom animation is enabled. By default it's enabled
 
2347                 // in all browsers that support CSS3 Transitions except Android.
 
2348                 zoomAnimation: true,
 
2350                 // @option zoomAnimationThreshold: Number = 4
 
2351                 // Won't animate zoom if the zoom difference exceeds this value.
 
2352                 zoomAnimationThreshold: 4,
 
2354                 // @option fadeAnimation: Boolean = true
 
2355                 // Whether the tile fade animation is enabled. By default it's enabled
 
2356                 // in all browsers that support CSS3 Transitions except Android.
 
2357                 fadeAnimation: true,
 
2359                 // @option markerZoomAnimation: Boolean = true
 
2360                 // Whether markers animate their zoom with the zoom animation, if disabled
 
2361                 // they will disappear for the length of the animation. By default it's
 
2362                 // enabled in all browsers that support CSS3 Transitions except Android.
 
2363                 markerZoomAnimation: true,
 
2365                 // @option transform3DLimit: Number = 2^23
 
2366                 // Defines the maximum size of a CSS translation transform. The default
 
2367                 // value should not be changed unless a web browser positions layers in
 
2368                 // the wrong place after doing a large `panBy`.
 
2369                 transform3DLimit: 8388608, // Precision limit of a 32-bit float
 
2371                 // @section Interaction Options
 
2372                 // @option zoomSnap: Number = 1
 
2373                 // Forces the map's zoom level to always be a multiple of this, particularly
 
2374                 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
 
2375                 // By default, the zoom level snaps to the nearest integer; lower values
 
2376                 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
 
2377                 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
 
2380                 // @option zoomDelta: Number = 1
 
2381                 // Controls how much the map's zoom level will change after a
 
2382                 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
 
2383                 // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
 
2384                 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
 
2387                 // @option trackResize: Boolean = true
 
2388                 // Whether the map automatically handles browser window resize to update itself.
 
2392         initialize: function (id, options) { // (HTMLElement or String, Object)
 
2393                 options = L.setOptions(this, options);
 
2395                 this._initContainer(id);
 
2398                 // hack for https://github.com/Leaflet/Leaflet/issues/1980
 
2399                 this._onResize = L.bind(this._onResize, this);
 
2403                 if (options.maxBounds) {
 
2404                         this.setMaxBounds(options.maxBounds);
 
2407                 if (options.zoom !== undefined) {
 
2408                         this._zoom = this._limitZoom(options.zoom);
 
2411                 if (options.center && options.zoom !== undefined) {
 
2412                         this.setView(L.latLng(options.center), options.zoom, {reset: true});
 
2415                 this._handlers = [];
 
2417                 this._zoomBoundLayers = {};
 
2418                 this._sizeChanged = true;
 
2420                 this.callInitHooks();
 
2422                 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
 
2423                 this._zoomAnimated = L.DomUtil.TRANSITION && L.Browser.any3d && !L.Browser.mobileOpera &&
 
2424                                 this.options.zoomAnimation;
 
2426                 // zoom transitions run with the same duration for all layers, so if one of transitionend events
 
2427                 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
 
2428                 if (this._zoomAnimated) {
 
2429                         this._createAnimProxy();
 
2430                         L.DomEvent.on(this._proxy, L.DomUtil.TRANSITION_END, this._catchTransitionEnd, this);
 
2433                 this._addLayers(this.options.layers);
 
2437         // @section Methods for modifying map state
 
2439         // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
 
2440         // Sets the view of the map (geographical center and zoom) with the given
 
2441         // animation options.
 
2442         setView: function (center, zoom, options) {
 
2444                 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
 
2445                 center = this._limitCenter(L.latLng(center), zoom, this.options.maxBounds);
 
2446                 options = options || {};
 
2450                 if (this._loaded && !options.reset && options !== true) {
 
2452                         if (options.animate !== undefined) {
 
2453                                 options.zoom = L.extend({animate: options.animate}, options.zoom);
 
2454                                 options.pan = L.extend({animate: options.animate, duration: options.duration}, options.pan);
 
2457                         // try animating pan or zoom
 
2458                         var moved = (this._zoom !== zoom) ?
 
2459                                 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
 
2460                                 this._tryAnimatedPan(center, options.pan);
 
2463                                 // prevent resize handler call, the view will refresh after animation anyway
 
2464                                 clearTimeout(this._sizeTimer);
 
2469                 // animation didn't start, just reset the map view
 
2470                 this._resetView(center, zoom);
 
2475         // @method setZoom(zoom: Number, options: Zoom/pan options): this
 
2476         // Sets the zoom of the map.
 
2477         setZoom: function (zoom, options) {
 
2478                 if (!this._loaded) {
 
2482                 return this.setView(this.getCenter(), zoom, {zoom: options});
 
2485         // @method zoomIn(delta?: Number, options?: Zoom options): this
 
2486         // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
 
2487         zoomIn: function (delta, options) {
 
2488                 delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1);
 
2489                 return this.setZoom(this._zoom + delta, options);
 
2492         // @method zoomOut(delta?: Number, options?: Zoom options): this
 
2493         // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
 
2494         zoomOut: function (delta, options) {
 
2495                 delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1);
 
2496                 return this.setZoom(this._zoom - delta, options);
 
2499         // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
 
2500         // Zooms the map while keeping a specified geographical point on the map
 
2501         // stationary (e.g. used internally for scroll zoom and double-click zoom).
 
2503         // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
 
2504         // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
 
2505         setZoomAround: function (latlng, zoom, options) {
 
2506                 var scale = this.getZoomScale(zoom),
 
2507                     viewHalf = this.getSize().divideBy(2),
 
2508                     containerPoint = latlng instanceof L.Point ? latlng : this.latLngToContainerPoint(latlng),
 
2510                     centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
 
2511                     newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
 
2513                 return this.setView(newCenter, zoom, {zoom: options});
 
2516         _getBoundsCenterZoom: function (bounds, options) {
 
2518                 options = options || {};
 
2519                 bounds = bounds.getBounds ? bounds.getBounds() : L.latLngBounds(bounds);
 
2521                 var paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
 
2522                     paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]),
 
2524                     zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
 
2526                 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
 
2528                 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
 
2530                     swPoint = this.project(bounds.getSouthWest(), zoom),
 
2531                     nePoint = this.project(bounds.getNorthEast(), zoom),
 
2532                     center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
 
2540         // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
 
2541         // Sets a map view that contains the given geographical bounds with the
 
2542         // maximum zoom level possible.
 
2543         fitBounds: function (bounds, options) {
 
2545                 bounds = L.latLngBounds(bounds);
 
2547                 if (!bounds.isValid()) {
 
2548                         throw new Error('Bounds are not valid.');
 
2551                 var target = this._getBoundsCenterZoom(bounds, options);
 
2552                 return this.setView(target.center, target.zoom, options);
 
2555         // @method fitWorld(options?: fitBounds options): this
 
2556         // Sets a map view that mostly contains the whole world with the maximum
 
2557         // zoom level possible.
 
2558         fitWorld: function (options) {
 
2559                 return this.fitBounds([[-90, -180], [90, 180]], options);
 
2562         // @method panTo(latlng: LatLng, options?: Pan options): this
 
2563         // Pans the map to a given center.
 
2564         panTo: function (center, options) { // (LatLng)
 
2565                 return this.setView(center, this._zoom, {pan: options});
 
2568         // @method panBy(offset: Point): this
 
2569         // Pans the map by a given number of pixels (animated).
 
2570         panBy: function (offset, options) {
 
2571                 offset = L.point(offset).round();
 
2572                 options = options || {};
 
2574                 if (!offset.x && !offset.y) {
 
2575                         return this.fire('moveend');
 
2577                 // If we pan too far, Chrome gets issues with tiles
 
2578                 // and makes them disappear or appear in the wrong place (slightly offset) #2602
 
2579                 if (options.animate !== true && !this.getSize().contains(offset)) {
 
2580                         this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
 
2584                 if (!this._panAnim) {
 
2585                         this._panAnim = new L.PosAnimation();
 
2588                                 'step': this._onPanTransitionStep,
 
2589                                 'end': this._onPanTransitionEnd
 
2593                 // don't fire movestart if animating inertia
 
2594                 if (!options.noMoveStart) {
 
2595                         this.fire('movestart');
 
2598                 // animate pan unless animate: false specified
 
2599                 if (options.animate !== false) {
 
2600                         L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim');
 
2602                         var newPos = this._getMapPanePos().subtract(offset).round();
 
2603                         this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
 
2605                         this._rawPanBy(offset);
 
2606                         this.fire('move').fire('moveend');
 
2612         // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
 
2613         // Sets the view of the map (geographical center and zoom) performing a smooth
 
2614         // pan-zoom animation.
 
2615         flyTo: function (targetCenter, targetZoom, options) {
 
2617                 options = options || {};
 
2618                 if (options.animate === false || !L.Browser.any3d) {
 
2619                         return this.setView(targetCenter, targetZoom, options);
 
2624                 var from = this.project(this.getCenter()),
 
2625                     to = this.project(targetCenter),
 
2626                     size = this.getSize(),
 
2627                     startZoom = this._zoom;
 
2629                 targetCenter = L.latLng(targetCenter);
 
2630                 targetZoom = targetZoom === undefined ? startZoom : targetZoom;
 
2632                 var w0 = Math.max(size.x, size.y),
 
2633                     w1 = w0 * this.getZoomScale(startZoom, targetZoom),
 
2634                     u1 = (to.distanceTo(from)) || 1,
 
2639                         var s1 = i ? -1 : 1,
 
2641                             t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
 
2642                             b1 = 2 * s2 * rho2 * u1,
 
2644                             sq = Math.sqrt(b * b + 1) - b;
 
2646                             // workaround for floating point precision bug when sq = 0, log = -Infinite,
 
2647                             // thus triggering an infinite loop in flyTo
 
2648                             var log = sq < 0.000000001 ? -18 : Math.log(sq);
 
2653                 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
 
2654                 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
 
2655                 function tanh(n) { return sinh(n) / cosh(n); }
 
2659                 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
 
2660                 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
 
2662                 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
 
2664                 var start = Date.now(),
 
2665                     S = (r(1) - r0) / rho,
 
2666                     duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
 
2669                         var t = (Date.now() - start) / duration,
 
2673                                 this._flyToFrame = L.Util.requestAnimFrame(frame, this);
 
2676                                         this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
 
2677                                         this.getScaleZoom(w0 / w(s), startZoom),
 
2682                                         ._move(targetCenter, targetZoom)
 
2687                 this._moveStart(true);
 
2693         // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
 
2694         // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
 
2695         // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
 
2696         flyToBounds: function (bounds, options) {
 
2697                 var target = this._getBoundsCenterZoom(bounds, options);
 
2698                 return this.flyTo(target.center, target.zoom, options);
 
2701         // @method setMaxBounds(bounds: Bounds): this
 
2702         // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
 
2703         setMaxBounds: function (bounds) {
 
2704                 bounds = L.latLngBounds(bounds);
 
2706                 if (!bounds.isValid()) {
 
2707                         this.options.maxBounds = null;
 
2708                         return this.off('moveend', this._panInsideMaxBounds);
 
2709                 } else if (this.options.maxBounds) {
 
2710                         this.off('moveend', this._panInsideMaxBounds);
 
2713                 this.options.maxBounds = bounds;
 
2716                         this._panInsideMaxBounds();
 
2719                 return this.on('moveend', this._panInsideMaxBounds);
 
2722         // @method setMinZoom(zoom: Number): this
 
2723         // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
 
2724         setMinZoom: function (zoom) {
 
2725                 this.options.minZoom = zoom;
 
2727                 if (this._loaded && this.getZoom() < this.options.minZoom) {
 
2728                         return this.setZoom(zoom);
 
2734         // @method setMaxZoom(zoom: Number): this
 
2735         // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
 
2736         setMaxZoom: function (zoom) {
 
2737                 this.options.maxZoom = zoom;
 
2739                 if (this._loaded && (this.getZoom() > this.options.maxZoom)) {
 
2740                         return this.setZoom(zoom);
 
2746         // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
 
2747         // 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.
 
2748         panInsideBounds: function (bounds, options) {
 
2749                 this._enforcingBounds = true;
 
2750                 var center = this.getCenter(),
 
2751                     newCenter = this._limitCenter(center, this._zoom, L.latLngBounds(bounds));
 
2753                 if (!center.equals(newCenter)) {
 
2754                         this.panTo(newCenter, options);
 
2757                 this._enforcingBounds = false;
 
2761         // @method invalidateSize(options: Zoom/Pan options): this
 
2762         // Checks if the map container size changed and updates the map if so —
 
2763         // call it after you've changed the map size dynamically, also animating
 
2764         // pan by default. If `options.pan` is `false`, panning will not occur.
 
2765         // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
 
2766         // that it doesn't happen often even if the method is called many
 
2770         // @method invalidateSize(animate: Boolean): this
 
2771         // Checks if the map container size changed and updates the map if so —
 
2772         // call it after you've changed the map size dynamically, also animating
 
2774         invalidateSize: function (options) {
 
2775                 if (!this._loaded) { return this; }
 
2777                 options = L.extend({
 
2780                 }, options === true ? {animate: true} : options);
 
2782                 var oldSize = this.getSize();
 
2783                 this._sizeChanged = true;
 
2784                 this._lastCenter = null;
 
2786                 var newSize = this.getSize(),
 
2787                     oldCenter = oldSize.divideBy(2).round(),
 
2788                     newCenter = newSize.divideBy(2).round(),
 
2789                     offset = oldCenter.subtract(newCenter);
 
2791                 if (!offset.x && !offset.y) { return this; }
 
2793                 if (options.animate && options.pan) {
 
2798                                 this._rawPanBy(offset);
 
2803                         if (options.debounceMoveend) {
 
2804                                 clearTimeout(this._sizeTimer);
 
2805                                 this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200);
 
2807                                 this.fire('moveend');
 
2811                 // @section Map state change events
 
2812                 // @event resize: ResizeEvent
 
2813                 // Fired when the map is resized.
 
2814                 return this.fire('resize', {
 
2820         // @section Methods for modifying map state
 
2821         // @method stop(): this
 
2822         // Stops the currently running `panTo` or `flyTo` animation, if any.
 
2824                 this.setZoom(this._limitZoom(this._zoom));
 
2825                 if (!this.options.zoomSnap) {
 
2826                         this.fire('viewreset');
 
2828                 return this._stop();
 
2831         // @section Geolocation methods
 
2832         // @method locate(options?: Locate options): this
 
2833         // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
 
2834         // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
 
2835         // and optionally sets the map view to the user's location with respect to
 
2836         // detection accuracy (or to the world view if geolocation failed).
 
2837         // Note that, if your page doesn't use HTTPS, this method will fail in
 
2838         // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
 
2839         // See `Locate options` for more details.
 
2840         locate: function (options) {
 
2842                 options = this._locateOptions = L.extend({
 
2846                         // maxZoom: <Number>
 
2848                         // enableHighAccuracy: false
 
2851                 if (!('geolocation' in navigator)) {
 
2852                         this._handleGeolocationError({
 
2854                                 message: 'Geolocation not supported.'
 
2859                 var onResponse = L.bind(this._handleGeolocationResponse, this),
 
2860                     onError = L.bind(this._handleGeolocationError, this);
 
2862                 if (options.watch) {
 
2863                         this._locationWatchId =
 
2864                                 navigator.geolocation.watchPosition(onResponse, onError, options);
 
2866                         navigator.geolocation.getCurrentPosition(onResponse, onError, options);
 
2871         // @method stopLocate(): this
 
2872         // Stops watching location previously initiated by `map.locate({watch: true})`
 
2873         // and aborts resetting the map view if map.locate was called with
 
2874         // `{setView: true}`.
 
2875         stopLocate: function () {
 
2876                 if (navigator.geolocation && navigator.geolocation.clearWatch) {
 
2877                         navigator.geolocation.clearWatch(this._locationWatchId);
 
2879                 if (this._locateOptions) {
 
2880                         this._locateOptions.setView = false;
 
2885         _handleGeolocationError: function (error) {
 
2887                     message = error.message ||
 
2888                             (c === 1 ? 'permission denied' :
 
2889                             (c === 2 ? 'position unavailable' : 'timeout'));
 
2891                 if (this._locateOptions.setView && !this._loaded) {
 
2895                 // @section Location events
 
2896                 // @event locationerror: ErrorEvent
 
2897                 // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
 
2898                 this.fire('locationerror', {
 
2900                         message: 'Geolocation error: ' + message + '.'
 
2904         _handleGeolocationResponse: function (pos) {
 
2905                 var lat = pos.coords.latitude,
 
2906                     lng = pos.coords.longitude,
 
2907                     latlng = new L.LatLng(lat, lng),
 
2908                     bounds = latlng.toBounds(pos.coords.accuracy),
 
2909                     options = this._locateOptions;
 
2911                 if (options.setView) {
 
2912                         var zoom = this.getBoundsZoom(bounds);
 
2913                         this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
 
2919                         timestamp: pos.timestamp
 
2922                 for (var i in pos.coords) {
 
2923                         if (typeof pos.coords[i] === 'number') {
 
2924                                 data[i] = pos.coords[i];
 
2928                 // @event locationfound: LocationEvent
 
2929                 // Fired when geolocation (using the [`locate`](#map-locate) method)
 
2930                 // went successfully.
 
2931                 this.fire('locationfound', data);
 
2934         // TODO handler.addTo
 
2935         // TODO Appropiate docs section?
 
2936         // @section Other Methods
 
2937         // @method addHandler(name: String, HandlerClass: Function): this
 
2938         // Adds a new `Handler` to the map, given its name and constructor function.
 
2939         addHandler: function (name, HandlerClass) {
 
2940                 if (!HandlerClass) { return this; }
 
2942                 var handler = this[name] = new HandlerClass(this);
 
2944                 this._handlers.push(handler);
 
2946                 if (this.options[name]) {
 
2953         // @method remove(): this
 
2954         // Destroys the map and clears all related event listeners.
 
2955         remove: function () {
 
2957                 this._initEvents(true);
 
2959                 if (this._containerId !== this._container._leaflet_id) {
 
2960                         throw new Error('Map container is being reused by another instance');
 
2964                         // throws error in IE6-8
 
2965                         delete this._container._leaflet_id;
 
2966                         delete this._containerId;
 
2969                         this._container._leaflet_id = undefined;
 
2971                         this._containerId = undefined;
 
2974                 L.DomUtil.remove(this._mapPane);
 
2976                 if (this._clearControlPos) {
 
2977                         this._clearControlPos();
 
2980                 this._clearHandlers();
 
2983                         // @section Map state change events
 
2984                         // @event unload: Event
 
2985                         // Fired when the map is destroyed with [remove](#map-remove) method.
 
2986                         this.fire('unload');
 
2989                 for (var i in this._layers) {
 
2990                         this._layers[i].remove();
 
2996         // @section Other Methods
 
2997         // @method createPane(name: String, container?: HTMLElement): HTMLElement
 
2998         // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
 
2999         // then returns it. The pane is created as a children of `container`, or
 
3000         // as a children of the main map pane if not set.
 
3001         createPane: function (name, container) {
 
3002                 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
 
3003                     pane = L.DomUtil.create('div', className, container || this._mapPane);
 
3006                         this._panes[name] = pane;
 
3011         // @section Methods for Getting Map State
 
3013         // @method getCenter(): LatLng
 
3014         // Returns the geographical center of the map view
 
3015         getCenter: function () {
 
3016                 this._checkIfLoaded();
 
3018                 if (this._lastCenter && !this._moved()) {
 
3019                         return this._lastCenter;
 
3021                 return this.layerPointToLatLng(this._getCenterLayerPoint());
 
3024         // @method getZoom(): Number
 
3025         // Returns the current zoom level of the map view
 
3026         getZoom: function () {
 
3030         // @method getBounds(): LatLngBounds
 
3031         // Returns the geographical bounds visible in the current map view
 
3032         getBounds: function () {
 
3033                 var bounds = this.getPixelBounds(),
 
3034                     sw = this.unproject(bounds.getBottomLeft()),
 
3035                     ne = this.unproject(bounds.getTopRight());
 
3037                 return new L.LatLngBounds(sw, ne);
 
3040         // @method getMinZoom(): Number
 
3041         // 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.
 
3042         getMinZoom: function () {
 
3043                 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
 
3046         // @method getMaxZoom(): Number
 
3047         // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
 
3048         getMaxZoom: function () {
 
3049                 return this.options.maxZoom === undefined ?
 
3050                         (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
 
3051                         this.options.maxZoom;
 
3054         // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean): Number
 
3055         // Returns the maximum zoom level on which the given bounds fit to the map
 
3056         // view in its entirety. If `inside` (optional) is set to `true`, the method
 
3057         // instead returns the minimum zoom level on which the map view fits into
 
3058         // the given bounds in its entirety.
 
3059         getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
 
3060                 bounds = L.latLngBounds(bounds);
 
3061                 padding = L.point(padding || [0, 0]);
 
3063                 var zoom = this.getZoom() || 0,
 
3064                     min = this.getMinZoom(),
 
3065                     max = this.getMaxZoom(),
 
3066                     nw = bounds.getNorthWest(),
 
3067                     se = bounds.getSouthEast(),
 
3068                     size = this.getSize().subtract(padding),
 
3069                     boundsSize = L.bounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
 
3070                     snap = L.Browser.any3d ? this.options.zoomSnap : 1;
 
3072                 var scale = Math.min(size.x / boundsSize.x, size.y / boundsSize.y);
 
3073                 zoom = this.getScaleZoom(scale, zoom);
 
3076                         zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
 
3077                         zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
 
3080                 return Math.max(min, Math.min(max, zoom));
 
3083         // @method getSize(): Point
 
3084         // Returns the current size of the map container (in pixels).
 
3085         getSize: function () {
 
3086                 if (!this._size || this._sizeChanged) {
 
3087                         this._size = new L.Point(
 
3088                                 this._container.clientWidth || 0,
 
3089                                 this._container.clientHeight || 0);
 
3091                         this._sizeChanged = false;
 
3093                 return this._size.clone();
 
3096         // @method getPixelBounds(): Bounds
 
3097         // Returns the bounds of the current map view in projected pixel
 
3098         // coordinates (sometimes useful in layer and overlay implementations).
 
3099         getPixelBounds: function (center, zoom) {
 
3100                 var topLeftPoint = this._getTopLeftPoint(center, zoom);
 
3101                 return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
 
3104         // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
 
3105         // the map pane? "left point of the map layer" can be confusing, specially
 
3106         // since there can be negative offsets.
 
3107         // @method getPixelOrigin(): Point
 
3108         // Returns the projected pixel coordinates of the top left point of
 
3109         // the map layer (useful in custom layer and overlay implementations).
 
3110         getPixelOrigin: function () {
 
3111                 this._checkIfLoaded();
 
3112                 return this._pixelOrigin;
 
3115         // @method getPixelWorldBounds(zoom?: Number): Bounds
 
3116         // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
 
3117         // If `zoom` is omitted, the map's current zoom level is used.
 
3118         getPixelWorldBounds: function (zoom) {
 
3119                 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
 
3122         // @section Other Methods
 
3124         // @method getPane(pane: String|HTMLElement): HTMLElement
 
3125         // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
 
3126         getPane: function (pane) {
 
3127                 return typeof pane === 'string' ? this._panes[pane] : pane;
 
3130         // @method getPanes(): Object
 
3131         // Returns a plain object containing the names of all [panes](#map-pane) as keys and
 
3132         // the panes as values.
 
3133         getPanes: function () {
 
3137         // @method getContainer: HTMLElement
 
3138         // Returns the HTML element that contains the map.
 
3139         getContainer: function () {
 
3140                 return this._container;
 
3144         // @section Conversion Methods
 
3146         // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
 
3147         // Returns the scale factor to be applied to a map transition from zoom level
 
3148         // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
 
3149         getZoomScale: function (toZoom, fromZoom) {
 
3150                 // TODO replace with universal implementation after refactoring projections
 
3151                 var crs = this.options.crs;
 
3152                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
 
3153                 return crs.scale(toZoom) / crs.scale(fromZoom);
 
3156         // @method getScaleZoom(scale: Number, fromZoom: Number): Number
 
3157         // Returns the zoom level that the map would end up at, if it is at `fromZoom`
 
3158         // level and everything is scaled by a factor of `scale`. Inverse of
 
3159         // [`getZoomScale`](#map-getZoomScale).
 
3160         getScaleZoom: function (scale, fromZoom) {
 
3161                 var crs = this.options.crs;
 
3162                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
 
3163                 var zoom = crs.zoom(scale * crs.scale(fromZoom));
 
3164                 return isNaN(zoom) ? Infinity : zoom;
 
3167         // @method project(latlng: LatLng, zoom: Number): Point
 
3168         // Projects a geographical coordinate `LatLng` according to the projection
 
3169         // of the map's CRS, then scales it according to `zoom` and the CRS's
 
3170         // `Transformation`. The result is pixel coordinate relative to
 
3172         project: function (latlng, zoom) {
 
3173                 zoom = zoom === undefined ? this._zoom : zoom;
 
3174                 return this.options.crs.latLngToPoint(L.latLng(latlng), zoom);
 
3177         // @method unproject(point: Point, zoom: Number): LatLng
 
3178         // Inverse of [`project`](#map-project).
 
3179         unproject: function (point, zoom) {
 
3180                 zoom = zoom === undefined ? this._zoom : zoom;
 
3181                 return this.options.crs.pointToLatLng(L.point(point), zoom);
 
3184         // @method layerPointToLatLng(point: Point): LatLng
 
3185         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
 
3186         // returns the corresponding geographical coordinate (for the current zoom level).
 
3187         layerPointToLatLng: function (point) {
 
3188                 var projectedPoint = L.point(point).add(this.getPixelOrigin());
 
3189                 return this.unproject(projectedPoint);
 
3192         // @method latLngToLayerPoint(latlng: LatLng): Point
 
3193         // Given a geographical coordinate, returns the corresponding pixel coordinate
 
3194         // relative to the [origin pixel](#map-getpixelorigin).
 
3195         latLngToLayerPoint: function (latlng) {
 
3196                 var projectedPoint = this.project(L.latLng(latlng))._round();
 
3197                 return projectedPoint._subtract(this.getPixelOrigin());
 
3200         // @method wrapLatLng(latlng: LatLng): LatLng
 
3201         // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
 
3202         // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
 
3204         // By default this means longitude is wrapped around the dateline so its
 
3205         // value is between -180 and +180 degrees.
 
3206         wrapLatLng: function (latlng) {
 
3207                 return this.options.crs.wrapLatLng(L.latLng(latlng));
 
3210         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
 
3211         // Returns a `LatLngBounds` with the same size as the given one, ensuring that
 
3212         // its center is within the CRS's bounds.
 
3213         // By default this means the center longitude is wrapped around the dateline so its
 
3214         // value is between -180 and +180 degrees, and the majority of the bounds
 
3215         // overlaps the CRS's bounds.
 
3216         wrapLatLngBounds: function (latlng) {
 
3217                 return this.options.crs.wrapLatLngBounds(L.latLngBounds(latlng));
 
3220         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
 
3221         // Returns the distance between two geographical coordinates according to
 
3222         // the map's CRS. By default this measures distance in meters.
 
3223         distance: function (latlng1, latlng2) {
 
3224                 return this.options.crs.distance(L.latLng(latlng1), L.latLng(latlng2));
 
3227         // @method containerPointToLayerPoint(point: Point): Point
 
3228         // Given a pixel coordinate relative to the map container, returns the corresponding
 
3229         // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
 
3230         containerPointToLayerPoint: function (point) { // (Point)
 
3231                 return L.point(point).subtract(this._getMapPanePos());
 
3234         // @method layerPointToContainerPoint(point: Point): Point
 
3235         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
 
3236         // returns the corresponding pixel coordinate relative to the map container.
 
3237         layerPointToContainerPoint: function (point) { // (Point)
 
3238                 return L.point(point).add(this._getMapPanePos());
 
3241         // @method containerPointToLatLng(point: Point): LatLng
 
3242         // Given a pixel coordinate relative to the map container, returns
 
3243         // the corresponding geographical coordinate (for the current zoom level).
 
3244         containerPointToLatLng: function (point) {
 
3245                 var layerPoint = this.containerPointToLayerPoint(L.point(point));
 
3246                 return this.layerPointToLatLng(layerPoint);
 
3249         // @method latLngToContainerPoint(latlng: LatLng): Point
 
3250         // Given a geographical coordinate, returns the corresponding pixel coordinate
 
3251         // relative to the map container.
 
3252         latLngToContainerPoint: function (latlng) {
 
3253                 return this.layerPointToContainerPoint(this.latLngToLayerPoint(L.latLng(latlng)));
 
3256         // @method mouseEventToContainerPoint(ev: MouseEvent): Point
 
3257         // Given a MouseEvent object, returns the pixel coordinate relative to the
 
3258         // map container where the event took place.
 
3259         mouseEventToContainerPoint: function (e) {
 
3260                 return L.DomEvent.getMousePosition(e, this._container);
 
3263         // @method mouseEventToLayerPoint(ev: MouseEvent): Point
 
3264         // Given a MouseEvent object, returns the pixel coordinate relative to
 
3265         // the [origin pixel](#map-getpixelorigin) where the event took place.
 
3266         mouseEventToLayerPoint: function (e) {
 
3267                 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
 
3270         // @method mouseEventToLatLng(ev: MouseEvent): LatLng
 
3271         // Given a MouseEvent object, returns geographical coordinate where the
 
3272         // event took place.
 
3273         mouseEventToLatLng: function (e) { // (MouseEvent)
 
3274                 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
 
3278         // map initialization methods
 
3280         _initContainer: function (id) {
 
3281                 var container = this._container = L.DomUtil.get(id);
 
3284                         throw new Error('Map container not found.');
 
3285                 } else if (container._leaflet_id) {
 
3286                         throw new Error('Map container is already initialized.');
 
3289                 L.DomEvent.addListener(container, 'scroll', this._onScroll, this);
 
3290                 this._containerId = L.Util.stamp(container);
 
3293         _initLayout: function () {
 
3294                 var container = this._container;
 
3296                 this._fadeAnimated = this.options.fadeAnimation && L.Browser.any3d;
 
3298                 L.DomUtil.addClass(container, 'leaflet-container' +
 
3299                         (L.Browser.touch ? ' leaflet-touch' : '') +
 
3300                         (L.Browser.retina ? ' leaflet-retina' : '') +
 
3301                         (L.Browser.ielt9 ? ' leaflet-oldie' : '') +
 
3302                         (L.Browser.safari ? ' leaflet-safari' : '') +
 
3303                         (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
 
3305                 var position = L.DomUtil.getStyle(container, 'position');
 
3307                 if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
 
3308                         container.style.position = 'relative';
 
3313                 if (this._initControlPos) {
 
3314                         this._initControlPos();
 
3318         _initPanes: function () {
 
3319                 var panes = this._panes = {};
 
3320                 this._paneRenderers = {};
 
3324                 // Panes are DOM elements used to control the ordering of layers on the map. You
 
3325                 // can access panes with [`map.getPane`](#map-getpane) or
 
3326                 // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
 
3327                 // [`map.createPane`](#map-createpane) method.
 
3329                 // Every map has the following default panes that differ only in zIndex.
 
3331                 // @pane mapPane: HTMLElement = 'auto'
 
3332                 // Pane that contains all other map panes
 
3334                 this._mapPane = this.createPane('mapPane', this._container);
 
3335                 L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
 
3337                 // @pane tilePane: HTMLElement = 200
 
3338                 // Pane for `GridLayer`s and `TileLayer`s
 
3339                 this.createPane('tilePane');
 
3340                 // @pane overlayPane: HTMLElement = 400
 
3341                 // Pane for vector overlays (`Path`s), like `Polyline`s and `Polygon`s
 
3342                 this.createPane('shadowPane');
 
3343                 // @pane shadowPane: HTMLElement = 500
 
3344                 // Pane for overlay shadows (e.g. `Marker` shadows)
 
3345                 this.createPane('overlayPane');
 
3346                 // @pane markerPane: HTMLElement = 600
 
3347                 // Pane for `Icon`s of `Marker`s
 
3348                 this.createPane('markerPane');
 
3349                 // @pane tooltipPane: HTMLElement = 650
 
3350                 // Pane for tooltip.
 
3351                 this.createPane('tooltipPane');
 
3352                 // @pane popupPane: HTMLElement = 700
 
3353                 // Pane for `Popup`s.
 
3354                 this.createPane('popupPane');
 
3356                 if (!this.options.markerZoomAnimation) {
 
3357                         L.DomUtil.addClass(panes.markerPane, 'leaflet-zoom-hide');
 
3358                         L.DomUtil.addClass(panes.shadowPane, 'leaflet-zoom-hide');
 
3363         // private methods that modify map state
 
3365         // @section Map state change events
 
3366         _resetView: function (center, zoom) {
 
3367                 L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
 
3369                 var loading = !this._loaded;
 
3370                 this._loaded = true;
 
3371                 zoom = this._limitZoom(zoom);
 
3373                 this.fire('viewprereset');
 
3375                 var zoomChanged = this._zoom !== zoom;
 
3377                         ._moveStart(zoomChanged)
 
3378                         ._move(center, zoom)
 
3379                         ._moveEnd(zoomChanged);
 
3381                 // @event viewreset: Event
 
3382                 // Fired when the map needs to redraw its content (this usually happens
 
3383                 // on map zoom or load). Very useful for creating custom overlays.
 
3384                 this.fire('viewreset');
 
3386                 // @event load: Event
 
3387                 // Fired when the map is initialized (when its center and zoom are set
 
3388                 // for the first time).
 
3394         _moveStart: function (zoomChanged) {
 
3395                 // @event zoomstart: Event
 
3396                 // Fired when the map zoom is about to change (e.g. before zoom animation).
 
3397                 // @event movestart: Event
 
3398                 // Fired when the view of the map starts changing (e.g. user starts dragging the map).
 
3400                         this.fire('zoomstart');
 
3402                 return this.fire('movestart');
 
3405         _move: function (center, zoom, data) {
 
3406                 if (zoom === undefined) {
 
3409                 var zoomChanged = this._zoom !== zoom;
 
3412                 this._lastCenter = center;
 
3413                 this._pixelOrigin = this._getNewPixelOrigin(center);
 
3415                 // @event zoom: Event
 
3416                 // Fired repeatedly during any change in zoom level, including zoom
 
3417                 // and fly animations.
 
3418                 if (zoomChanged || (data && data.pinch)) {      // Always fire 'zoom' if pinching because #3530
 
3419                         this.fire('zoom', data);
 
3422                 // @event move: Event
 
3423                 // Fired repeatedly during any movement of the map, including pan and
 
3425                 return this.fire('move', data);
 
3428         _moveEnd: function (zoomChanged) {
 
3429                 // @event zoomend: Event
 
3430                 // Fired when the map has changed, after any animations.
 
3432                         this.fire('zoomend');
 
3435                 // @event moveend: Event
 
3436                 // Fired when the center of the map stops changing (e.g. user stopped
 
3437                 // dragging the map).
 
3438                 return this.fire('moveend');
 
3441         _stop: function () {
 
3442                 L.Util.cancelAnimFrame(this._flyToFrame);
 
3443                 if (this._panAnim) {
 
3444                         this._panAnim.stop();
 
3449         _rawPanBy: function (offset) {
 
3450                 L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
 
3453         _getZoomSpan: function () {
 
3454                 return this.getMaxZoom() - this.getMinZoom();
 
3457         _panInsideMaxBounds: function () {
 
3458                 if (!this._enforcingBounds) {
 
3459                         this.panInsideBounds(this.options.maxBounds);
 
3463         _checkIfLoaded: function () {
 
3464                 if (!this._loaded) {
 
3465                         throw new Error('Set map center and zoom first.');
 
3469         // DOM event handling
 
3471         // @section Interaction events
 
3472         _initEvents: function (remove) {
 
3473                 if (!L.DomEvent) { return; }
 
3476                 this._targets[L.stamp(this._container)] = this;
 
3478                 var onOff = remove ? 'off' : 'on';
 
3480                 // @event click: MouseEvent
 
3481                 // Fired when the user clicks (or taps) the map.
 
3482                 // @event dblclick: MouseEvent
 
3483                 // Fired when the user double-clicks (or double-taps) the map.
 
3484                 // @event mousedown: MouseEvent
 
3485                 // Fired when the user pushes the mouse button on the map.
 
3486                 // @event mouseup: MouseEvent
 
3487                 // Fired when the user releases the mouse button on the map.
 
3488                 // @event mouseover: MouseEvent
 
3489                 // Fired when the mouse enters the map.
 
3490                 // @event mouseout: MouseEvent
 
3491                 // Fired when the mouse leaves the map.
 
3492                 // @event mousemove: MouseEvent
 
3493                 // Fired while the mouse moves over the map.
 
3494                 // @event contextmenu: MouseEvent
 
3495                 // Fired when the user pushes the right mouse button on the map, prevents
 
3496                 // default browser context menu from showing if there are listeners on
 
3497                 // this event. Also fired on mobile when the user holds a single touch
 
3498                 // for a second (also called long press).
 
3499                 // @event keypress: KeyboardEvent
 
3500                 // Fired when the user presses a key from the keyboard while the map is focused.
 
3501                 L.DomEvent[onOff](this._container, 'click dblclick mousedown mouseup ' +
 
3502                         'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
 
3504                 if (this.options.trackResize) {
 
3505                         L.DomEvent[onOff](window, 'resize', this._onResize, this);
 
3508                 if (L.Browser.any3d && this.options.transform3DLimit) {
 
3509                         this[onOff]('moveend', this._onMoveEnd);
 
3513         _onResize: function () {
 
3514                 L.Util.cancelAnimFrame(this._resizeRequest);
 
3515                 this._resizeRequest = L.Util.requestAnimFrame(
 
3516                         function () { this.invalidateSize({debounceMoveend: true}); }, this);
 
3519         _onScroll: function () {
 
3520                 this._container.scrollTop  = 0;
 
3521                 this._container.scrollLeft = 0;
 
3524         _onMoveEnd: function () {
 
3525                 var pos = this._getMapPanePos();
 
3526                 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
 
3527                         // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
 
3528                         // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
 
3529                         this._resetView(this.getCenter(), this.getZoom());
 
3533         _findEventTargets: function (e, type) {
 
3536                     isHover = type === 'mouseout' || type === 'mouseover',
 
3537                     src = e.target || e.srcElement,
 
3541                         target = this._targets[L.stamp(src)];
 
3542                         if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
 
3543                                 // Prevent firing click after you just dragged an object.
 
3547                         if (target && target.listens(type, true)) {
 
3548                                 if (isHover && !L.DomEvent._isExternalTarget(src, e)) { break; }
 
3549                                 targets.push(target);
 
3550                                 if (isHover) { break; }
 
3552                         if (src === this._container) { break; }
 
3553                         src = src.parentNode;
 
3555                 if (!targets.length && !dragging && !isHover && L.DomEvent._isExternalTarget(src, e)) {
 
3561         _handleDOMEvent: function (e) {
 
3562                 if (!this._loaded || L.DomEvent._skipped(e)) { return; }
 
3564                 var type = e.type === 'keypress' && e.keyCode === 13 ? 'click' : e.type;
 
3566                 if (type === 'mousedown') {
 
3567                         // prevents outline when clicking on keyboard-focusable element
 
3568                         L.DomUtil.preventOutline(e.target || e.srcElement);
 
3571                 this._fireDOMEvent(e, type);
 
3574         _fireDOMEvent: function (e, type, targets) {
 
3576                 if (e.type === 'click') {
 
3577                         // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
 
3578                         // @event preclick: MouseEvent
 
3579                         // Fired before mouse click on the map (sometimes useful when you
 
3580                         // want something to happen on click before any existing click
 
3581                         // handlers start running).
 
3582                         var synth = L.Util.extend({}, e);
 
3583                         synth.type = 'preclick';
 
3584                         this._fireDOMEvent(synth, synth.type, targets);
 
3587                 if (e._stopped) { return; }
 
3589                 // Find the layer the event is propagating from and its parents.
 
3590                 targets = (targets || []).concat(this._findEventTargets(e, type));
 
3592                 if (!targets.length) { return; }
 
3594                 var target = targets[0];
 
3595                 if (type === 'contextmenu' && target.listens(type, true)) {
 
3596                         L.DomEvent.preventDefault(e);
 
3603                 if (e.type !== 'keypress') {
 
3604                         var isMarker = target instanceof L.Marker;
 
3605                         data.containerPoint = isMarker ?
 
3606                                         this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
 
3607                         data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
 
3608                         data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
 
3611                 for (var i = 0; i < targets.length; i++) {
 
3612                         targets[i].fire(type, data, true);
 
3613                         if (data.originalEvent._stopped ||
 
3614                                 (targets[i].options.nonBubblingEvents && L.Util.indexOf(targets[i].options.nonBubblingEvents, type) !== -1)) { return; }
 
3618         _draggableMoved: function (obj) {
 
3619                 obj = obj.dragging && obj.dragging.enabled() ? obj : this;
 
3620                 return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
 
3623         _clearHandlers: function () {
 
3624                 for (var i = 0, len = this._handlers.length; i < len; i++) {
 
3625                         this._handlers[i].disable();
 
3629         // @section Other Methods
 
3631         // @method whenReady(fn: Function, context?: Object): this
 
3632         // Runs the given function `fn` when the map gets initialized with
 
3633         // a view (center and zoom) and at least one layer, or immediately
 
3634         // if it's already initialized, optionally passing a function context.
 
3635         whenReady: function (callback, context) {
 
3637                         callback.call(context || this, {target: this});
 
3639                         this.on('load', callback, context);
 
3645         // private methods for getting map state
 
3647         _getMapPanePos: function () {
 
3648                 return L.DomUtil.getPosition(this._mapPane) || new L.Point(0, 0);
 
3651         _moved: function () {
 
3652                 var pos = this._getMapPanePos();
 
3653                 return pos && !pos.equals([0, 0]);
 
3656         _getTopLeftPoint: function (center, zoom) {
 
3657                 var pixelOrigin = center && zoom !== undefined ?
 
3658                         this._getNewPixelOrigin(center, zoom) :
 
3659                         this.getPixelOrigin();
 
3660                 return pixelOrigin.subtract(this._getMapPanePos());
 
3663         _getNewPixelOrigin: function (center, zoom) {
 
3664                 var viewHalf = this.getSize()._divideBy(2);
 
3665                 return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
 
3668         _latLngToNewLayerPoint: function (latlng, zoom, center) {
 
3669                 var topLeft = this._getNewPixelOrigin(center, zoom);
 
3670                 return this.project(latlng, zoom)._subtract(topLeft);
 
3673         _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
 
3674                 var topLeft = this._getNewPixelOrigin(center, zoom);
 
3676                         this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
 
3677                         this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
 
3678                         this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
 
3679                         this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
 
3683         // layer point of the current center
 
3684         _getCenterLayerPoint: function () {
 
3685                 return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
 
3688         // offset of the specified place to the current center in pixels
 
3689         _getCenterOffset: function (latlng) {
 
3690                 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
 
3693         // adjust center for view to get inside bounds
 
3694         _limitCenter: function (center, zoom, bounds) {
 
3696                 if (!bounds) { return center; }
 
3698                 var centerPoint = this.project(center, zoom),
 
3699                     viewHalf = this.getSize().divideBy(2),
 
3700                     viewBounds = new L.Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
 
3701                     offset = this._getBoundsOffset(viewBounds, bounds, zoom);
 
3703                 // If offset is less than a pixel, ignore.
 
3704                 // This prevents unstable projections from getting into
 
3705                 // an infinite loop of tiny offsets.
 
3706                 if (offset.round().equals([0, 0])) {
 
3710                 return this.unproject(centerPoint.add(offset), zoom);
 
3713         // adjust offset for view to get inside bounds
 
3714         _limitOffset: function (offset, bounds) {
 
3715                 if (!bounds) { return offset; }
 
3717                 var viewBounds = this.getPixelBounds(),
 
3718                     newBounds = new L.Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
 
3720                 return offset.add(this._getBoundsOffset(newBounds, bounds));
 
3723         // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
 
3724         _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
 
3725                 var projectedMaxBounds = L.bounds(
 
3726                         this.project(maxBounds.getNorthEast(), zoom),
 
3727                         this.project(maxBounds.getSouthWest(), zoom)
 
3729                     minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
 
3730                     maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
 
3732                     dx = this._rebound(minOffset.x, -maxOffset.x),
 
3733                     dy = this._rebound(minOffset.y, -maxOffset.y);
 
3735                 return new L.Point(dx, dy);
 
3738         _rebound: function (left, right) {
 
3739                 return left + right > 0 ?
 
3740                         Math.round(left - right) / 2 :
 
3741                         Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
 
3744         _limitZoom: function (zoom) {
 
3745                 var min = this.getMinZoom(),
 
3746                     max = this.getMaxZoom(),
 
3747                     snap = L.Browser.any3d ? this.options.zoomSnap : 1;
 
3749                         zoom = Math.round(zoom / snap) * snap;
 
3751                 return Math.max(min, Math.min(max, zoom));
 
3754         _onPanTransitionStep: function () {
 
3758         _onPanTransitionEnd: function () {
 
3759                 L.DomUtil.removeClass(this._mapPane, 'leaflet-pan-anim');
 
3760                 this.fire('moveend');
 
3763         _tryAnimatedPan: function (center, options) {
 
3764                 // difference between the new and current centers in pixels
 
3765                 var offset = this._getCenterOffset(center)._floor();
 
3767                 // don't animate too far unless animate: true specified in options
 
3768                 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
 
3770                 this.panBy(offset, options);
 
3775         _createAnimProxy: function () {
 
3777                 var proxy = this._proxy = L.DomUtil.create('div', 'leaflet-proxy leaflet-zoom-animated');
 
3778                 this._panes.mapPane.appendChild(proxy);
 
3780                 this.on('zoomanim', function (e) {
 
3781                         var prop = L.DomUtil.TRANSFORM,
 
3782                             transform = proxy.style[prop];
 
3784                         L.DomUtil.setTransform(proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
 
3786                         // workaround for case when transform is the same and so transitionend event is not fired
 
3787                         if (transform === proxy.style[prop] && this._animatingZoom) {
 
3788                                 this._onZoomTransitionEnd();
 
3792                 this.on('load moveend', function () {
 
3793                         var c = this.getCenter(),
 
3795                         L.DomUtil.setTransform(proxy, this.project(c, z), this.getZoomScale(z, 1));
 
3799         _catchTransitionEnd: function (e) {
 
3800                 if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
 
3801                         this._onZoomTransitionEnd();
 
3805         _nothingToAnimate: function () {
 
3806                 return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
 
3809         _tryAnimatedZoom: function (center, zoom, options) {
 
3811                 if (this._animatingZoom) { return true; }
 
3813                 options = options || {};
 
3815                 // don't animate if disabled, not supported or zoom difference is too large
 
3816                 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
 
3817                         Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
 
3819                 // offset is the pixel coords of the zoom origin relative to the current center
 
3820                 var scale = this.getZoomScale(zoom),
 
3821                     offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
 
3823                 // don't animate if the zoom origin isn't within one screen from the current center, unless forced
 
3824                 if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
 
3826                 L.Util.requestAnimFrame(function () {
 
3829                             ._animateZoom(center, zoom, true);
 
3835         _animateZoom: function (center, zoom, startAnim, noUpdate) {
 
3837                         this._animatingZoom = true;
 
3839                         // remember what center/zoom to set after animation
 
3840                         this._animateToCenter = center;
 
3841                         this._animateToZoom = zoom;
 
3843                         L.DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim');
 
3846                 // @event zoomanim: ZoomAnimEvent
 
3847                 // Fired on every frame of a zoom animation
 
3848                 this.fire('zoomanim', {
 
3854                 // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
 
3855                 setTimeout(L.bind(this._onZoomTransitionEnd, this), 250);
 
3858         _onZoomTransitionEnd: function () {
 
3859                 if (!this._animatingZoom) { return; }
 
3861                 L.DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim');
 
3863                 this._animatingZoom = false;
 
3865                 this._move(this._animateToCenter, this._animateToZoom);
 
3867                 // This anim frame should prevent an obscure iOS webkit tile loading race condition.
 
3868                 L.Util.requestAnimFrame(function () {
 
3869                         this._moveEnd(true);
 
3876 // @factory L.map(id: String, options?: Map options)
 
3877 // Instantiates a map object given the DOM ID of a `<div>` element
 
3878 // and optionally an object literal with `Map options`.
 
3881 // @factory L.map(el: HTMLElement, options?: Map options)
 
3882 // Instantiates a map object given an instance of a `<div>` HTML element
 
3883 // and optionally an object literal with `Map options`.
 
3884 L.map = function (id, options) {
 
3885         return new L.Map(id, options);
 
3897  * A set of methods from the Layer base class that all Leaflet layers use.
 
3898  * Inherits all methods, options and events from `L.Evented`.
 
3903  * var layer = L.Marker(latlng).addTo(map);
 
3909  * Fired after the layer is added to a map
 
3911  * @event remove: Event
 
3912  * Fired after the layer is removed from a map
 
3916 L.Layer = L.Evented.extend({
 
3918         // Classes extending `L.Layer` will inherit the following options:
 
3920                 // @option pane: String = 'overlayPane'
 
3921                 // 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.
 
3922                 pane: 'overlayPane',
 
3923                 nonBubblingEvents: [],  // Array of events that should not be bubbled to DOM parents (like the map),
 
3925                 // @option attribution: String = null
 
3926                 // String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox".
 
3931          * Classes extending `L.Layer` will inherit the following methods:
 
3933          * @method addTo(map: Map): this
 
3934          * Adds the layer to the given map
 
3936         addTo: function (map) {
 
3941         // @method remove: this
 
3942         // Removes the layer from the map it is currently active on.
 
3943         remove: function () {
 
3944                 return this.removeFrom(this._map || this._mapToAdd);
 
3947         // @method removeFrom(map: Map): this
 
3948         // Removes the layer from the given map
 
3949         removeFrom: function (obj) {
 
3951                         obj.removeLayer(this);
 
3956         // @method getPane(name? : String): HTMLElement
 
3957         // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
 
3958         getPane: function (name) {
 
3959                 return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
 
3962         addInteractiveTarget: function (targetEl) {
 
3963                 this._map._targets[L.stamp(targetEl)] = this;
 
3967         removeInteractiveTarget: function (targetEl) {
 
3968                 delete this._map._targets[L.stamp(targetEl)];
 
3972         // @method getAttribution: String
 
3973         // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
 
3974         getAttribution: function () {
 
3975                 return this.options.attribution;
 
3978         _layerAdd: function (e) {
 
3981                 // check in case layer gets added and then removed before the map is ready
 
3982                 if (!map.hasLayer(this)) { return; }
 
3985                 this._zoomAnimated = map._zoomAnimated;
 
3987                 if (this.getEvents) {
 
3988                         var events = this.getEvents();
 
3989                         map.on(events, this);
 
3990                         this.once('remove', function () {
 
3991                                 map.off(events, this);
 
3997                 if (this.getAttribution && map.attributionControl) {
 
3998                         map.attributionControl.addAttribution(this.getAttribution());
 
4002                 map.fire('layeradd', {layer: this});
 
4006 /* @section Extension methods
 
4009  * Every layer should extend from `L.Layer` and (re-)implement the following methods.
 
4011  * @method onAdd(map: Map): this
 
4012  * 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).
 
4014  * @method onRemove(map: Map): this
 
4015  * 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).
 
4017  * @method getEvents(): Object
 
4018  * 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.
 
4020  * @method getAttribution(): String
 
4021  * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
 
4023  * @method beforeAdd(map: Map): this
 
4024  * 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.
 
4029  * @section Layer events
 
4031  * @event layeradd: LayerEvent
 
4032  * Fired when a new layer is added to the map.
 
4034  * @event layerremove: LayerEvent
 
4035  * Fired when some layer is removed from the map
 
4037  * @section Methods for Layers and Controls
 
4040         // @method addLayer(layer: Layer): this
 
4041         // Adds the given layer to the map
 
4042         addLayer: function (layer) {
 
4043                 var id = L.stamp(layer);
 
4044                 if (this._layers[id]) { return this; }
 
4045                 this._layers[id] = layer;
 
4047                 layer._mapToAdd = this;
 
4049                 if (layer.beforeAdd) {
 
4050                         layer.beforeAdd(this);
 
4053                 this.whenReady(layer._layerAdd, layer);
 
4058         // @method removeLayer(layer: Layer): this
 
4059         // Removes the given layer from the map.
 
4060         removeLayer: function (layer) {
 
4061                 var id = L.stamp(layer);
 
4063                 if (!this._layers[id]) { return this; }
 
4066                         layer.onRemove(this);
 
4069                 if (layer.getAttribution && this.attributionControl) {
 
4070                         this.attributionControl.removeAttribution(layer.getAttribution());
 
4073                 delete this._layers[id];
 
4076                         this.fire('layerremove', {layer: layer});
 
4077                         layer.fire('remove');
 
4080                 layer._map = layer._mapToAdd = null;
 
4085         // @method hasLayer(layer: Layer): Boolean
 
4086         // Returns `true` if the given layer is currently added to the map
 
4087         hasLayer: function (layer) {
 
4088                 return !!layer && (L.stamp(layer) in this._layers);
 
4091         /* @method eachLayer(fn: Function, context?: Object): this
 
4092          * Iterates over the layers of the map, optionally specifying context of the iterator function.
 
4094          * map.eachLayer(function(layer){
 
4095          *     layer.bindPopup('Hello');
 
4099         eachLayer: function (method, context) {
 
4100                 for (var i in this._layers) {
 
4101                         method.call(context, this._layers[i]);
 
4106         _addLayers: function (layers) {
 
4107                 layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : [];
 
4109                 for (var i = 0, len = layers.length; i < len; i++) {
 
4110                         this.addLayer(layers[i]);
 
4114         _addZoomLimit: function (layer) {
 
4115                 if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
 
4116                         this._zoomBoundLayers[L.stamp(layer)] = layer;
 
4117                         this._updateZoomLevels();
 
4121         _removeZoomLimit: function (layer) {
 
4122                 var id = L.stamp(layer);
 
4124                 if (this._zoomBoundLayers[id]) {
 
4125                         delete this._zoomBoundLayers[id];
 
4126                         this._updateZoomLevels();
 
4130         _updateZoomLevels: function () {
 
4131                 var minZoom = Infinity,
 
4132                     maxZoom = -Infinity,
 
4133                     oldZoomSpan = this._getZoomSpan();
 
4135                 for (var i in this._zoomBoundLayers) {
 
4136                         var options = this._zoomBoundLayers[i].options;
 
4138                         minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
 
4139                         maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
 
4142                 this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
 
4143                 this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
 
4145                 // @section Map state change events
 
4146                 // @event zoomlevelschange: Event
 
4147                 // Fired when the number of zoomlevels on the map is changed due
 
4148                 // to adding or removing a layer.
 
4149                 if (oldZoomSpan !== this._getZoomSpan()) {
 
4150                         this.fire('zoomlevelschange');
 
4153                 if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
 
4154                         this.setZoom(this._layersMaxZoom);
 
4156                 if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
 
4157                         this.setZoom(this._layersMinZoom);
 
4165  * @namespace DomEvent
 
4166  * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
 
4169 // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
 
4173 var eventsKey = '_leaflet_events';
 
4177         // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
 
4178         // Adds a listener function (`fn`) to a particular DOM event type of the
 
4179         // element `el`. You can optionally specify the context of the listener
 
4180         // (object the `this` keyword will point to). You can also pass several
 
4181         // space-separated types (e.g. `'click dblclick'`).
 
4184         // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
 
4185         // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
4186         on: function (obj, types, fn, context) {
 
4188                 if (typeof types === 'object') {
 
4189                         for (var type in types) {
 
4190                                 this._on(obj, type, types[type], fn);
 
4193                         types = L.Util.splitWords(types);
 
4195                         for (var i = 0, len = types.length; i < len; i++) {
 
4196                                 this._on(obj, types[i], fn, context);
 
4203         // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
 
4204         // Removes a previously added listener function. If no function is specified,
 
4205         // it will remove all the listeners of that particular DOM event from the element.
 
4206         // Note that if you passed a custom context to on, you must pass the same
 
4207         // context to `off` in order to remove the listener.
 
4210         // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
 
4211         // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
4212         off: function (obj, types, fn, context) {
 
4214                 if (typeof types === 'object') {
 
4215                         for (var type in types) {
 
4216                                 this._off(obj, type, types[type], fn);
 
4219                         types = L.Util.splitWords(types);
 
4221                         for (var i = 0, len = types.length; i < len; i++) {
 
4222                                 this._off(obj, types[i], fn, context);
 
4229         _on: function (obj, type, fn, context) {
 
4230                 var id = type + L.stamp(fn) + (context ? '_' + L.stamp(context) : '');
 
4232                 if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
 
4234                 var handler = function (e) {
 
4235                         return fn.call(context || obj, e || window.event);
 
4238                 var originalHandler = handler;
 
4240                 if (L.Browser.pointer && type.indexOf('touch') === 0) {
 
4241                         this.addPointerListener(obj, type, handler, id);
 
4243                 } else if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener &&
 
4244                            !(L.Browser.pointer && L.Browser.chrome)) {
 
4245                         // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
 
4247                         this.addDoubleTapListener(obj, handler, id);
 
4249                 } else if ('addEventListener' in obj) {
 
4251                         if (type === 'mousewheel') {
 
4252                                 obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
 
4254                         } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
 
4255                                 handler = function (e) {
 
4256                                         e = e || window.event;
 
4257                                         if (L.DomEvent._isExternalTarget(obj, e)) {
 
4261                                 obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
 
4264                                 if (type === 'click' && L.Browser.android) {
 
4265                                         handler = function (e) {
 
4266                                                 return L.DomEvent._filterClick(e, originalHandler);
 
4269                                 obj.addEventListener(type, handler, false);
 
4272                 } else if ('attachEvent' in obj) {
 
4273                         obj.attachEvent('on' + type, handler);
 
4276                 obj[eventsKey] = obj[eventsKey] || {};
 
4277                 obj[eventsKey][id] = handler;
 
4282         _off: function (obj, type, fn, context) {
 
4284                 var id = type + L.stamp(fn) + (context ? '_' + L.stamp(context) : ''),
 
4285                     handler = obj[eventsKey] && obj[eventsKey][id];
 
4287                 if (!handler) { return this; }
 
4289                 if (L.Browser.pointer && type.indexOf('touch') === 0) {
 
4290                         this.removePointerListener(obj, type, id);
 
4292                 } else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) {
 
4293                         this.removeDoubleTapListener(obj, id);
 
4295                 } else if ('removeEventListener' in obj) {
 
4297                         if (type === 'mousewheel') {
 
4298                                 obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
 
4301                                 obj.removeEventListener(
 
4302                                         type === 'mouseenter' ? 'mouseover' :
 
4303                                         type === 'mouseleave' ? 'mouseout' : type, handler, false);
 
4306                 } else if ('detachEvent' in obj) {
 
4307                         obj.detachEvent('on' + type, handler);
 
4310                 obj[eventsKey][id] = null;
 
4315         // @function stopPropagation(ev: DOMEvent): this
 
4316         // Stop the given event from propagation to parent elements. Used inside the listener functions:
 
4318         // L.DomEvent.on(div, 'click', function (ev) {
 
4319         //      L.DomEvent.stopPropagation(ev);
 
4322         stopPropagation: function (e) {
 
4324                 if (e.stopPropagation) {
 
4325                         e.stopPropagation();
 
4326                 } else if (e.originalEvent) {  // In case of Leaflet event.
 
4327                         e.originalEvent._stopped = true;
 
4329                         e.cancelBubble = true;
 
4331                 L.DomEvent._skipped(e);
 
4336         // @function disableScrollPropagation(el: HTMLElement): this
 
4337         // Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
 
4338         disableScrollPropagation: function (el) {
 
4339                 return L.DomEvent.on(el, 'mousewheel', L.DomEvent.stopPropagation);
 
4342         // @function disableClickPropagation(el: HTMLElement): this
 
4343         // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
 
4344         // `'mousedown'` and `'touchstart'` events (plus browser variants).
 
4345         disableClickPropagation: function (el) {
 
4346                 var stop = L.DomEvent.stopPropagation;
 
4348                 L.DomEvent.on(el, L.Draggable.START.join(' '), stop);
 
4350                 return L.DomEvent.on(el, {
 
4351                         click: L.DomEvent._fakeStop,
 
4356         // @function preventDefault(ev: DOMEvent): this
 
4357         // Prevents the default action of the DOM Event `ev` from happening (such as
 
4358         // following a link in the href of the a element, or doing a POST request
 
4359         // with page reload when a `<form>` is submitted).
 
4360         // Use it inside listener functions.
 
4361         preventDefault: function (e) {
 
4363                 if (e.preventDefault) {
 
4366                         e.returnValue = false;
 
4371         // @function stop(ev): this
 
4372         // Does `stopPropagation` and `preventDefault` at the same time.
 
4373         stop: function (e) {
 
4376                         .stopPropagation(e);
 
4379         // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
 
4380         // Gets normalized mouse position from a DOM event relative to the
 
4381         // `container` or to the whole page if not specified.
 
4382         getMousePosition: function (e, container) {
 
4384                         return new L.Point(e.clientX, e.clientY);
 
4387                 var rect = container.getBoundingClientRect();
 
4390                         e.clientX - rect.left - container.clientLeft,
 
4391                         e.clientY - rect.top - container.clientTop);
 
4394         // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
 
4395         // and Firefox scrolls device pixels, not CSS pixels
 
4396         _wheelPxFactor: (L.Browser.win && L.Browser.chrome) ? 2 :
 
4397                         L.Browser.gecko ? window.devicePixelRatio :
 
4400         // @function getWheelDelta(ev: DOMEvent): Number
 
4401         // Gets normalized wheel delta from a mousewheel DOM event, in vertical
 
4402         // pixels scrolled (negative if scrolling down).
 
4403         // Events from pointing devices without precise scrolling are mapped to
 
4404         // a best guess of 60 pixels.
 
4405         getWheelDelta: function (e) {
 
4406                 return (L.Browser.edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
 
4407                        (e.deltaY && e.deltaMode === 0) ? -e.deltaY / L.DomEvent._wheelPxFactor : // Pixels
 
4408                        (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
 
4409                        (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
 
4410                        (e.deltaX || e.deltaZ) ? 0 :     // Skip horizontal/depth wheel events
 
4411                        e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
 
4412                        (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
 
4413                        e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
 
4419         _fakeStop: function (e) {
 
4420                 // fakes stopPropagation by setting a special event flag, checked/reset with L.DomEvent._skipped(e)
 
4421                 L.DomEvent._skipEvents[e.type] = true;
 
4424         _skipped: function (e) {
 
4425                 var skipped = this._skipEvents[e.type];
 
4426                 // reset when checking, as it's only used in map container and propagates outside of the map
 
4427                 this._skipEvents[e.type] = false;
 
4431         // check if element really left/entered the event target (for mouseenter/mouseleave)
 
4432         _isExternalTarget: function (el, e) {
 
4434                 var related = e.relatedTarget;
 
4436                 if (!related) { return true; }
 
4439                         while (related && (related !== el)) {
 
4440                                 related = related.parentNode;
 
4445                 return (related !== el);
 
4448         // this is a horrible workaround for a bug in Android where a single touch triggers two click events
 
4449         _filterClick: function (e, handler) {
 
4450                 var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
 
4451                     elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick);
 
4453                 // are they closer together than 500ms yet more than 100ms?
 
4454                 // Android typically triggers them ~300ms apart while multiple listeners
 
4455                 // on the same event should be triggered far faster;
 
4456                 // or check if click is simulated on the element, and if it is, reject any non-simulated events
 
4458                 if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
 
4462                 L.DomEvent._lastClick = timeStamp;
 
4468 // @function addListener(…): this
 
4469 // Alias to [`L.DomEvent.on`](#domevent-on)
 
4470 L.DomEvent.addListener = L.DomEvent.on;
 
4472 // @function removeListener(…): this
 
4473 // Alias to [`L.DomEvent.off`](#domevent-off)
 
4474 L.DomEvent.removeListener = L.DomEvent.off;
 
4479  * @class PosAnimation
 
4480  * @aka L.PosAnimation
 
4482  * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
 
4486  * var fx = new L.PosAnimation();
 
4487  * fx.run(el, [300, 500], 0.5);
 
4490  * @constructor L.PosAnimation()
 
4491  * Creates a `PosAnimation` object.
 
4495 L.PosAnimation = L.Evented.extend({
 
4497         // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
 
4498         // Run an animation of a given element to a new position, optionally setting
 
4499         // duration in seconds (`0.25` by default) and easing linearity factor (3rd
 
4500         // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
 
4501         // `0.5` by default).
 
4502         run: function (el, newPos, duration, easeLinearity) {
 
4506                 this._inProgress = true;
 
4507                 this._duration = duration || 0.25;
 
4508                 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
 
4510                 this._startPos = L.DomUtil.getPosition(el);
 
4511                 this._offset = newPos.subtract(this._startPos);
 
4512                 this._startTime = +new Date();
 
4514                 // @event start: Event
 
4515                 // Fired when the animation starts
 
4522         // Stops the animation (if currently running).
 
4524                 if (!this._inProgress) { return; }
 
4530         _animate: function () {
 
4532                 this._animId = L.Util.requestAnimFrame(this._animate, this);
 
4536         _step: function (round) {
 
4537                 var elapsed = (+new Date()) - this._startTime,
 
4538                     duration = this._duration * 1000;
 
4540                 if (elapsed < duration) {
 
4541                         this._runFrame(this._easeOut(elapsed / duration), round);
 
4548         _runFrame: function (progress, round) {
 
4549                 var pos = this._startPos.add(this._offset.multiplyBy(progress));
 
4553                 L.DomUtil.setPosition(this._el, pos);
 
4555                 // @event step: Event
 
4556                 // Fired continuously during the animation.
 
4560         _complete: function () {
 
4561                 L.Util.cancelAnimFrame(this._animId);
 
4563                 this._inProgress = false;
 
4564                 // @event end: Event
 
4565                 // Fired when the animation ends.
 
4569         _easeOut: function (t) {
 
4570                 return 1 - Math.pow(1 - t, this._easeOutPower);
 
4577  * @namespace Projection
 
4578  * @projection L.Projection.Mercator
 
4580  * 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.
 
4583 L.Projection.Mercator = {
 
4585         R_MINOR: 6356752.314245179,
 
4587         bounds: L.bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
 
4589         project: function (latlng) {
 
4590                 var d = Math.PI / 180,
 
4593                     tmp = this.R_MINOR / r,
 
4594                     e = Math.sqrt(1 - tmp * tmp),
 
4595                     con = e * Math.sin(y);
 
4597                 var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
 
4598                 y = -r * Math.log(Math.max(ts, 1E-10));
 
4600                 return new L.Point(latlng.lng * d * r, y);
 
4603         unproject: function (point) {
 
4604                 var d = 180 / Math.PI,
 
4606                     tmp = this.R_MINOR / r,
 
4607                     e = Math.sqrt(1 - tmp * tmp),
 
4608                     ts = Math.exp(-point.y / r),
 
4609                     phi = Math.PI / 2 - 2 * Math.atan(ts);
 
4611                 for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
 
4612                         con = e * Math.sin(phi);
 
4613                         con = Math.pow((1 - con) / (1 + con), e / 2);
 
4614                         dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
 
4618                 return new L.LatLng(phi * d, point.x * d / r);
 
4626  * @crs L.CRS.EPSG3395
 
4628  * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
 
4631 L.CRS.EPSG3395 = L.extend({}, L.CRS.Earth, {
 
4633         projection: L.Projection.Mercator,
 
4635         transformation: (function () {
 
4636                 var scale = 0.5 / (Math.PI * L.Projection.Mercator.R);
 
4637                 return new L.Transformation(scale, 0.5, -scale, 0.5);
 
4648  * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
 
4649  * 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.
 
4652  * @section Synchronous usage
 
4655  * 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.
 
4658  * var CanvasLayer = L.GridLayer.extend({
 
4659  *     createTile: function(coords){
 
4660  *         // create a <canvas> element for drawing
 
4661  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
4663  *         // setup tile width and height according to the options
 
4664  *         var size = this.getTileSize();
 
4665  *         tile.width = size.x;
 
4666  *         tile.height = size.y;
 
4668  *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
 
4669  *         var ctx = tile.getContext('2d');
 
4671  *         // return the tile so it can be rendered on screen
 
4677  * @section Asynchronous usage
 
4680  * 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.
 
4683  * var CanvasLayer = L.GridLayer.extend({
 
4684  *     createTile: function(coords, done){
 
4687  *         // create a <canvas> element for drawing
 
4688  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
4690  *         // setup tile width and height according to the options
 
4691  *         var size = this.getTileSize();
 
4692  *         tile.width = size.x;
 
4693  *         tile.height = size.y;
 
4695  *         // draw something asynchronously and pass the tile to the done() callback
 
4696  *         setTimeout(function() {
 
4697  *             done(error, tile);
 
4709 L.GridLayer = L.Layer.extend({
 
4712         // @aka GridLayer options
 
4714                 // @option tileSize: Number|Point = 256
 
4715                 // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
 
4718                 // @option opacity: Number = 1.0
 
4719                 // Opacity of the tiles. Can be used in the `createTile()` function.
 
4722                 // @option updateWhenIdle: Boolean = depends
 
4723                 // If `false`, new tiles are loaded during panning, otherwise only after it (for better performance). `true` by default on mobile browsers, otherwise `false`.
 
4724                 updateWhenIdle: L.Browser.mobile,
 
4726                 // @option updateWhenZooming: Boolean = true
 
4727                 // 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.
 
4728                 updateWhenZooming: true,
 
4730                 // @option updateInterval: Number = 200
 
4731                 // Tiles will not update more than once every `updateInterval` milliseconds when panning.
 
4732                 updateInterval: 200,
 
4734                 // @option zIndex: Number = 1
 
4735                 // The explicit zIndex of the tile layer.
 
4738                 // @option bounds: LatLngBounds = undefined
 
4739                 // If set, tiles will only be loaded inside the set `LatLngBounds`.
 
4742                 // @option minZoom: Number = 0
 
4743                 // The minimum zoom level that tiles will be loaded at. By default the entire map.
 
4746                 // @option maxZoom: Number = undefined
 
4747                 // The maximum zoom level that tiles will be loaded at.
 
4750                 // @option noWrap: Boolean = false
 
4751                 // Whether the layer is wrapped around the antimeridian. If `true`, the
 
4752                 // GridLayer will only be displayed once at low zoom levels. Has no
 
4753                 // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
 
4754                 // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
 
4755                 // tiles outside the CRS limits.
 
4758                 // @option pane: String = 'tilePane'
 
4759                 // `Map pane` where the grid layer will be added.
 
4762                 // @option className: String = ''
 
4763                 // A custom class name to assign to the tile layer. Empty by default.
 
4766                 // @option keepBuffer: Number = 2
 
4767                 // When panning the map, keep this many rows and columns of tiles before unloading them.
 
4771         initialize: function (options) {
 
4772                 L.setOptions(this, options);
 
4775         onAdd: function () {
 
4776                 this._initContainer();
 
4785         beforeAdd: function (map) {
 
4786                 map._addZoomLimit(this);
 
4789         onRemove: function (map) {
 
4790                 this._removeAllTiles();
 
4791                 L.DomUtil.remove(this._container);
 
4792                 map._removeZoomLimit(this);
 
4793                 this._container = null;
 
4794                 this._tileZoom = null;
 
4797         // @method bringToFront: this
 
4798         // Brings the tile layer to the top of all tile layers.
 
4799         bringToFront: function () {
 
4801                         L.DomUtil.toFront(this._container);
 
4802                         this._setAutoZIndex(Math.max);
 
4807         // @method bringToBack: this
 
4808         // Brings the tile layer to the bottom of all tile layers.
 
4809         bringToBack: function () {
 
4811                         L.DomUtil.toBack(this._container);
 
4812                         this._setAutoZIndex(Math.min);
 
4817         // @method getContainer: HTMLElement
 
4818         // Returns the HTML element that contains the tiles for this layer.
 
4819         getContainer: function () {
 
4820                 return this._container;
 
4823         // @method setOpacity(opacity: Number): this
 
4824         // Changes the [opacity](#gridlayer-opacity) of the grid layer.
 
4825         setOpacity: function (opacity) {
 
4826                 this.options.opacity = opacity;
 
4827                 this._updateOpacity();
 
4831         // @method setZIndex(zIndex: Number): this
 
4832         // Changes the [zIndex](#gridlayer-zindex) of the grid layer.
 
4833         setZIndex: function (zIndex) {
 
4834                 this.options.zIndex = zIndex;
 
4835                 this._updateZIndex();
 
4840         // @method isLoading: Boolean
 
4841         // Returns `true` if any tile in the grid layer has not finished loading.
 
4842         isLoading: function () {
 
4843                 return this._loading;
 
4846         // @method redraw: this
 
4847         // Causes the layer to clear all the tiles and request them again.
 
4848         redraw: function () {
 
4850                         this._removeAllTiles();
 
4856         getEvents: function () {
 
4858                         viewprereset: this._invalidateAll,
 
4859                         viewreset: this._resetView,
 
4860                         zoom: this._resetView,
 
4861                         moveend: this._onMoveEnd
 
4864                 if (!this.options.updateWhenIdle) {
 
4865                         // update tiles on move, but not more often than once per given interval
 
4866                         if (!this._onMove) {
 
4867                                 this._onMove = L.Util.throttle(this._onMoveEnd, this.options.updateInterval, this);
 
4870                         events.move = this._onMove;
 
4873                 if (this._zoomAnimated) {
 
4874                         events.zoomanim = this._animateZoom;
 
4880         // @section Extension methods
 
4881         // Layers extending `GridLayer` shall reimplement the following method.
 
4882         // @method createTile(coords: Object, done?: Function): HTMLElement
 
4883         // Called only internally, must be overriden by classes extending `GridLayer`.
 
4884         // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
 
4885         // is specified, it must be called when the tile has finished loading and drawing.
 
4886         createTile: function () {
 
4887                 return document.createElement('div');
 
4891         // @method getTileSize: Point
 
4892         // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
 
4893         getTileSize: function () {
 
4894                 var s = this.options.tileSize;
 
4895                 return s instanceof L.Point ? s : new L.Point(s, s);
 
4898         _updateZIndex: function () {
 
4899                 if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
 
4900                         this._container.style.zIndex = this.options.zIndex;
 
4904         _setAutoZIndex: function (compare) {
 
4905                 // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
 
4907                 var layers = this.getPane().children,
 
4908                     edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
 
4910                 for (var i = 0, len = layers.length, zIndex; i < len; i++) {
 
4912                         zIndex = layers[i].style.zIndex;
 
4914                         if (layers[i] !== this._container && zIndex) {
 
4915                                 edgeZIndex = compare(edgeZIndex, +zIndex);
 
4919                 if (isFinite(edgeZIndex)) {
 
4920                         this.options.zIndex = edgeZIndex + compare(-1, 1);
 
4921                         this._updateZIndex();
 
4925         _updateOpacity: function () {
 
4926                 if (!this._map) { return; }
 
4928                 // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
 
4929                 if (L.Browser.ielt9) { return; }
 
4931                 L.DomUtil.setOpacity(this._container, this.options.opacity);
 
4933                 var now = +new Date(),
 
4937                 for (var key in this._tiles) {
 
4938                         var tile = this._tiles[key];
 
4939                         if (!tile.current || !tile.loaded) { continue; }
 
4941                         var fade = Math.min(1, (now - tile.loaded) / 200);
 
4943                         L.DomUtil.setOpacity(tile.el, fade);
 
4947                                 if (tile.active) { willPrune = true; }
 
4952                 if (willPrune && !this._noPrune) { this._pruneTiles(); }
 
4955                         L.Util.cancelAnimFrame(this._fadeFrame);
 
4956                         this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this);
 
4960         _initContainer: function () {
 
4961                 if (this._container) { return; }
 
4963                 this._container = L.DomUtil.create('div', 'leaflet-layer ' + (this.options.className || ''));
 
4964                 this._updateZIndex();
 
4966                 if (this.options.opacity < 1) {
 
4967                         this._updateOpacity();
 
4970                 this.getPane().appendChild(this._container);
 
4973         _updateLevels: function () {
 
4975                 var zoom = this._tileZoom,
 
4976                     maxZoom = this.options.maxZoom;
 
4978                 if (zoom === undefined) { return undefined; }
 
4980                 for (var z in this._levels) {
 
4981                         if (this._levels[z].el.children.length || z === zoom) {
 
4982                                 this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
 
4984                                 L.DomUtil.remove(this._levels[z].el);
 
4985                                 this._removeTilesAtZoom(z);
 
4986                                 delete this._levels[z];
 
4990                 var level = this._levels[zoom],
 
4994                         level = this._levels[zoom] = {};
 
4996                         level.el = L.DomUtil.create('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
 
4997                         level.el.style.zIndex = maxZoom;
 
4999                         level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
 
5002                         this._setZoomTransform(level, map.getCenter(), map.getZoom());
 
5004                         // force the browser to consider the newly added element for transition
 
5005                         L.Util.falseFn(level.el.offsetWidth);
 
5008                 this._level = level;
 
5013         _pruneTiles: function () {
 
5020                 var zoom = this._map.getZoom();
 
5021                 if (zoom > this.options.maxZoom ||
 
5022                         zoom < this.options.minZoom) {
 
5023                         this._removeAllTiles();
 
5027                 for (key in this._tiles) {
 
5028                         tile = this._tiles[key];
 
5029                         tile.retain = tile.current;
 
5032                 for (key in this._tiles) {
 
5033                         tile = this._tiles[key];
 
5034                         if (tile.current && !tile.active) {
 
5035                                 var coords = tile.coords;
 
5036                                 if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
 
5037                                         this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
 
5042                 for (key in this._tiles) {
 
5043                         if (!this._tiles[key].retain) {
 
5044                                 this._removeTile(key);
 
5049         _removeTilesAtZoom: function (zoom) {
 
5050                 for (var key in this._tiles) {
 
5051                         if (this._tiles[key].coords.z !== zoom) {
 
5054                         this._removeTile(key);
 
5058         _removeAllTiles: function () {
 
5059                 for (var key in this._tiles) {
 
5060                         this._removeTile(key);
 
5064         _invalidateAll: function () {
 
5065                 for (var z in this._levels) {
 
5066                         L.DomUtil.remove(this._levels[z].el);
 
5067                         delete this._levels[z];
 
5069                 this._removeAllTiles();
 
5071                 this._tileZoom = null;
 
5074         _retainParent: function (x, y, z, minZoom) {
 
5075                 var x2 = Math.floor(x / 2),
 
5076                     y2 = Math.floor(y / 2),
 
5078                     coords2 = new L.Point(+x2, +y2);
 
5081                 var key = this._tileCoordsToKey(coords2),
 
5082                     tile = this._tiles[key];
 
5084                 if (tile && tile.active) {
 
5088                 } else if (tile && tile.loaded) {
 
5093                         return this._retainParent(x2, y2, z2, minZoom);
 
5099         _retainChildren: function (x, y, z, maxZoom) {
 
5101                 for (var i = 2 * x; i < 2 * x + 2; i++) {
 
5102                         for (var j = 2 * y; j < 2 * y + 2; j++) {
 
5104                                 var coords = new L.Point(i, j);
 
5107                                 var key = this._tileCoordsToKey(coords),
 
5108                                     tile = this._tiles[key];
 
5110                                 if (tile && tile.active) {
 
5114                                 } else if (tile && tile.loaded) {
 
5118                                 if (z + 1 < maxZoom) {
 
5119                                         this._retainChildren(i, j, z + 1, maxZoom);
 
5125         _resetView: function (e) {
 
5126                 var animating = e && (e.pinch || e.flyTo);
 
5127                 this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
 
5130         _animateZoom: function (e) {
 
5131                 this._setView(e.center, e.zoom, true, e.noUpdate);
 
5134         _setView: function (center, zoom, noPrune, noUpdate) {
 
5135                 var tileZoom = Math.round(zoom);
 
5136                 if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
 
5137                     (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
 
5138                         tileZoom = undefined;
 
5141                 var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
 
5143                 if (!noUpdate || tileZoomChanged) {
 
5145                         this._tileZoom = tileZoom;
 
5147                         if (this._abortLoading) {
 
5148                                 this._abortLoading();
 
5151                         this._updateLevels();
 
5154                         if (tileZoom !== undefined) {
 
5155                                 this._update(center);
 
5162                         // Flag to prevent _updateOpacity from pruning tiles during
 
5163                         // a zoom anim or a pinch gesture
 
5164                         this._noPrune = !!noPrune;
 
5167                 this._setZoomTransforms(center, zoom);
 
5170         _setZoomTransforms: function (center, zoom) {
 
5171                 for (var i in this._levels) {
 
5172                         this._setZoomTransform(this._levels[i], center, zoom);
 
5176         _setZoomTransform: function (level, center, zoom) {
 
5177                 var scale = this._map.getZoomScale(zoom, level.zoom),
 
5178                     translate = level.origin.multiplyBy(scale)
 
5179                         .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
 
5181                 if (L.Browser.any3d) {
 
5182                         L.DomUtil.setTransform(level.el, translate, scale);
 
5184                         L.DomUtil.setPosition(level.el, translate);
 
5188         _resetGrid: function () {
 
5189                 var map = this._map,
 
5190                     crs = map.options.crs,
 
5191                     tileSize = this._tileSize = this.getTileSize(),
 
5192                     tileZoom = this._tileZoom;
 
5194                 var bounds = this._map.getPixelWorldBounds(this._tileZoom);
 
5196                         this._globalTileRange = this._pxBoundsToTileRange(bounds);
 
5199                 this._wrapX = crs.wrapLng && !this.options.noWrap && [
 
5200                         Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
 
5201                         Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
 
5203                 this._wrapY = crs.wrapLat && !this.options.noWrap && [
 
5204                         Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
 
5205                         Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
 
5209         _onMoveEnd: function () {
 
5210                 if (!this._map || this._map._animatingZoom) { return; }
 
5215         _getTiledPixelBounds: function (center) {
 
5216                 var map = this._map,
 
5217                     mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
 
5218                     scale = map.getZoomScale(mapZoom, this._tileZoom),
 
5219                     pixelCenter = map.project(center, this._tileZoom).floor(),
 
5220                     halfSize = map.getSize().divideBy(scale * 2);
 
5222                 return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
 
5225         // Private method to load tiles in the grid's active zoom level according to map bounds
 
5226         _update: function (center) {
 
5227                 var map = this._map;
 
5228                 if (!map) { return; }
 
5229                 var zoom = map.getZoom();
 
5231                 if (center === undefined) { center = map.getCenter(); }
 
5232                 if (this._tileZoom === undefined) { return; }   // if out of minzoom/maxzoom
 
5234                 var pixelBounds = this._getTiledPixelBounds(center),
 
5235                     tileRange = this._pxBoundsToTileRange(pixelBounds),
 
5236                     tileCenter = tileRange.getCenter(),
 
5238                     margin = this.options.keepBuffer,
 
5239                     noPruneRange = new L.Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
 
5240                                               tileRange.getTopRight().add([margin, -margin]));
 
5242                 for (var key in this._tiles) {
 
5243                         var c = this._tiles[key].coords;
 
5244                         if (c.z !== this._tileZoom || !noPruneRange.contains(L.point(c.x, c.y))) {
 
5245                                 this._tiles[key].current = false;
 
5249                 // _update just loads more tiles. If the tile zoom level differs too much
 
5250                 // from the map's, let _setView reset levels and prune old tiles.
 
5251                 if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
 
5253                 // create a queue of coordinates to load tiles from
 
5254                 for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
 
5255                         for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
 
5256                                 var coords = new L.Point(i, j);
 
5257                                 coords.z = this._tileZoom;
 
5259                                 if (!this._isValidTile(coords)) { continue; }
 
5261                                 var tile = this._tiles[this._tileCoordsToKey(coords)];
 
5263                                         tile.current = true;
 
5270                 // sort tile queue to load tiles in order of their distance to center
 
5271                 queue.sort(function (a, b) {
 
5272                         return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
 
5275                 if (queue.length !== 0) {
 
5276                         // if it's the first batch of tiles to load
 
5277                         if (!this._loading) {
 
5278                                 this._loading = true;
 
5279                                 // @event loading: Event
 
5280                                 // Fired when the grid layer starts loading tiles.
 
5281                                 this.fire('loading');
 
5284                         // create DOM fragment to append tiles in one batch
 
5285                         var fragment = document.createDocumentFragment();
 
5287                         for (i = 0; i < queue.length; i++) {
 
5288                                 this._addTile(queue[i], fragment);
 
5291                         this._level.el.appendChild(fragment);
 
5295         _isValidTile: function (coords) {
 
5296                 var crs = this._map.options.crs;
 
5298                 if (!crs.infinite) {
 
5299                         // don't load tile if it's out of bounds and not wrapped
 
5300                         var bounds = this._globalTileRange;
 
5301                         if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
 
5302                             (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
 
5305                 if (!this.options.bounds) { return true; }
 
5307                 // don't load tile if it doesn't intersect the bounds in options
 
5308                 var tileBounds = this._tileCoordsToBounds(coords);
 
5309                 return L.latLngBounds(this.options.bounds).overlaps(tileBounds);
 
5312         _keyToBounds: function (key) {
 
5313                 return this._tileCoordsToBounds(this._keyToTileCoords(key));
 
5316         // converts tile coordinates to its geographical bounds
 
5317         _tileCoordsToBounds: function (coords) {
 
5319                 var map = this._map,
 
5320                     tileSize = this.getTileSize(),
 
5322                     nwPoint = coords.scaleBy(tileSize),
 
5323                     sePoint = nwPoint.add(tileSize),
 
5325                     nw = map.unproject(nwPoint, coords.z),
 
5326                     se = map.unproject(sePoint, coords.z),
 
5327                     bounds = new L.LatLngBounds(nw, se);
 
5329                 if (!this.options.noWrap) {
 
5330                         map.wrapLatLngBounds(bounds);
 
5336         // converts tile coordinates to key for the tile cache
 
5337         _tileCoordsToKey: function (coords) {
 
5338                 return coords.x + ':' + coords.y + ':' + coords.z;
 
5341         // converts tile cache key to coordinates
 
5342         _keyToTileCoords: function (key) {
 
5343                 var k = key.split(':'),
 
5344                     coords = new L.Point(+k[0], +k[1]);
 
5349         _removeTile: function (key) {
 
5350                 var tile = this._tiles[key];
 
5351                 if (!tile) { return; }
 
5353                 L.DomUtil.remove(tile.el);
 
5355                 delete this._tiles[key];
 
5357                 // @event tileunload: TileEvent
 
5358                 // Fired when a tile is removed (e.g. when a tile goes off the screen).
 
5359                 this.fire('tileunload', {
 
5361                         coords: this._keyToTileCoords(key)
 
5365         _initTile: function (tile) {
 
5366                 L.DomUtil.addClass(tile, 'leaflet-tile');
 
5368                 var tileSize = this.getTileSize();
 
5369                 tile.style.width = tileSize.x + 'px';
 
5370                 tile.style.height = tileSize.y + 'px';
 
5372                 tile.onselectstart = L.Util.falseFn;
 
5373                 tile.onmousemove = L.Util.falseFn;
 
5375                 // update opacity on tiles in IE7-8 because of filter inheritance problems
 
5376                 if (L.Browser.ielt9 && this.options.opacity < 1) {
 
5377                         L.DomUtil.setOpacity(tile, this.options.opacity);
 
5380                 // without this hack, tiles disappear after zoom on Chrome for Android
 
5381                 // https://github.com/Leaflet/Leaflet/issues/2078
 
5382                 if (L.Browser.android && !L.Browser.android23) {
 
5383                         tile.style.WebkitBackfaceVisibility = 'hidden';
 
5387         _addTile: function (coords, container) {
 
5388                 var tilePos = this._getTilePos(coords),
 
5389                     key = this._tileCoordsToKey(coords);
 
5391                 var tile = this.createTile(this._wrapCoords(coords), L.bind(this._tileReady, this, coords));
 
5393                 this._initTile(tile);
 
5395                 // if createTile is defined with a second argument ("done" callback),
 
5396                 // we know that tile is async and will be ready later; otherwise
 
5397                 if (this.createTile.length < 2) {
 
5398                         // mark tile as ready, but delay one frame for opacity animation to happen
 
5399                         L.Util.requestAnimFrame(L.bind(this._tileReady, this, coords, null, tile));
 
5402                 L.DomUtil.setPosition(tile, tilePos);
 
5404                 // save tile in cache
 
5405                 this._tiles[key] = {
 
5411                 container.appendChild(tile);
 
5412                 // @event tileloadstart: TileEvent
 
5413                 // Fired when a tile is requested and starts loading.
 
5414                 this.fire('tileloadstart', {
 
5420         _tileReady: function (coords, err, tile) {
 
5421                 if (!this._map) { return; }
 
5424                         // @event tileerror: TileErrorEvent
 
5425                         // Fired when there is an error loading a tile.
 
5426                         this.fire('tileerror', {
 
5433                 var key = this._tileCoordsToKey(coords);
 
5435                 tile = this._tiles[key];
 
5436                 if (!tile) { return; }
 
5438                 tile.loaded = +new Date();
 
5439                 if (this._map._fadeAnimated) {
 
5440                         L.DomUtil.setOpacity(tile.el, 0);
 
5441                         L.Util.cancelAnimFrame(this._fadeFrame);
 
5442                         this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this);
 
5449                         L.DomUtil.addClass(tile.el, 'leaflet-tile-loaded');
 
5451                         // @event tileload: TileEvent
 
5452                         // Fired when a tile loads.
 
5453                         this.fire('tileload', {
 
5459                 if (this._noTilesToLoad()) {
 
5460                         this._loading = false;
 
5461                         // @event load: Event
 
5462                         // Fired when the grid layer loaded all visible tiles.
 
5465                         if (L.Browser.ielt9 || !this._map._fadeAnimated) {
 
5466                                 L.Util.requestAnimFrame(this._pruneTiles, this);
 
5468                                 // Wait a bit more than 0.2 secs (the duration of the tile fade-in)
 
5469                                 // to trigger a pruning.
 
5470                                 setTimeout(L.bind(this._pruneTiles, this), 250);
 
5475         _getTilePos: function (coords) {
 
5476                 return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
 
5479         _wrapCoords: function (coords) {
 
5480                 var newCoords = new L.Point(
 
5481                         this._wrapX ? L.Util.wrapNum(coords.x, this._wrapX) : coords.x,
 
5482                         this._wrapY ? L.Util.wrapNum(coords.y, this._wrapY) : coords.y);
 
5483                 newCoords.z = coords.z;
 
5487         _pxBoundsToTileRange: function (bounds) {
 
5488                 var tileSize = this.getTileSize();
 
5489                 return new L.Bounds(
 
5490                         bounds.min.unscaleBy(tileSize).floor(),
 
5491                         bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
 
5494         _noTilesToLoad: function () {
 
5495                 for (var key in this._tiles) {
 
5496                         if (!this._tiles[key].loaded) { return false; }
 
5502 // @factory L.gridLayer(options?: GridLayer options)
 
5503 // Creates a new instance of GridLayer with the supplied options.
 
5504 L.gridLayer = function (options) {
 
5505         return new L.GridLayer(options);
 
5512  * @inherits GridLayer
 
5514  * Used to load and display tile layers on the map. Extends `GridLayer`.
 
5519  * L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar'}).addTo(map);
 
5522  * @section URL template
 
5525  * A string of the following form:
 
5528  * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
 
5531  * `{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.
 
5533  * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
 
5536  * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
 
5541 L.TileLayer = L.GridLayer.extend({
 
5544         // @aka TileLayer options
 
5546                 // @option minZoom: Number = 0
 
5547                 // Minimum zoom number.
 
5550                 // @option maxZoom: Number = 18
 
5551                 // Maximum zoom number.
 
5554                 // @option maxNativeZoom: Number = null
 
5555                 // Maximum zoom number the tile source has available. If it is specified,
 
5556                 // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
 
5557                 // from `maxNativeZoom` level and auto-scaled.
 
5558                 maxNativeZoom: null,
 
5560                 // @option minNativeZoom: Number = null
 
5561                 // Minimum zoom number the tile source has available. If it is specified,
 
5562                 // the tiles on all zoom levels lower than `minNativeZoom` will be loaded
 
5563                 // from `minNativeZoom` level and auto-scaled.
 
5564                 minNativeZoom: null,
 
5566                 // @option subdomains: String|String[] = 'abc'
 
5567                 // 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.
 
5570                 // @option errorTileUrl: String = ''
 
5571                 // URL to the tile image to show in place of the tile that failed to load.
 
5574                 // @option zoomOffset: Number = 0
 
5575                 // The zoom number used in tile URLs will be offset with this value.
 
5578                 // @option tms: Boolean = false
 
5579                 // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
 
5582                 // @option zoomReverse: Boolean = false
 
5583                 // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
 
5586                 // @option detectRetina: Boolean = false
 
5587                 // 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.
 
5588                 detectRetina: false,
 
5590                 // @option crossOrigin: Boolean = false
 
5591                 // If true, all tiles will have their crossOrigin attribute set to ''. This is needed if you want to access tile pixel data.
 
5595         initialize: function (url, options) {
 
5599                 options = L.setOptions(this, options);
 
5601                 // detecting retina displays, adjusting tileSize and zoom levels
 
5602                 if (options.detectRetina && L.Browser.retina && options.maxZoom > 0) {
 
5604                         options.tileSize = Math.floor(options.tileSize / 2);
 
5606                         if (!options.zoomReverse) {
 
5607                                 options.zoomOffset++;
 
5610                                 options.zoomOffset--;
 
5614                         options.minZoom = Math.max(0, options.minZoom);
 
5617                 if (typeof options.subdomains === 'string') {
 
5618                         options.subdomains = options.subdomains.split('');
 
5621                 // for https://github.com/Leaflet/Leaflet/issues/137
 
5622                 if (!L.Browser.android) {
 
5623                         this.on('tileunload', this._onTileRemove);
 
5627         // @method setUrl(url: String, noRedraw?: Boolean): this
 
5628         // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
 
5629         setUrl: function (url, noRedraw) {
 
5638         // @method createTile(coords: Object, done?: Function): HTMLElement
 
5639         // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
 
5640         // to return an `<img>` HTML element with the appropiate image URL given `coords`. The `done`
 
5641         // callback is called when the tile has been loaded.
 
5642         createTile: function (coords, done) {
 
5643                 var tile = document.createElement('img');
 
5645                 L.DomEvent.on(tile, 'load', L.bind(this._tileOnLoad, this, done, tile));
 
5646                 L.DomEvent.on(tile, 'error', L.bind(this._tileOnError, this, done, tile));
 
5648                 if (this.options.crossOrigin) {
 
5649                         tile.crossOrigin = '';
 
5653                  Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
 
5654                  http://www.w3.org/TR/WCAG20-TECHS/H67
 
5659                  Set role="presentation" to force screen readers to ignore this
 
5660                  https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
 
5662                 tile.setAttribute('role', 'presentation');
 
5664                 tile.src = this.getTileUrl(coords);
 
5669         // @section Extension methods
 
5671         // Layers extending `TileLayer` might reimplement the following method.
 
5672         // @method getTileUrl(coords: Object): String
 
5673         // Called only internally, returns the URL for a tile given its coordinates.
 
5674         // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
 
5675         getTileUrl: function (coords) {
 
5677                         r: L.Browser.retina ? '@2x' : '',
 
5678                         s: this._getSubdomain(coords),
 
5681                         z: this._getZoomForUrl()
 
5683                 if (this._map && !this._map.options.crs.infinite) {
 
5684                         var invertedY = this._globalTileRange.max.y - coords.y;
 
5685                         if (this.options.tms) {
 
5686                                 data['y'] = invertedY;
 
5688                         data['-y'] = invertedY;
 
5691                 return L.Util.template(this._url, L.extend(data, this.options));
 
5694         _tileOnLoad: function (done, tile) {
 
5695                 // For https://github.com/Leaflet/Leaflet/issues/3332
 
5696                 if (L.Browser.ielt9) {
 
5697                         setTimeout(L.bind(done, this, null, tile), 0);
 
5703         _tileOnError: function (done, tile, e) {
 
5704                 var errorUrl = this.options.errorTileUrl;
 
5705                 if (errorUrl && tile.src !== errorUrl) {
 
5706                         tile.src = errorUrl;
 
5711         getTileSize: function () {
 
5712                 var map = this._map,
 
5713                 tileSize = L.GridLayer.prototype.getTileSize.call(this),
 
5714                 zoom = this._tileZoom + this.options.zoomOffset,
 
5715                 minNativeZoom = this.options.minNativeZoom,
 
5716                 maxNativeZoom = this.options.maxNativeZoom;
 
5718                 // decrease tile size when scaling below minNativeZoom
 
5719                 if (minNativeZoom !== null && zoom < minNativeZoom) {
 
5720                         return tileSize.divideBy(map.getZoomScale(minNativeZoom, zoom)).round();
 
5723                 // increase tile size when scaling above maxNativeZoom
 
5724                 if (maxNativeZoom !== null && zoom > maxNativeZoom) {
 
5725                         return tileSize.divideBy(map.getZoomScale(maxNativeZoom, zoom)).round();
 
5731         _onTileRemove: function (e) {
 
5732                 e.tile.onload = null;
 
5735         _getZoomForUrl: function () {
 
5736                 var zoom = this._tileZoom,
 
5737                 maxZoom = this.options.maxZoom,
 
5738                 zoomReverse = this.options.zoomReverse,
 
5739                 zoomOffset = this.options.zoomOffset,
 
5740                 minNativeZoom = this.options.minNativeZoom,
 
5741                 maxNativeZoom = this.options.maxNativeZoom;
 
5744                         zoom = maxZoom - zoom;
 
5749                 if (minNativeZoom !== null && zoom < minNativeZoom) {
 
5750                         return minNativeZoom;
 
5753                 if (maxNativeZoom !== null && zoom > maxNativeZoom) {
 
5754                         return maxNativeZoom;
 
5760         _getSubdomain: function (tilePoint) {
 
5761                 var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
 
5762                 return this.options.subdomains[index];
 
5765         // stops loading all tiles in the background layer
 
5766         _abortLoading: function () {
 
5768                 for (i in this._tiles) {
 
5769                         if (this._tiles[i].coords.z !== this._tileZoom) {
 
5770                                 tile = this._tiles[i].el;
 
5772                                 tile.onload = L.Util.falseFn;
 
5773                                 tile.onerror = L.Util.falseFn;
 
5775                                 if (!tile.complete) {
 
5776                                         tile.src = L.Util.emptyImageUrl;
 
5777                                         L.DomUtil.remove(tile);
 
5785 // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
 
5786 // Instantiates a tile layer object given a `URL template` and optionally an options object.
 
5788 L.tileLayer = function (url, options) {
 
5789         return new L.TileLayer(url, options);
 
5795  * @class TileLayer.WMS
 
5796  * @inherits TileLayer
 
5797  * @aka L.TileLayer.WMS
 
5798  * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
 
5803  * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
 
5804  *      layers: 'nexrad-n0r-900913',
 
5805  *      format: 'image/png',
 
5806  *      transparent: true,
 
5807  *      attribution: "Weather data © 2012 IEM Nexrad"
 
5812 L.TileLayer.WMS = L.TileLayer.extend({
 
5815         // @aka TileLayer.WMS options
 
5816         // If any custom options not documented here are used, they will be sent to the
 
5817         // WMS server as extra parameters in each request URL. This can be useful for
 
5818         // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
 
5823                 // @option layers: String = ''
 
5824                 // **(required)** Comma-separated list of WMS layers to show.
 
5827                 // @option styles: String = ''
 
5828                 // Comma-separated list of WMS styles.
 
5831                 // @option format: String = 'image/jpeg'
 
5832                 // WMS image format (use `'image/png'` for layers with transparency).
 
5833                 format: 'image/jpeg',
 
5835                 // @option transparent: Boolean = false
 
5836                 // If `true`, the WMS service will return images with transparency.
 
5839                 // @option version: String = '1.1.1'
 
5840                 // Version of the WMS service to use
 
5845                 // @option crs: CRS = null
 
5846                 // Coordinate Reference System to use for the WMS requests, defaults to
 
5847                 // map CRS. Don't change this if you're not sure what it means.
 
5850                 // @option uppercase: Boolean = false
 
5851                 // If `true`, WMS request parameter keys will be uppercase.
 
5855         initialize: function (url, options) {
 
5859                 var wmsParams = L.extend({}, this.defaultWmsParams);
 
5861                 // all keys that are not TileLayer options go to WMS params
 
5862                 for (var i in options) {
 
5863                         if (!(i in this.options)) {
 
5864                                 wmsParams[i] = options[i];
 
5868                 options = L.setOptions(this, options);
 
5870                 wmsParams.width = wmsParams.height = options.tileSize * (options.detectRetina && L.Browser.retina ? 2 : 1);
 
5872                 this.wmsParams = wmsParams;
 
5875         onAdd: function (map) {
 
5877                 this._crs = this.options.crs || map.options.crs;
 
5878                 this._wmsVersion = parseFloat(this.wmsParams.version);
 
5880                 var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
 
5881                 this.wmsParams[projectionKey] = this._crs.code;
 
5883                 L.TileLayer.prototype.onAdd.call(this, map);
 
5886         getTileUrl: function (coords) {
 
5888                 var tileBounds = this._tileCoordsToBounds(coords),
 
5889                     nw = this._crs.project(tileBounds.getNorthWest()),
 
5890                     se = this._crs.project(tileBounds.getSouthEast()),
 
5892                     bbox = (this._wmsVersion >= 1.3 && this._crs === L.CRS.EPSG4326 ?
 
5893                             [se.y, nw.x, nw.y, se.x] :
 
5894                             [nw.x, se.y, se.x, nw.y]).join(','),
 
5896                     url = L.TileLayer.prototype.getTileUrl.call(this, coords);
 
5899                         L.Util.getParamString(this.wmsParams, url, this.options.uppercase) +
 
5900                         (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
 
5903         // @method setParams(params: Object, noRedraw?: Boolean): this
 
5904         // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
 
5905         setParams: function (params, noRedraw) {
 
5907                 L.extend(this.wmsParams, params);
 
5918 // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
 
5919 // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
 
5920 L.tileLayer.wms = function (url, options) {
 
5921         return new L.TileLayer.WMS(url, options);
 
5927  * @class ImageOverlay
 
5928  * @aka L.ImageOverlay
 
5929  * @inherits Interactive layer
 
5931  * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
 
5936  * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
 
5937  *      imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
 
5938  * L.imageOverlay(imageUrl, imageBounds).addTo(map);
 
5942 L.ImageOverlay = L.Layer.extend({
 
5945         // @aka ImageOverlay options
 
5947                 // @option opacity: Number = 1.0
 
5948                 // The opacity of the image overlay.
 
5951                 // @option alt: String = ''
 
5952                 // Text for the `alt` attribute of the image (useful for accessibility).
 
5955                 // @option interactive: Boolean = false
 
5956                 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
 
5959                 // @option crossOrigin: Boolean = false
 
5960                 // If true, the image will have its crossOrigin attribute set to ''. This is needed if you want to access image pixel data.
 
5964         initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
 
5966                 this._bounds = L.latLngBounds(bounds);
 
5968                 L.setOptions(this, options);
 
5971         onAdd: function () {
 
5975                         if (this.options.opacity < 1) {
 
5976                                 this._updateOpacity();
 
5980                 if (this.options.interactive) {
 
5981                         L.DomUtil.addClass(this._image, 'leaflet-interactive');
 
5982                         this.addInteractiveTarget(this._image);
 
5985                 this.getPane().appendChild(this._image);
 
5989         onRemove: function () {
 
5990                 L.DomUtil.remove(this._image);
 
5991                 if (this.options.interactive) {
 
5992                         this.removeInteractiveTarget(this._image);
 
5996         // @method setOpacity(opacity: Number): this
 
5997         // Sets the opacity of the overlay.
 
5998         setOpacity: function (opacity) {
 
5999                 this.options.opacity = opacity;
 
6002                         this._updateOpacity();
 
6007         setStyle: function (styleOpts) {
 
6008                 if (styleOpts.opacity) {
 
6009                         this.setOpacity(styleOpts.opacity);
 
6014         // @method bringToFront(): this
 
6015         // Brings the layer to the top of all overlays.
 
6016         bringToFront: function () {
 
6018                         L.DomUtil.toFront(this._image);
 
6023         // @method bringToBack(): this
 
6024         // Brings the layer to the bottom of all overlays.
 
6025         bringToBack: function () {
 
6027                         L.DomUtil.toBack(this._image);
 
6032         // @method setUrl(url: String): this
 
6033         // Changes the URL of the image.
 
6034         setUrl: function (url) {
 
6038                         this._image.src = url;
 
6043         // @method setBounds(bounds: LatLngBounds): this
 
6044         // Update the bounds that this ImageOverlay covers
 
6045         setBounds: function (bounds) {
 
6046                 this._bounds = bounds;
 
6054         getEvents: function () {
 
6057                         viewreset: this._reset
 
6060                 if (this._zoomAnimated) {
 
6061                         events.zoomanim = this._animateZoom;
 
6067         // @method getBounds(): LatLngBounds
 
6068         // Get the bounds that this ImageOverlay covers
 
6069         getBounds: function () {
 
6070                 return this._bounds;
 
6073         // @method getElement(): HTMLElement
 
6074         // Get the img element that represents the ImageOverlay on the map
 
6075         getElement: function () {
 
6079         _initImage: function () {
 
6080                 var img = this._image = L.DomUtil.create('img',
 
6081                                 'leaflet-image-layer ' + (this._zoomAnimated ? 'leaflet-zoom-animated' : ''));
 
6083                 img.onselectstart = L.Util.falseFn;
 
6084                 img.onmousemove = L.Util.falseFn;
 
6086                 img.onload = L.bind(this.fire, this, 'load');
 
6088                 if (this.options.crossOrigin) {
 
6089                         img.crossOrigin = '';
 
6092                 img.src = this._url;
 
6093                 img.alt = this.options.alt;
 
6096         _animateZoom: function (e) {
 
6097                 var scale = this._map.getZoomScale(e.zoom),
 
6098                     offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
 
6100                 L.DomUtil.setTransform(this._image, offset, scale);
 
6103         _reset: function () {
 
6104                 var image = this._image,
 
6105                     bounds = new L.Bounds(
 
6106                         this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
 
6107                         this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
 
6108                     size = bounds.getSize();
 
6110                 L.DomUtil.setPosition(image, bounds.min);
 
6112                 image.style.width  = size.x + 'px';
 
6113                 image.style.height = size.y + 'px';
 
6116         _updateOpacity: function () {
 
6117                 L.DomUtil.setOpacity(this._image, this.options.opacity);
 
6121 // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
 
6122 // Instantiates an image overlay object given the URL of the image and the
 
6123 // geographical bounds it is tied to.
 
6124 L.imageOverlay = function (url, bounds, options) {
 
6125         return new L.ImageOverlay(url, bounds, options);
 
6135  * Represents an icon to provide when creating a marker.
 
6140  * var myIcon = L.icon({
 
6141  *     iconUrl: 'my-icon.png',
 
6142  *     iconRetinaUrl: 'my-icon@2x.png',
 
6143  *     iconSize: [38, 95],
 
6144  *     iconAnchor: [22, 94],
 
6145  *     popupAnchor: [-3, -76],
 
6146  *     shadowUrl: 'my-icon-shadow.png',
 
6147  *     shadowRetinaUrl: 'my-icon-shadow@2x.png',
 
6148  *     shadowSize: [68, 95],
 
6149  *     shadowAnchor: [22, 94]
 
6152  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
 
6155  * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
 
6159 L.Icon = L.Class.extend({
 
6164          * @option iconUrl: String = null
 
6165          * **(required)** The URL to the icon image (absolute or relative to your script path).
 
6167          * @option iconRetinaUrl: String = null
 
6168          * The URL to a retina sized version of the icon image (absolute or relative to your
 
6169          * script path). Used for Retina screen devices.
 
6171          * @option iconSize: Point = null
 
6172          * Size of the icon image in pixels.
 
6174          * @option iconAnchor: Point = null
 
6175          * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
 
6176          * will be aligned so that this point is at the marker's geographical location. Centered
 
6177          * by default if size is specified, also can be set in CSS with negative margins.
 
6179          * @option popupAnchor: Point = null
 
6180          * The coordinates of the point from which popups will "open", relative to the icon anchor.
 
6182          * @option shadowUrl: String = null
 
6183          * The URL to the icon shadow image. If not specified, no shadow image will be created.
 
6185          * @option shadowRetinaUrl: String = null
 
6187          * @option shadowSize: Point = null
 
6188          * Size of the shadow image in pixels.
 
6190          * @option shadowAnchor: Point = null
 
6191          * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
 
6192          * as iconAnchor if not specified).
 
6194          * @option className: String = ''
 
6195          * A custom class name to assign to both icon and shadow images. Empty by default.
 
6198         initialize: function (options) {
 
6199                 L.setOptions(this, options);
 
6202         // @method createIcon(oldIcon?: HTMLElement): HTMLElement
 
6203         // Called internally when the icon has to be shown, returns a `<img>` HTML element
 
6204         // styled according to the options.
 
6205         createIcon: function (oldIcon) {
 
6206                 return this._createIcon('icon', oldIcon);
 
6209         // @method createShadow(oldIcon?: HTMLElement): HTMLElement
 
6210         // As `createIcon`, but for the shadow beneath it.
 
6211         createShadow: function (oldIcon) {
 
6212                 return this._createIcon('shadow', oldIcon);
 
6215         _createIcon: function (name, oldIcon) {
 
6216                 var src = this._getIconUrl(name);
 
6219                         if (name === 'icon') {
 
6220                                 throw new Error('iconUrl not set in Icon options (see the docs).');
 
6225                 var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
 
6226                 this._setIconStyles(img, name);
 
6231         _setIconStyles: function (img, name) {
 
6232                 var options = this.options;
 
6233                 var sizeOption = options[name + 'Size'];
 
6235                 if (typeof sizeOption === 'number') {
 
6236                         sizeOption = [sizeOption, sizeOption];
 
6239                 var size = L.point(sizeOption),
 
6240                     anchor = L.point(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
 
6241                             size && size.divideBy(2, true));
 
6243                 img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
 
6246                         img.style.marginLeft = (-anchor.x) + 'px';
 
6247                         img.style.marginTop  = (-anchor.y) + 'px';
 
6251                         img.style.width  = size.x + 'px';
 
6252                         img.style.height = size.y + 'px';
 
6256         _createImg: function (src, el) {
 
6257                 el = el || document.createElement('img');
 
6262         _getIconUrl: function (name) {
 
6263                 return L.Browser.retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
 
6268 // @factory L.icon(options: Icon options)
 
6269 // Creates an icon instance with the given options.
 
6270 L.icon = function (options) {
 
6271         return new L.Icon(options);
 
6277  * @miniclass Icon.Default (Icon)
 
6278  * @aka L.Icon.Default
 
6281  * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
 
6282  * no icon is specified. Points to the blue marker image distributed with Leaflet
 
6285  * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
 
6286  * (which is a set of `Icon options`).
 
6288  * If you want to _completely_ replace the default icon, override the
 
6289  * `L.Marker.prototype.options.icon` with your own icon instead.
 
6292 L.Icon.Default = L.Icon.extend({
 
6295                 iconUrl:       'marker-icon.png',
 
6296                 iconRetinaUrl: 'marker-icon-2x.png',
 
6297                 shadowUrl:     'marker-shadow.png',
 
6299                 iconAnchor:  [12, 41],
 
6300                 popupAnchor: [1, -34],
 
6301                 tooltipAnchor: [16, -28],
 
6302                 shadowSize:  [41, 41]
 
6305         _getIconUrl: function (name) {
 
6306                 if (!L.Icon.Default.imagePath) {        // Deprecated, backwards-compatibility only
 
6307                         L.Icon.Default.imagePath = this._detectIconPath();
 
6310                 // @option imagePath: String
 
6311                 // `L.Icon.Default` will try to auto-detect the absolute location of the
 
6312                 // blue icon images. If you are placing these images in a non-standard
 
6313                 // way, set this option to point to the right absolute path.
 
6314                 return (this.options.imagePath || L.Icon.Default.imagePath) + L.Icon.prototype._getIconUrl.call(this, name);
 
6317         _detectIconPath: function () {
 
6318                 var el = L.DomUtil.create('div',  'leaflet-default-icon-path', document.body);
 
6319                 var path = L.DomUtil.getStyle(el, 'background-image') ||
 
6320                            L.DomUtil.getStyle(el, 'backgroundImage');   // IE8
 
6322                 document.body.removeChild(el);
 
6324                 return path.indexOf('url') === 0 ?
 
6325                         path.replace(/^url\([\"\']?/, '').replace(/marker-icon\.png[\"\']?\)$/, '') : '';
 
6333  * @inherits Interactive layer
 
6335  * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
 
6340  * L.marker([50.5, 30.5]).addTo(map);
 
6344 L.Marker = L.Layer.extend({
 
6347         // @aka Marker options
 
6349                 // @option icon: Icon = *
 
6350                 // Icon class to use for rendering the marker. See [Icon documentation](#L.Icon) for details on how to customize the marker icon. If not specified, a new `L.Icon.Default` is used.
 
6351                 icon: new L.Icon.Default(),
 
6353                 // Option inherited from "Interactive layer" abstract class
 
6356                 // @option draggable: Boolean = false
 
6357                 // Whether the marker is draggable with mouse/touch or not.
 
6360                 // @option keyboard: Boolean = true
 
6361                 // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
 
6364                 // @option title: String = ''
 
6365                 // Text for the browser tooltip that appear on marker hover (no tooltip by default).
 
6368                 // @option alt: String = ''
 
6369                 // Text for the `alt` attribute of the icon image (useful for accessibility).
 
6372                 // @option zIndexOffset: Number = 0
 
6373                 // 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).
 
6376                 // @option opacity: Number = 1.0
 
6377                 // The opacity of the marker.
 
6380                 // @option riseOnHover: Boolean = false
 
6381                 // If `true`, the marker will get on top of others when you hover the mouse over it.
 
6384                 // @option riseOffset: Number = 250
 
6385                 // The z-index offset used for the `riseOnHover` feature.
 
6388                 // @option pane: String = 'markerPane'
 
6389                 // `Map pane` where the markers icon will be added.
 
6392                 // FIXME: shadowPane is no longer a valid option
 
6393                 nonBubblingEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu']
 
6398          * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
 
6401         initialize: function (latlng, options) {
 
6402                 L.setOptions(this, options);
 
6403                 this._latlng = L.latLng(latlng);
 
6406         onAdd: function (map) {
 
6407                 this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
 
6409                 if (this._zoomAnimated) {
 
6410                         map.on('zoomanim', this._animateZoom, this);
 
6417         onRemove: function (map) {
 
6418                 if (this.dragging && this.dragging.enabled()) {
 
6419                         this.options.draggable = true;
 
6420                         this.dragging.removeHooks();
 
6423                 if (this._zoomAnimated) {
 
6424                         map.off('zoomanim', this._animateZoom, this);
 
6428                 this._removeShadow();
 
6431         getEvents: function () {
 
6434                         viewreset: this.update
 
6438         // @method getLatLng: LatLng
 
6439         // Returns the current geographical position of the marker.
 
6440         getLatLng: function () {
 
6441                 return this._latlng;
 
6444         // @method setLatLng(latlng: LatLng): this
 
6445         // Changes the marker position to the given point.
 
6446         setLatLng: function (latlng) {
 
6447                 var oldLatLng = this._latlng;
 
6448                 this._latlng = L.latLng(latlng);
 
6451                 // @event move: Event
 
6452                 // 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`.
 
6453                 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
 
6456         // @method setZIndexOffset(offset: Number): this
 
6457         // Changes the [zIndex offset](#marker-zindexoffset) of the marker.
 
6458         setZIndexOffset: function (offset) {
 
6459                 this.options.zIndexOffset = offset;
 
6460                 return this.update();
 
6463         // @method setIcon(icon: Icon): this
 
6464         // Changes the marker icon.
 
6465         setIcon: function (icon) {
 
6467                 this.options.icon = icon;
 
6475                         this.bindPopup(this._popup, this._popup.options);
 
6481         getElement: function () {
 
6485         update: function () {
 
6488                         var pos = this._map.latLngToLayerPoint(this._latlng).round();
 
6495         _initIcon: function () {
 
6496                 var options = this.options,
 
6497                     classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
 
6499                 var icon = options.icon.createIcon(this._icon),
 
6502                 // if we're not reusing the icon, remove the old one and init new one
 
6503                 if (icon !== this._icon) {
 
6509                         if (options.title) {
 
6510                                 icon.title = options.title;
 
6513                                 icon.alt = options.alt;
 
6517                 L.DomUtil.addClass(icon, classToAdd);
 
6519                 if (options.keyboard) {
 
6520                         icon.tabIndex = '0';
 
6525                 if (options.riseOnHover) {
 
6527                                 mouseover: this._bringToFront,
 
6528                                 mouseout: this._resetZIndex
 
6532                 var newShadow = options.icon.createShadow(this._shadow),
 
6535                 if (newShadow !== this._shadow) {
 
6536                         this._removeShadow();
 
6541                         L.DomUtil.addClass(newShadow, classToAdd);
 
6544                 this._shadow = newShadow;
 
6547                 if (options.opacity < 1) {
 
6548                         this._updateOpacity();
 
6553                         this.getPane().appendChild(this._icon);
 
6555                 this._initInteraction();
 
6556                 if (newShadow && addShadow) {
 
6557                         this.getPane('shadowPane').appendChild(this._shadow);
 
6561         _removeIcon: function () {
 
6562                 if (this.options.riseOnHover) {
 
6564                                 mouseover: this._bringToFront,
 
6565                                 mouseout: this._resetZIndex
 
6569                 L.DomUtil.remove(this._icon);
 
6570                 this.removeInteractiveTarget(this._icon);
 
6575         _removeShadow: function () {
 
6577                         L.DomUtil.remove(this._shadow);
 
6579                 this._shadow = null;
 
6582         _setPos: function (pos) {
 
6583                 L.DomUtil.setPosition(this._icon, pos);
 
6586                         L.DomUtil.setPosition(this._shadow, pos);
 
6589                 this._zIndex = pos.y + this.options.zIndexOffset;
 
6591                 this._resetZIndex();
 
6594         _updateZIndex: function (offset) {
 
6595                 this._icon.style.zIndex = this._zIndex + offset;
 
6598         _animateZoom: function (opt) {
 
6599                 var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
 
6604         _initInteraction: function () {
 
6606                 if (!this.options.interactive) { return; }
 
6608                 L.DomUtil.addClass(this._icon, 'leaflet-interactive');
 
6610                 this.addInteractiveTarget(this._icon);
 
6612                 if (L.Handler.MarkerDrag) {
 
6613                         var draggable = this.options.draggable;
 
6614                         if (this.dragging) {
 
6615                                 draggable = this.dragging.enabled();
 
6616                                 this.dragging.disable();
 
6619                         this.dragging = new L.Handler.MarkerDrag(this);
 
6622                                 this.dragging.enable();
 
6627         // @method setOpacity(opacity: Number): this
 
6628         // Changes the opacity of the marker.
 
6629         setOpacity: function (opacity) {
 
6630                 this.options.opacity = opacity;
 
6632                         this._updateOpacity();
 
6638         _updateOpacity: function () {
 
6639                 var opacity = this.options.opacity;
 
6641                 L.DomUtil.setOpacity(this._icon, opacity);
 
6644                         L.DomUtil.setOpacity(this._shadow, opacity);
 
6648         _bringToFront: function () {
 
6649                 this._updateZIndex(this.options.riseOffset);
 
6652         _resetZIndex: function () {
 
6653                 this._updateZIndex(0);
 
6656         _getPopupAnchor: function () {
 
6657                 return this.options.icon.options.popupAnchor || [0, 0];
 
6660         _getTooltipAnchor: function () {
 
6661                 return this.options.icon.options.tooltipAnchor || [0, 0];
 
6666 // factory L.marker(latlng: LatLng, options? : Marker options)
 
6668 // @factory L.marker(latlng: LatLng, options? : Marker options)
 
6669 // Instantiates a Marker object given a geographical point and optionally an options object.
 
6670 L.marker = function (latlng, options) {
 
6671         return new L.Marker(latlng, options);
 
6681  * Represents a lightweight icon for markers that uses a simple `<div>`
 
6682  * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
 
6686  * var myIcon = L.divIcon({className: 'my-div-icon'});
 
6687  * // you can set .my-div-icon styles in CSS
 
6689  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
 
6692  * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
 
6695 L.DivIcon = L.Icon.extend({
 
6698                 // @aka DivIcon options
 
6699                 iconSize: [12, 12], // also can be set through CSS
 
6701                 // iconAnchor: (Point),
 
6702                 // popupAnchor: (Point),
 
6704                 // @option html: String = ''
 
6705                 // Custom HTML code to put inside the div element, empty by default.
 
6708                 // @option bgPos: Point = [0, 0]
 
6709                 // Optional relative position of the background, in pixels
 
6712                 className: 'leaflet-div-icon'
 
6715         createIcon: function (oldIcon) {
 
6716                 var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
 
6717                     options = this.options;
 
6719                 div.innerHTML = options.html !== false ? options.html : '';
 
6721                 if (options.bgPos) {
 
6722                         var bgPos = L.point(options.bgPos);
 
6723                         div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
 
6725                 this._setIconStyles(div, 'icon');
 
6730         createShadow: function () {
 
6735 // @factory L.divIcon(options: DivIcon options)
 
6736 // Creates a `DivIcon` instance with the given options.
 
6737 L.divIcon = function (options) {
 
6738         return new L.DivIcon(options);
 
6747  * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
 
6750 // @namespace DivOverlay
 
6751 L.DivOverlay = L.Layer.extend({
 
6754         // @aka DivOverlay options
 
6756                 // @option offset: Point = Point(0, 7)
 
6757                 // The offset of the popup position. Useful to control the anchor
 
6758                 // of the popup when opening it on some overlays.
 
6761                 // @option className: String = ''
 
6762                 // A custom CSS class name to assign to the popup.
 
6765                 // @option pane: String = 'popupPane'
 
6766                 // `Map pane` where the popup will be added.
 
6770         initialize: function (options, source) {
 
6771                 L.setOptions(this, options);
 
6773                 this._source = source;
 
6776         onAdd: function (map) {
 
6777                 this._zoomAnimated = map._zoomAnimated;
 
6779                 if (!this._container) {
 
6783                 if (map._fadeAnimated) {
 
6784                         L.DomUtil.setOpacity(this._container, 0);
 
6787                 clearTimeout(this._removeTimeout);
 
6788                 this.getPane().appendChild(this._container);
 
6791                 if (map._fadeAnimated) {
 
6792                         L.DomUtil.setOpacity(this._container, 1);
 
6795                 this.bringToFront();
 
6798         onRemove: function (map) {
 
6799                 if (map._fadeAnimated) {
 
6800                         L.DomUtil.setOpacity(this._container, 0);
 
6801                         this._removeTimeout = setTimeout(L.bind(L.DomUtil.remove, L.DomUtil, this._container), 200);
 
6803                         L.DomUtil.remove(this._container);
 
6808         // @method getLatLng: LatLng
 
6809         // Returns the geographical point of popup.
 
6810         getLatLng: function () {
 
6811                 return this._latlng;
 
6814         // @method setLatLng(latlng: LatLng): this
 
6815         // Sets the geographical point where the popup will open.
 
6816         setLatLng: function (latlng) {
 
6817                 this._latlng = L.latLng(latlng);
 
6819                         this._updatePosition();
 
6825         // @method getContent: String|HTMLElement
 
6826         // Returns the content of the popup.
 
6827         getContent: function () {
 
6828                 return this._content;
 
6831         // @method setContent(htmlContent: String|HTMLElement|Function): this
 
6832         // 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.
 
6833         setContent: function (content) {
 
6834                 this._content = content;
 
6839         // @method getElement: String|HTMLElement
 
6840         // Alias for [getContent()](#popup-getcontent)
 
6841         getElement: function () {
 
6842                 return this._container;
 
6845         // @method update: null
 
6846         // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
 
6847         update: function () {
 
6848                 if (!this._map) { return; }
 
6850                 this._container.style.visibility = 'hidden';
 
6852                 this._updateContent();
 
6853                 this._updateLayout();
 
6854                 this._updatePosition();
 
6856                 this._container.style.visibility = '';
 
6861         getEvents: function () {
 
6863                         zoom: this._updatePosition,
 
6864                         viewreset: this._updatePosition
 
6867                 if (this._zoomAnimated) {
 
6868                         events.zoomanim = this._animateZoom;
 
6873         // @method isOpen: Boolean
 
6874         // Returns `true` when the popup is visible on the map.
 
6875         isOpen: function () {
 
6876                 return !!this._map && this._map.hasLayer(this);
 
6879         // @method bringToFront: this
 
6880         // Brings this popup in front of other popups (in the same map pane).
 
6881         bringToFront: function () {
 
6883                         L.DomUtil.toFront(this._container);
 
6888         // @method bringToBack: this
 
6889         // Brings this popup to the back of other popups (in the same map pane).
 
6890         bringToBack: function () {
 
6892                         L.DomUtil.toBack(this._container);
 
6897         _updateContent: function () {
 
6898                 if (!this._content) { return; }
 
6900                 var node = this._contentNode;
 
6901                 var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
 
6903                 if (typeof content === 'string') {
 
6904                         node.innerHTML = content;
 
6906                         while (node.hasChildNodes()) {
 
6907                                 node.removeChild(node.firstChild);
 
6909                         node.appendChild(content);
 
6911                 this.fire('contentupdate');
 
6914         _updatePosition: function () {
 
6915                 if (!this._map) { return; }
 
6917                 var pos = this._map.latLngToLayerPoint(this._latlng),
 
6918                     offset = L.point(this.options.offset),
 
6919                     anchor = this._getAnchor();
 
6921                 if (this._zoomAnimated) {
 
6922                         L.DomUtil.setPosition(this._container, pos.add(anchor));
 
6924                         offset = offset.add(pos).add(anchor);
 
6927                 var bottom = this._containerBottom = -offset.y,
 
6928                     left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
 
6930                 // bottom position the popup in case the height of the popup changes (images loading etc)
 
6931                 this._container.style.bottom = bottom + 'px';
 
6932                 this._container.style.left = left + 'px';
 
6935         _getAnchor: function () {
 
6945  * @inherits DivOverlay
 
6947  * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
 
6948  * open popups while making sure that only one popup is open at one time
 
6949  * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
 
6953  * If you want to just bind a popup to marker click and then open it, it's really easy:
 
6956  * marker.bindPopup(popupContent).openPopup();
 
6958  * Path overlays like polylines also have a `bindPopup` method.
 
6959  * Here's a more complicated way to open a popup on a map:
 
6962  * var popup = L.popup()
 
6963  *      .setLatLng(latlng)
 
6964  *      .setContent('<p>Hello world!<br />This is a nice popup.</p>')
 
6971 L.Popup = L.DivOverlay.extend({
 
6974         // @aka Popup options
 
6976                 // @option maxWidth: Number = 300
 
6977                 // Max width of the popup, in pixels.
 
6980                 // @option minWidth: Number = 50
 
6981                 // Min width of the popup, in pixels.
 
6984                 // @option maxHeight: Number = null
 
6985                 // If set, creates a scrollable container of the given height
 
6986                 // inside a popup if its content exceeds it.
 
6989                 // @option autoPan: Boolean = true
 
6990                 // Set it to `false` if you don't want the map to do panning animation
 
6991                 // to fit the opened popup.
 
6994                 // @option autoPanPaddingTopLeft: Point = null
 
6995                 // The margin between the popup and the top left corner of the map
 
6996                 // view after autopanning was performed.
 
6997                 autoPanPaddingTopLeft: null,
 
6999                 // @option autoPanPaddingBottomRight: Point = null
 
7000                 // The margin between the popup and the bottom right corner of the map
 
7001                 // view after autopanning was performed.
 
7002                 autoPanPaddingBottomRight: null,
 
7004                 // @option autoPanPadding: Point = Point(5, 5)
 
7005                 // Equivalent of setting both top left and bottom right autopan padding to the same value.
 
7006                 autoPanPadding: [5, 5],
 
7008                 // @option keepInView: Boolean = false
 
7009                 // Set it to `true` if you want to prevent users from panning the popup
 
7010                 // off of the screen while it is open.
 
7013                 // @option closeButton: Boolean = true
 
7014                 // Controls the presence of a close button in the popup.
 
7017                 // @option autoClose: Boolean = true
 
7018                 // Set it to `false` if you want to override the default behavior of
 
7019                 // the popup closing when user clicks the map (set globally by
 
7020                 // the Map's [closePopupOnClick](#map-closepopuponclick) option).
 
7023                 // @option className: String = ''
 
7024                 // A custom CSS class name to assign to the popup.
 
7029         // @method openOn(map: Map): this
 
7030         // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
 
7031         openOn: function (map) {
 
7032                 map.openPopup(this);
 
7036         onAdd: function (map) {
 
7037                 L.DivOverlay.prototype.onAdd.call(this, map);
 
7040                 // @section Popup events
 
7041                 // @event popupopen: PopupEvent
 
7042                 // Fired when a popup is opened in the map
 
7043                 map.fire('popupopen', {popup: this});
 
7047                         // @section Popup events
 
7048                         // @event popupopen: PopupEvent
 
7049                         // Fired when a popup bound to this layer is opened
 
7050                         this._source.fire('popupopen', {popup: this}, true);
 
7051                         // For non-path layers, we toggle the popup when clicking
 
7052                         // again the layer, so prevent the map to reopen it.
 
7053                         if (!(this._source instanceof L.Path)) {
 
7054                                 this._source.on('preclick', L.DomEvent.stopPropagation);
 
7059         onRemove: function (map) {
 
7060                 L.DivOverlay.prototype.onRemove.call(this, map);
 
7063                 // @section Popup events
 
7064                 // @event popupclose: PopupEvent
 
7065                 // Fired when a popup in the map is closed
 
7066                 map.fire('popupclose', {popup: this});
 
7070                         // @section Popup events
 
7071                         // @event popupclose: PopupEvent
 
7072                         // Fired when a popup bound to this layer is closed
 
7073                         this._source.fire('popupclose', {popup: this}, true);
 
7074                         if (!(this._source instanceof L.Path)) {
 
7075                                 this._source.off('preclick', L.DomEvent.stopPropagation);
 
7080         getEvents: function () {
 
7081                 var events = L.DivOverlay.prototype.getEvents.call(this);
 
7083                 if ('closeOnClick' in this.options ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
 
7084                         events.preclick = this._close;
 
7087                 if (this.options.keepInView) {
 
7088                         events.moveend = this._adjustPan;
 
7094         _close: function () {
 
7096                         this._map.closePopup(this);
 
7100         _initLayout: function () {
 
7101                 var prefix = 'leaflet-popup',
 
7102                     container = this._container = L.DomUtil.create('div',
 
7103                         prefix + ' ' + (this.options.className || '') +
 
7104                         ' leaflet-zoom-animated');
 
7106                 if (this.options.closeButton) {
 
7107                         var closeButton = this._closeButton = L.DomUtil.create('a', prefix + '-close-button', container);
 
7108                         closeButton.href = '#close';
 
7109                         closeButton.innerHTML = '×';
 
7111                         L.DomEvent.on(closeButton, 'click', this._onCloseButtonClick, this);
 
7114                 var wrapper = this._wrapper = L.DomUtil.create('div', prefix + '-content-wrapper', container);
 
7115                 this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper);
 
7118                         .disableClickPropagation(wrapper)
 
7119                         .disableScrollPropagation(this._contentNode)
 
7120                         .on(wrapper, 'contextmenu', L.DomEvent.stopPropagation);
 
7122                 this._tipContainer = L.DomUtil.create('div', prefix + '-tip-container', container);
 
7123                 this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer);
 
7126         _updateLayout: function () {
 
7127                 var container = this._contentNode,
 
7128                     style = container.style;
 
7131                 style.whiteSpace = 'nowrap';
 
7133                 var width = container.offsetWidth;
 
7134                 width = Math.min(width, this.options.maxWidth);
 
7135                 width = Math.max(width, this.options.minWidth);
 
7137                 style.width = (width + 1) + 'px';
 
7138                 style.whiteSpace = '';
 
7142                 var height = container.offsetHeight,
 
7143                     maxHeight = this.options.maxHeight,
 
7144                     scrolledClass = 'leaflet-popup-scrolled';
 
7146                 if (maxHeight && height > maxHeight) {
 
7147                         style.height = maxHeight + 'px';
 
7148                         L.DomUtil.addClass(container, scrolledClass);
 
7150                         L.DomUtil.removeClass(container, scrolledClass);
 
7153                 this._containerWidth = this._container.offsetWidth;
 
7156         _animateZoom: function (e) {
 
7157                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
 
7158                     anchor = this._getAnchor();
 
7159                 L.DomUtil.setPosition(this._container, pos.add(anchor));
 
7162         _adjustPan: function () {
 
7163                 if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; }
 
7165                 var map = this._map,
 
7166                     marginBottom = parseInt(L.DomUtil.getStyle(this._container, 'marginBottom'), 10) || 0,
 
7167                     containerHeight = this._container.offsetHeight + marginBottom,
 
7168                     containerWidth = this._containerWidth,
 
7169                     layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom);
 
7171                 layerPos._add(L.DomUtil.getPosition(this._container));
 
7173                 var containerPos = map.layerPointToContainerPoint(layerPos),
 
7174                     padding = L.point(this.options.autoPanPadding),
 
7175                     paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding),
 
7176                     paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding),
 
7177                     size = map.getSize(),
 
7181                 if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
 
7182                         dx = containerPos.x + containerWidth - size.x + paddingBR.x;
 
7184                 if (containerPos.x - dx - paddingTL.x < 0) { // left
 
7185                         dx = containerPos.x - paddingTL.x;
 
7187                 if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
 
7188                         dy = containerPos.y + containerHeight - size.y + paddingBR.y;
 
7190                 if (containerPos.y - dy - paddingTL.y < 0) { // top
 
7191                         dy = containerPos.y - paddingTL.y;
 
7195                 // @section Popup events
 
7196                 // @event autopanstart: Event
 
7197                 // Fired when the map starts autopanning when opening a popup.
 
7200                             .fire('autopanstart')
 
7205         _onCloseButtonClick: function (e) {
 
7210         _getAnchor: function () {
 
7211                 // Where should we anchor the popup on the source layer?
 
7212                 return L.point(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
 
7218 // @factory L.popup(options?: Popup options, source?: Layer)
 
7219 // 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.
 
7220 L.popup = function (options, source) {
 
7221         return new L.Popup(options, source);
 
7226  * @section Interaction Options
 
7227  * @option closePopupOnClick: Boolean = true
 
7228  * Set it to `false` if you don't want popups to close when user clicks the map.
 
7230 L.Map.mergeOptions({
 
7231         closePopupOnClick: true
 
7236 // @section Methods for Layers and Controls
 
7238         // @method openPopup(popup: Popup): this
 
7239         // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
 
7241         // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
 
7242         // Creates a popup with the specified content and options and opens it in the given point on a map.
 
7243         openPopup: function (popup, latlng, options) {
 
7244                 if (!(popup instanceof L.Popup)) {
 
7245                         popup = new L.Popup(options).setContent(popup);
 
7249                         popup.setLatLng(latlng);
 
7252                 if (this.hasLayer(popup)) {
 
7256                 if (this._popup && this._popup.options.autoClose) {
 
7260                 this._popup = popup;
 
7261                 return this.addLayer(popup);
 
7264         // @method closePopup(popup?: Popup): this
 
7265         // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
 
7266         closePopup: function (popup) {
 
7267                 if (!popup || popup === this._popup) {
 
7268                         popup = this._popup;
 
7272                         this.removeLayer(popup);
 
7280  * @section Popup methods example
 
7282  * All layers share a set of methods convenient for binding popups to it.
 
7285  * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
 
7286  * layer.openPopup();
 
7287  * layer.closePopup();
 
7290  * 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.
 
7293 // @section Popup methods
 
7296         // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
 
7297         // Binds a popup to the layer with the passed `content` and sets up the
 
7298         // neccessary event listeners. If a `Function` is passed it will receive
 
7299         // the layer as the first argument and should return a `String` or `HTMLElement`.
 
7300         bindPopup: function (content, options) {
 
7302                 if (content instanceof L.Popup) {
 
7303                         L.setOptions(content, options);
 
7304                         this._popup = content;
 
7305                         content._source = this;
 
7307                         if (!this._popup || options) {
 
7308                                 this._popup = new L.Popup(options, this);
 
7310                         this._popup.setContent(content);
 
7313                 if (!this._popupHandlersAdded) {
 
7315                                 click: this._openPopup,
 
7316                                 remove: this.closePopup,
 
7317                                 move: this._movePopup
 
7319                         this._popupHandlersAdded = true;
 
7325         // @method unbindPopup(): this
 
7326         // Removes the popup previously bound with `bindPopup`.
 
7327         unbindPopup: function () {
 
7330                                 click: this._openPopup,
 
7331                                 remove: this.closePopup,
 
7332                                 move: this._movePopup
 
7334                         this._popupHandlersAdded = false;
 
7340         // @method openPopup(latlng?: LatLng): this
 
7341         // Opens the bound popup at the specificed `latlng` or at the default popup anchor if no `latlng` is passed.
 
7342         openPopup: function (layer, latlng) {
 
7343                 if (!(layer instanceof L.Layer)) {
 
7348                 if (layer instanceof L.FeatureGroup) {
 
7349                         for (var id in this._layers) {
 
7350                                 layer = this._layers[id];
 
7356                         latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
 
7359                 if (this._popup && this._map) {
 
7360                         // set popup source to this layer
 
7361                         this._popup._source = layer;
 
7363                         // update the popup (content, layout, ect...)
 
7364                         this._popup.update();
 
7366                         // open the popup on the map
 
7367                         this._map.openPopup(this._popup, latlng);
 
7373         // @method closePopup(): this
 
7374         // Closes the popup bound to this layer if it is open.
 
7375         closePopup: function () {
 
7377                         this._popup._close();
 
7382         // @method togglePopup(): this
 
7383         // Opens or closes the popup bound to this layer depending on its current state.
 
7384         togglePopup: function (target) {
 
7386                         if (this._popup._map) {
 
7389                                 this.openPopup(target);
 
7395         // @method isPopupOpen(): boolean
 
7396         // Returns `true` if the popup bound to this layer is currently open.
 
7397         isPopupOpen: function () {
 
7398                 return (this._popup ? this._popup.isOpen() : false);
 
7401         // @method setPopupContent(content: String|HTMLElement|Popup): this
 
7402         // Sets the content of the popup bound to this layer.
 
7403         setPopupContent: function (content) {
 
7405                         this._popup.setContent(content);
 
7410         // @method getPopup(): Popup
 
7411         // Returns the popup bound to this layer.
 
7412         getPopup: function () {
 
7416         _openPopup: function (e) {
 
7417                 var layer = e.layer || e.target;
 
7427                 // prevent map click
 
7430                 // if this inherits from Path its a vector and we can just
 
7431                 // open the popup at the new location
 
7432                 if (layer instanceof L.Path) {
 
7433                         this.openPopup(e.layer || e.target, e.latlng);
 
7437                 // otherwise treat it like a marker and figure out
 
7438                 // if we should toggle it open/closed
 
7439                 if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
 
7442                         this.openPopup(layer, e.latlng);
 
7446         _movePopup: function (e) {
 
7447                 this._popup.setLatLng(e.latlng);
 
7455  * @inherits DivOverlay
 
7457  * Used to display small texts on top of map layers.
 
7462  * marker.bindTooltip("my tooltip text").openTooltip();
 
7464  * Note about tooltip offset. Leaflet takes two options in consideration
 
7465  * for computing tooltip offseting:
 
7466  * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
 
7467  *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
 
7468  *   move it to the bottom. Negatives will move to the left and top.
 
7469  * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
 
7470  *   should adapt this value if you use a custom icon.
 
7474 // @namespace Tooltip
 
7475 L.Tooltip = L.DivOverlay.extend({
 
7478         // @aka Tooltip options
 
7480                 // @option pane: String = 'tooltipPane'
 
7481                 // `Map pane` where the tooltip will be added.
 
7482                 pane: 'tooltipPane',
 
7484                 // @option offset: Point = Point(0, 0)
 
7485                 // Optional offset of the tooltip position.
 
7488                 // @option direction: String = 'auto'
 
7489                 // Direction where to open the tooltip. Possible values are: `right`, `left`,
 
7490                 // `top`, `bottom`, `center`, `auto`.
 
7491                 // `auto` will dynamicaly switch between `right` and `left` according to the tooltip
 
7492                 // position on the map.
 
7495                 // @option permanent: Boolean = false
 
7496                 // Whether to open the tooltip permanently or only on mouseover.
 
7499                 // @option sticky: Boolean = false
 
7500                 // If true, the tooltip will follow the mouse instead of being fixed at the feature center.
 
7503                 // @option interactive: Boolean = false
 
7504                 // If true, the tooltip will listen to the feature events.
 
7507                 // @option opacity: Number = 0.9
 
7508                 // Tooltip container opacity.
 
7512         onAdd: function (map) {
 
7513                 L.DivOverlay.prototype.onAdd.call(this, map);
 
7514                 this.setOpacity(this.options.opacity);
 
7517                 // @section Tooltip events
 
7518                 // @event tooltipopen: TooltipEvent
 
7519                 // Fired when a tooltip is opened in the map.
 
7520                 map.fire('tooltipopen', {tooltip: this});
 
7524                         // @section Tooltip events
 
7525                         // @event tooltipopen: TooltipEvent
 
7526                         // Fired when a tooltip bound to this layer is opened.
 
7527                         this._source.fire('tooltipopen', {tooltip: this}, true);
 
7531         onRemove: function (map) {
 
7532                 L.DivOverlay.prototype.onRemove.call(this, map);
 
7535                 // @section Tooltip events
 
7536                 // @event tooltipclose: TooltipEvent
 
7537                 // Fired when a tooltip in the map is closed.
 
7538                 map.fire('tooltipclose', {tooltip: this});
 
7542                         // @section Tooltip events
 
7543                         // @event tooltipclose: TooltipEvent
 
7544                         // Fired when a tooltip bound to this layer is closed.
 
7545                         this._source.fire('tooltipclose', {tooltip: this}, true);
 
7549         getEvents: function () {
 
7550                 var events = L.DivOverlay.prototype.getEvents.call(this);
 
7552                 if (L.Browser.touch && !this.options.permanent) {
 
7553                         events.preclick = this._close;
 
7559         _close: function () {
 
7561                         this._map.closeTooltip(this);
 
7565         _initLayout: function () {
 
7566                 var prefix = 'leaflet-tooltip',
 
7567                     className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
 
7569                 this._contentNode = this._container = L.DomUtil.create('div', className);
 
7572         _updateLayout: function () {},
 
7574         _adjustPan: function () {},
 
7576         _setPosition: function (pos) {
 
7577                 var map = this._map,
 
7578                     container = this._container,
 
7579                     centerPoint = map.latLngToContainerPoint(map.getCenter()),
 
7580                     tooltipPoint = map.layerPointToContainerPoint(pos),
 
7581                     direction = this.options.direction,
 
7582                     tooltipWidth = container.offsetWidth,
 
7583                     tooltipHeight = container.offsetHeight,
 
7584                     offset = L.point(this.options.offset),
 
7585                     anchor = this._getAnchor();
 
7587                 if (direction === 'top') {
 
7588                         pos = pos.add(L.point(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true));
 
7589                 } else if (direction === 'bottom') {
 
7590                         pos = pos.subtract(L.point(tooltipWidth / 2 - offset.x, -offset.y, true));
 
7591                 } else if (direction === 'center') {
 
7592                         pos = pos.subtract(L.point(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true));
 
7593                 } else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
 
7594                         direction = 'right';
 
7595                         pos = pos.add(L.point(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true));
 
7598                         pos = pos.subtract(L.point(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true));
 
7601                 L.DomUtil.removeClass(container, 'leaflet-tooltip-right');
 
7602                 L.DomUtil.removeClass(container, 'leaflet-tooltip-left');
 
7603                 L.DomUtil.removeClass(container, 'leaflet-tooltip-top');
 
7604                 L.DomUtil.removeClass(container, 'leaflet-tooltip-bottom');
 
7605                 L.DomUtil.addClass(container, 'leaflet-tooltip-' + direction);
 
7606                 L.DomUtil.setPosition(container, pos);
 
7609         _updatePosition: function () {
 
7610                 var pos = this._map.latLngToLayerPoint(this._latlng);
 
7611                 this._setPosition(pos);
 
7614         setOpacity: function (opacity) {
 
7615                 this.options.opacity = opacity;
 
7617                 if (this._container) {
 
7618                         L.DomUtil.setOpacity(this._container, opacity);
 
7622         _animateZoom: function (e) {
 
7623                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
 
7624                 this._setPosition(pos);
 
7627         _getAnchor: function () {
 
7628                 // Where should we anchor the tooltip on the source layer?
 
7629                 return L.point(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
 
7634 // @namespace Tooltip
 
7635 // @factory L.tooltip(options?: Tooltip options, source?: Layer)
 
7636 // 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.
 
7637 L.tooltip = function (options, source) {
 
7638         return new L.Tooltip(options, source);
 
7642 // @section Methods for Layers and Controls
 
7645         // @method openTooltip(tooltip: Tooltip): this
 
7646         // Opens the specified tooltip.
 
7648         // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
 
7649         // Creates a tooltip with the specified content and options and open it.
 
7650         openTooltip: function (tooltip, latlng, options) {
 
7651                 if (!(tooltip instanceof L.Tooltip)) {
 
7652                         tooltip = new L.Tooltip(options).setContent(tooltip);
 
7656                         tooltip.setLatLng(latlng);
 
7659                 if (this.hasLayer(tooltip)) {
 
7663                 return this.addLayer(tooltip);
 
7666         // @method closeTooltip(tooltip?: Tooltip): this
 
7667         // Closes the tooltip given as parameter.
 
7668         closeTooltip: function (tooltip) {
 
7670                         this.removeLayer(tooltip);
 
7679  * @section Tooltip methods example
 
7681  * All layers share a set of methods convenient for binding tooltips to it.
 
7684  * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
 
7685  * layer.openTooltip();
 
7686  * layer.closeTooltip();
 
7690 // @section Tooltip methods
 
7693         // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
 
7694         // Binds a tooltip to the layer with the passed `content` and sets up the
 
7695         // neccessary event listeners. If a `Function` is passed it will receive
 
7696         // the layer as the first argument and should return a `String` or `HTMLElement`.
 
7697         bindTooltip: function (content, options) {
 
7699                 if (content instanceof L.Tooltip) {
 
7700                         L.setOptions(content, options);
 
7701                         this._tooltip = content;
 
7702                         content._source = this;
 
7704                         if (!this._tooltip || options) {
 
7705                                 this._tooltip = L.tooltip(options, this);
 
7707                         this._tooltip.setContent(content);
 
7711                 this._initTooltipInteractions();
 
7713                 if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
 
7720         // @method unbindTooltip(): this
 
7721         // Removes the tooltip previously bound with `bindTooltip`.
 
7722         unbindTooltip: function () {
 
7723                 if (this._tooltip) {
 
7724                         this._initTooltipInteractions(true);
 
7725                         this.closeTooltip();
 
7726                         this._tooltip = null;
 
7731         _initTooltipInteractions: function (remove) {
 
7732                 if (!remove && this._tooltipHandlersAdded) { return; }
 
7733                 var onOff = remove ? 'off' : 'on',
 
7735                         remove: this.closeTooltip,
 
7736                         move: this._moveTooltip
 
7738                 if (!this._tooltip.options.permanent) {
 
7739                         events.mouseover = this._openTooltip;
 
7740                         events.mouseout = this.closeTooltip;
 
7741                         if (this._tooltip.options.sticky) {
 
7742                                 events.mousemove = this._moveTooltip;
 
7744                         if (L.Browser.touch) {
 
7745                                 events.click = this._openTooltip;
 
7748                         events.add = this._openTooltip;
 
7750                 this[onOff](events);
 
7751                 this._tooltipHandlersAdded = !remove;
 
7754         // @method openTooltip(latlng?: LatLng): this
 
7755         // Opens the bound tooltip at the specificed `latlng` or at the default tooltip anchor if no `latlng` is passed.
 
7756         openTooltip: function (layer, latlng) {
 
7757                 if (!(layer instanceof L.Layer)) {
 
7762                 if (layer instanceof L.FeatureGroup) {
 
7763                         for (var id in this._layers) {
 
7764                                 layer = this._layers[id];
 
7770                         latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
 
7773                 if (this._tooltip && this._map) {
 
7775                         // set tooltip source to this layer
 
7776                         this._tooltip._source = layer;
 
7778                         // update the tooltip (content, layout, ect...)
 
7779                         this._tooltip.update();
 
7781                         // open the tooltip on the map
 
7782                         this._map.openTooltip(this._tooltip, latlng);
 
7784                         // Tooltip container may not be defined if not permanent and never
 
7786                         if (this._tooltip.options.interactive && this._tooltip._container) {
 
7787                                 L.DomUtil.addClass(this._tooltip._container, 'leaflet-clickable');
 
7788                                 this.addInteractiveTarget(this._tooltip._container);
 
7795         // @method closeTooltip(): this
 
7796         // Closes the tooltip bound to this layer if it is open.
 
7797         closeTooltip: function () {
 
7798                 if (this._tooltip) {
 
7799                         this._tooltip._close();
 
7800                         if (this._tooltip.options.interactive && this._tooltip._container) {
 
7801                                 L.DomUtil.removeClass(this._tooltip._container, 'leaflet-clickable');
 
7802                                 this.removeInteractiveTarget(this._tooltip._container);
 
7808         // @method toggleTooltip(): this
 
7809         // Opens or closes the tooltip bound to this layer depending on its current state.
 
7810         toggleTooltip: function (target) {
 
7811                 if (this._tooltip) {
 
7812                         if (this._tooltip._map) {
 
7813                                 this.closeTooltip();
 
7815                                 this.openTooltip(target);
 
7821         // @method isTooltipOpen(): boolean
 
7822         // Returns `true` if the tooltip bound to this layer is currently open.
 
7823         isTooltipOpen: function () {
 
7824                 return this._tooltip.isOpen();
 
7827         // @method setTooltipContent(content: String|HTMLElement|Tooltip): this
 
7828         // Sets the content of the tooltip bound to this layer.
 
7829         setTooltipContent: function (content) {
 
7830                 if (this._tooltip) {
 
7831                         this._tooltip.setContent(content);
 
7836         // @method getTooltip(): Tooltip
 
7837         // Returns the tooltip bound to this layer.
 
7838         getTooltip: function () {
 
7839                 return this._tooltip;
 
7842         _openTooltip: function (e) {
 
7843                 var layer = e.layer || e.target;
 
7845                 if (!this._tooltip || !this._map) {
 
7848                 this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
 
7851         _moveTooltip: function (e) {
 
7852                 var latlng = e.latlng, containerPoint, layerPoint;
 
7853                 if (this._tooltip.options.sticky && e.originalEvent) {
 
7854                         containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
 
7855                         layerPoint = this._map.containerPointToLayerPoint(containerPoint);
 
7856                         latlng = this._map.layerPointToLatLng(layerPoint);
 
7858                 this._tooltip.setLatLng(latlng);
 
7869  * Used to group several layers and handle them as one. If you add it to the map,
 
7870  * any layers added or removed from the group will be added/removed on the map as
 
7871  * well. Extends `Layer`.
 
7876  * L.layerGroup([marker1, marker2])
 
7877  *      .addLayer(polyline)
 
7882 L.LayerGroup = L.Layer.extend({
 
7884         initialize: function (layers) {
 
7890                         for (i = 0, len = layers.length; i < len; i++) {
 
7891                                 this.addLayer(layers[i]);
 
7896         // @method addLayer(layer: Layer): this
 
7897         // Adds the given layer to the group.
 
7898         addLayer: function (layer) {
 
7899                 var id = this.getLayerId(layer);
 
7901                 this._layers[id] = layer;
 
7904                         this._map.addLayer(layer);
 
7910         // @method removeLayer(layer: Layer): this
 
7911         // Removes the given layer from the group.
 
7913         // @method removeLayer(id: Number): this
 
7914         // Removes the layer with the given internal ID from the group.
 
7915         removeLayer: function (layer) {
 
7916                 var id = layer in this._layers ? layer : this.getLayerId(layer);
 
7918                 if (this._map && this._layers[id]) {
 
7919                         this._map.removeLayer(this._layers[id]);
 
7922                 delete this._layers[id];
 
7927         // @method hasLayer(layer: Layer): Boolean
 
7928         // Returns `true` if the given layer is currently added to the group.
 
7929         hasLayer: function (layer) {
 
7930                 return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
 
7933         // @method clearLayers(): this
 
7934         // Removes all the layers from the group.
 
7935         clearLayers: function () {
 
7936                 for (var i in this._layers) {
 
7937                         this.removeLayer(this._layers[i]);
 
7942         // @method invoke(methodName: String, …): this
 
7943         // Calls `methodName` on every layer contained in this group, passing any
 
7944         // additional parameters. Has no effect if the layers contained do not
 
7945         // implement `methodName`.
 
7946         invoke: function (methodName) {
 
7947                 var args = Array.prototype.slice.call(arguments, 1),
 
7950                 for (i in this._layers) {
 
7951                         layer = this._layers[i];
 
7953                         if (layer[methodName]) {
 
7954                                 layer[methodName].apply(layer, args);
 
7961         onAdd: function (map) {
 
7962                 for (var i in this._layers) {
 
7963                         map.addLayer(this._layers[i]);
 
7967         onRemove: function (map) {
 
7968                 for (var i in this._layers) {
 
7969                         map.removeLayer(this._layers[i]);
 
7973         // @method eachLayer(fn: Function, context?: Object): this
 
7974         // Iterates over the layers of the group, optionally specifying context of the iterator function.
 
7976         // group.eachLayer(function (layer) {
 
7977         //      layer.bindPopup('Hello');
 
7980         eachLayer: function (method, context) {
 
7981                 for (var i in this._layers) {
 
7982                         method.call(context, this._layers[i]);
 
7987         // @method getLayer(id: Number): Layer
 
7988         // Returns the layer with the given internal ID.
 
7989         getLayer: function (id) {
 
7990                 return this._layers[id];
 
7993         // @method getLayers(): Layer[]
 
7994         // Returns an array of all the layers added to the group.
 
7995         getLayers: function () {
 
7998                 for (var i in this._layers) {
 
7999                         layers.push(this._layers[i]);
 
8004         // @method setZIndex(zIndex: Number): this
 
8005         // Calls `setZIndex` on every layer contained in this group, passing the z-index.
 
8006         setZIndex: function (zIndex) {
 
8007                 return this.invoke('setZIndex', zIndex);
 
8010         // @method getLayerId(layer: Layer): Number
 
8011         // Returns the internal ID for a layer
 
8012         getLayerId: function (layer) {
 
8013                 return L.stamp(layer);
 
8018 // @factory L.layerGroup(layers: Layer[])
 
8019 // Create a layer group, optionally given an initial set of layers.
 
8020 L.layerGroup = function (layers) {
 
8021         return new L.LayerGroup(layers);
 
8027  * @class FeatureGroup
 
8028  * @aka L.FeatureGroup
 
8029  * @inherits LayerGroup
 
8031  * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
 
8032  *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
 
8033  *  * Events are propagated to the `FeatureGroup`, so if the group has an event
 
8034  * handler, it will handle events from any of the layers. This includes mouse events
 
8035  * and custom events.
 
8036  *  * Has `layeradd` and `layerremove` events
 
8041  * L.featureGroup([marker1, marker2, polyline])
 
8042  *      .bindPopup('Hello world!')
 
8043  *      .on('click', function() { alert('Clicked on a member of the group!'); })
 
8048 L.FeatureGroup = L.LayerGroup.extend({
 
8050         addLayer: function (layer) {
 
8051                 if (this.hasLayer(layer)) {
 
8055                 layer.addEventParent(this);
 
8057                 L.LayerGroup.prototype.addLayer.call(this, layer);
 
8059                 // @event layeradd: LayerEvent
 
8060                 // Fired when a layer is added to this `FeatureGroup`
 
8061                 return this.fire('layeradd', {layer: layer});
 
8064         removeLayer: function (layer) {
 
8065                 if (!this.hasLayer(layer)) {
 
8068                 if (layer in this._layers) {
 
8069                         layer = this._layers[layer];
 
8072                 layer.removeEventParent(this);
 
8074                 L.LayerGroup.prototype.removeLayer.call(this, layer);
 
8076                 // @event layerremove: LayerEvent
 
8077                 // Fired when a layer is removed from this `FeatureGroup`
 
8078                 return this.fire('layerremove', {layer: layer});
 
8081         // @method setStyle(style: Path options): this
 
8082         // Sets the given path options to each layer of the group that has a `setStyle` method.
 
8083         setStyle: function (style) {
 
8084                 return this.invoke('setStyle', style);
 
8087         // @method bringToFront(): this
 
8088         // Brings the layer group to the top of all other layers
 
8089         bringToFront: function () {
 
8090                 return this.invoke('bringToFront');
 
8093         // @method bringToBack(): this
 
8094         // Brings the layer group to the top of all other layers
 
8095         bringToBack: function () {
 
8096                 return this.invoke('bringToBack');
 
8099         // @method getBounds(): LatLngBounds
 
8100         // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
 
8101         getBounds: function () {
 
8102                 var bounds = new L.LatLngBounds();
 
8104                 for (var id in this._layers) {
 
8105                         var layer = this._layers[id];
 
8106                         bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
 
8112 // @factory L.featureGroup(layers: Layer[])
 
8113 // Create a feature group, optionally given an initial set of layers.
 
8114 L.featureGroup = function (layers) {
 
8115         return new L.FeatureGroup(layers);
 
8125  * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
 
8126  * DOM container of the renderer, its bounds, and its zoom animation.
 
8128  * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
 
8129  * itself can be added or removed to the map. All paths use a renderer, which can
 
8130  * be implicit (the map will decide the type of renderer and use it automatically)
 
8131  * or explicit (using the [`renderer`](#path-renderer) option of the path).
 
8133  * Do not use this class directly, use `SVG` and `Canvas` instead.
 
8135  * @event update: Event
 
8136  * Fired when the renderer updates its bounds, center and zoom, for example when
 
8140 L.Renderer = L.Layer.extend({
 
8143         // @aka Renderer options
 
8145                 // @option padding: Number = 0.1
 
8146                 // How much to extend the clip area around the map view (relative to its size)
 
8147                 // e.g. 0.1 would be 10% of map view in each direction
 
8151         initialize: function (options) {
 
8152                 L.setOptions(this, options);
 
8154                 this._layers = this._layers || {};
 
8157         onAdd: function () {
 
8158                 if (!this._container) {
 
8159                         this._initContainer(); // defined by renderer implementations
 
8161                         if (this._zoomAnimated) {
 
8162                                 L.DomUtil.addClass(this._container, 'leaflet-zoom-animated');
 
8166                 this.getPane().appendChild(this._container);
 
8168                 this.on('update', this._updatePaths, this);
 
8171         onRemove: function () {
 
8172                 L.DomUtil.remove(this._container);
 
8173                 this.off('update', this._updatePaths, this);
 
8176         getEvents: function () {
 
8178                         viewreset: this._reset,
 
8180                         moveend: this._update,
 
8181                         zoomend: this._onZoomEnd
 
8183                 if (this._zoomAnimated) {
 
8184                         events.zoomanim = this._onAnimZoom;
 
8189         _onAnimZoom: function (ev) {
 
8190                 this._updateTransform(ev.center, ev.zoom);
 
8193         _onZoom: function () {
 
8194                 this._updateTransform(this._map.getCenter(), this._map.getZoom());
 
8197         _updateTransform: function (center, zoom) {
 
8198                 var scale = this._map.getZoomScale(zoom, this._zoom),
 
8199                     position = L.DomUtil.getPosition(this._container),
 
8200                     viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
 
8201                     currentCenterPoint = this._map.project(this._center, zoom),
 
8202                     destCenterPoint = this._map.project(center, zoom),
 
8203                     centerOffset = destCenterPoint.subtract(currentCenterPoint),
 
8205                     topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
 
8207                 if (L.Browser.any3d) {
 
8208                         L.DomUtil.setTransform(this._container, topLeftOffset, scale);
 
8210                         L.DomUtil.setPosition(this._container, topLeftOffset);
 
8214         _reset: function () {
 
8216                 this._updateTransform(this._center, this._zoom);
 
8218                 for (var id in this._layers) {
 
8219                         this._layers[id]._reset();
 
8223         _onZoomEnd: function () {
 
8224                 for (var id in this._layers) {
 
8225                         this._layers[id]._project();
 
8229         _updatePaths: function () {
 
8230                 for (var id in this._layers) {
 
8231                         this._layers[id]._update();
 
8235         _update: function () {
 
8236                 // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
 
8237                 // Subclasses are responsible of firing the 'update' event.
 
8238                 var p = this.options.padding,
 
8239                     size = this._map.getSize(),
 
8240                     min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
 
8242                 this._bounds = new L.Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
 
8244                 this._center = this._map.getCenter();
 
8245                 this._zoom = this._map.getZoom();
 
8251         // @namespace Map; @method getRenderer(layer: Path): Renderer
 
8252         // Returns the instance of `Renderer` that should be used to render the given
 
8253         // `Path`. It will ensure that the `renderer` options of the map and paths
 
8254         // are respected, and that the renderers do exist on the map.
 
8255         getRenderer: function (layer) {
 
8256                 // @namespace Path; @option renderer: Renderer
 
8257                 // Use this specific instance of `Renderer` for this path. Takes
 
8258                 // precedence over the map's [default renderer](#map-renderer).
 
8259                 var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
 
8262                         // @namespace Map; @option preferCanvas: Boolean = false
 
8263                         // Whether `Path`s should be rendered on a `Canvas` renderer.
 
8264                         // By default, all `Path`s are rendered in a `SVG` renderer.
 
8265                         renderer = this._renderer = (this.options.preferCanvas && L.canvas()) || L.svg();
 
8268                 if (!this.hasLayer(renderer)) {
 
8269                         this.addLayer(renderer);
 
8274         _getPaneRenderer: function (name) {
 
8275                 if (name === 'overlayPane' || name === undefined) {
 
8279                 var renderer = this._paneRenderers[name];
 
8280                 if (renderer === undefined) {
 
8281                         renderer = (L.SVG && L.svg({pane: name})) || (L.Canvas && L.canvas({pane: name}));
 
8282                         this._paneRenderers[name] = renderer;
 
8293  * @inherits Interactive layer
 
8295  * An abstract class that contains options and constants shared between vector
 
8296  * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
 
8299 L.Path = L.Layer.extend({
 
8302         // @aka Path options
 
8304                 // @option stroke: Boolean = true
 
8305                 // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
 
8308                 // @option color: String = '#3388ff'
 
8312                 // @option weight: Number = 3
 
8313                 // Stroke width in pixels
 
8316                 // @option opacity: Number = 1.0
 
8320                 // @option lineCap: String= 'round'
 
8321                 // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
 
8324                 // @option lineJoin: String = 'round'
 
8325                 // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
 
8328                 // @option dashArray: String = null
 
8329                 // 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).
 
8332                 // @option dashOffset: String = null
 
8333                 // 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).
 
8336                 // @option fill: Boolean = depends
 
8337                 // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
 
8340                 // @option fillColor: String = *
 
8341                 // Fill color. Defaults to the value of the [`color`](#path-color) option
 
8344                 // @option fillOpacity: Number = 0.2
 
8348                 // @option fillRule: String = 'evenodd'
 
8349                 // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
 
8350                 fillRule: 'evenodd',
 
8354                 // Option inherited from "Interactive layer" abstract class
 
8358         beforeAdd: function (map) {
 
8359                 // Renderer is set here because we need to call renderer.getEvents
 
8360                 // before this.getEvents.
 
8361                 this._renderer = map.getRenderer(this);
 
8364         onAdd: function () {
 
8365                 this._renderer._initPath(this);
 
8367                 this._renderer._addPath(this);
 
8370         onRemove: function () {
 
8371                 this._renderer._removePath(this);
 
8374         // @method redraw(): this
 
8375         // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
 
8376         redraw: function () {
 
8378                         this._renderer._updatePath(this);
 
8383         // @method setStyle(style: Path options): this
 
8384         // Changes the appearance of a Path based on the options in the `Path options` object.
 
8385         setStyle: function (style) {
 
8386                 L.setOptions(this, style);
 
8387                 if (this._renderer) {
 
8388                         this._renderer._updateStyle(this);
 
8393         // @method bringToFront(): this
 
8394         // Brings the layer to the top of all path layers.
 
8395         bringToFront: function () {
 
8396                 if (this._renderer) {
 
8397                         this._renderer._bringToFront(this);
 
8402         // @method bringToBack(): this
 
8403         // Brings the layer to the bottom of all path layers.
 
8404         bringToBack: function () {
 
8405                 if (this._renderer) {
 
8406                         this._renderer._bringToBack(this);
 
8411         getElement: function () {
 
8415         _reset: function () {
 
8416                 // defined in children classes
 
8421         _clickTolerance: function () {
 
8422                 // used when doing hit detection for Canvas layers
 
8423                 return (this.options.stroke ? this.options.weight / 2 : 0) + (L.Browser.touch ? 10 : 0);
 
8430  * @namespace LineUtil
 
8432  * Various utility functions for polyine points processing, used by Leaflet internally to make polylines lightning-fast.
 
8437         // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
 
8438         // Improves rendering performance dramatically by lessening the number of points to draw.
 
8440         // @function simplify(points: Point[], tolerance: Number): Point[]
 
8441         // Dramatically reduces the number of points in a polyline while retaining
 
8442         // its shape and returns a new array of simplified points, using the
 
8443         // [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
 
8444         // Used for a huge performance boost when processing/displaying Leaflet polylines for
 
8445         // each zoom level and also reducing visual noise. tolerance affects the amount of
 
8446         // simplification (lesser value means higher quality but slower and with more points).
 
8447         // Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
 
8448         simplify: function (points, tolerance) {
 
8449                 if (!tolerance || !points.length) {
 
8450                         return points.slice();
 
8453                 var sqTolerance = tolerance * tolerance;
 
8455                 // stage 1: vertex reduction
 
8456                 points = this._reducePoints(points, sqTolerance);
 
8458                 // stage 2: Douglas-Peucker simplification
 
8459                 points = this._simplifyDP(points, sqTolerance);
 
8464         // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
 
8465         // Returns the distance between point `p` and segment `p1` to `p2`.
 
8466         pointToSegmentDistance:  function (p, p1, p2) {
 
8467                 return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true));
 
8470         // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
 
8471         // Returns the closest point from a point `p` on a segment `p1` to `p2`.
 
8472         closestPointOnSegment: function (p, p1, p2) {
 
8473                 return this._sqClosestPointOnSegment(p, p1, p2);
 
8476         // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
 
8477         _simplifyDP: function (points, sqTolerance) {
 
8479                 var len = points.length,
 
8480                     ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
 
8481                     markers = new ArrayConstructor(len);
 
8483                 markers[0] = markers[len - 1] = 1;
 
8485                 this._simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
 
8490                 for (i = 0; i < len; i++) {
 
8492                                 newPoints.push(points[i]);
 
8499         _simplifyDPStep: function (points, markers, sqTolerance, first, last) {
 
8504                 for (i = first + 1; i <= last - 1; i++) {
 
8505                         sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true);
 
8507                         if (sqDist > maxSqDist) {
 
8513                 if (maxSqDist > sqTolerance) {
 
8516                         this._simplifyDPStep(points, markers, sqTolerance, first, index);
 
8517                         this._simplifyDPStep(points, markers, sqTolerance, index, last);
 
8521         // reduce points that are too close to each other to a single point
 
8522         _reducePoints: function (points, sqTolerance) {
 
8523                 var reducedPoints = [points[0]];
 
8525                 for (var i = 1, prev = 0, len = points.length; i < len; i++) {
 
8526                         if (this._sqDist(points[i], points[prev]) > sqTolerance) {
 
8527                                 reducedPoints.push(points[i]);
 
8531                 if (prev < len - 1) {
 
8532                         reducedPoints.push(points[len - 1]);
 
8534                 return reducedPoints;
 
8538         // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
 
8539         // Clips the segment a to b by rectangular bounds with the
 
8540         // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
 
8541         // (modifying the segment points directly!). Used by Leaflet to only show polyline
 
8542         // points that are on the screen or near, increasing performance.
 
8543         clipSegment: function (a, b, bounds, useLastCode, round) {
 
8544                 var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds),
 
8545                     codeB = this._getBitCode(b, bounds),
 
8547                     codeOut, p, newCode;
 
8549                 // save 2nd code to avoid calculating it on the next segment
 
8550                 this._lastCode = codeB;
 
8553                         // if a,b is inside the clip window (trivial accept)
 
8554                         if (!(codeA | codeB)) {
 
8558                         // if a,b is outside the clip window (trivial reject)
 
8559                         if (codeA & codeB) {
 
8564                         codeOut = codeA || codeB;
 
8565                         p = this._getEdgeIntersection(a, b, codeOut, bounds, round);
 
8566                         newCode = this._getBitCode(p, bounds);
 
8568                         if (codeOut === codeA) {
 
8578         _getEdgeIntersection: function (a, b, code, bounds, round) {
 
8585                 if (code & 8) { // top
 
8586                         x = a.x + dx * (max.y - a.y) / dy;
 
8589                 } else if (code & 4) { // bottom
 
8590                         x = a.x + dx * (min.y - a.y) / dy;
 
8593                 } else if (code & 2) { // right
 
8595                         y = a.y + dy * (max.x - a.x) / dx;
 
8597                 } else if (code & 1) { // left
 
8599                         y = a.y + dy * (min.x - a.x) / dx;
 
8602                 return new L.Point(x, y, round);
 
8605         _getBitCode: function (p, bounds) {
 
8608                 if (p.x < bounds.min.x) { // left
 
8610                 } else if (p.x > bounds.max.x) { // right
 
8614                 if (p.y < bounds.min.y) { // bottom
 
8616                 } else if (p.y > bounds.max.y) { // top
 
8623         // square distance (to avoid unnecessary Math.sqrt calls)
 
8624         _sqDist: function (p1, p2) {
 
8625                 var dx = p2.x - p1.x,
 
8627                 return dx * dx + dy * dy;
 
8630         // return closest point on segment or distance to that point
 
8631         _sqClosestPointOnSegment: function (p, p1, p2, sqDist) {
 
8636                     dot = dx * dx + dy * dy,
 
8640                         t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
 
8654                 return sqDist ? dx * dx + dy * dy : new L.Point(x, y);
 
8665  * A class for drawing polyline overlays on a map. Extends `Path`.
 
8670  * // create a red polyline from an array of LatLng points
 
8677  * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
 
8679  * // zoom the map to the polyline
 
8680  * map.fitBounds(polyline.getBounds());
 
8683  * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
 
8686  * // create a red polyline from an array of arrays of LatLng points
 
8688  *      [[45.51, -122.68],
 
8698 L.Polyline = L.Path.extend({
 
8701         // @aka Polyline options
 
8703                 // @option smoothFactor: Number = 1.0
 
8704                 // How much to simplify the polyline on each zoom level. More means
 
8705                 // better performance and smoother look, and less means more accurate representation.
 
8708                 // @option noClip: Boolean = false
 
8709                 // Disable polyline clipping.
 
8713         initialize: function (latlngs, options) {
 
8714                 L.setOptions(this, options);
 
8715                 this._setLatLngs(latlngs);
 
8718         // @method getLatLngs(): LatLng[]
 
8719         // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
 
8720         getLatLngs: function () {
 
8721                 return this._latlngs;
 
8724         // @method setLatLngs(latlngs: LatLng[]): this
 
8725         // Replaces all the points in the polyline with the given array of geographical points.
 
8726         setLatLngs: function (latlngs) {
 
8727                 this._setLatLngs(latlngs);
 
8728                 return this.redraw();
 
8731         // @method isEmpty(): Boolean
 
8732         // Returns `true` if the Polyline has no LatLngs.
 
8733         isEmpty: function () {
 
8734                 return !this._latlngs.length;
 
8737         closestLayerPoint: function (p) {
 
8738                 var minDistance = Infinity,
 
8740                     closest = L.LineUtil._sqClosestPointOnSegment,
 
8743                 for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
 
8744                         var points = this._parts[j];
 
8746                         for (var i = 1, len = points.length; i < len; i++) {
 
8750                                 var sqDist = closest(p, p1, p2, true);
 
8752                                 if (sqDist < minDistance) {
 
8753                                         minDistance = sqDist;
 
8754                                         minPoint = closest(p, p1, p2);
 
8759                         minPoint.distance = Math.sqrt(minDistance);
 
8764         // @method getCenter(): LatLng
 
8765         // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
 
8766         getCenter: function () {
 
8767                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
8769                         throw new Error('Must add layer to map before using getCenter()');
 
8772                 var i, halfDist, segDist, dist, p1, p2, ratio,
 
8773                     points = this._rings[0],
 
8774                     len = points.length;
 
8776                 if (!len) { return null; }
 
8778                 // polyline centroid algorithm; only uses the first ring if there are multiple
 
8780                 for (i = 0, halfDist = 0; i < len - 1; i++) {
 
8781                         halfDist += points[i].distanceTo(points[i + 1]) / 2;
 
8784                 // The line is so small in the current view that all points are on the same pixel.
 
8785                 if (halfDist === 0) {
 
8786                         return this._map.layerPointToLatLng(points[0]);
 
8789                 for (i = 0, dist = 0; i < len - 1; i++) {
 
8792                         segDist = p1.distanceTo(p2);
 
8795                         if (dist > halfDist) {
 
8796                                 ratio = (dist - halfDist) / segDist;
 
8797                                 return this._map.layerPointToLatLng([
 
8798                                         p2.x - ratio * (p2.x - p1.x),
 
8799                                         p2.y - ratio * (p2.y - p1.y)
 
8805         // @method getBounds(): LatLngBounds
 
8806         // Returns the `LatLngBounds` of the path.
 
8807         getBounds: function () {
 
8808                 return this._bounds;
 
8811         // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
 
8812         // Adds a given point to the polyline. By default, adds to the first ring of
 
8813         // the polyline in case of a multi-polyline, but can be overridden by passing
 
8814         // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
 
8815         addLatLng: function (latlng, latlngs) {
 
8816                 latlngs = latlngs || this._defaultShape();
 
8817                 latlng = L.latLng(latlng);
 
8818                 latlngs.push(latlng);
 
8819                 this._bounds.extend(latlng);
 
8820                 return this.redraw();
 
8823         _setLatLngs: function (latlngs) {
 
8824                 this._bounds = new L.LatLngBounds();
 
8825                 this._latlngs = this._convertLatLngs(latlngs);
 
8828         _defaultShape: function () {
 
8829                 return L.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0];
 
8832         // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
 
8833         _convertLatLngs: function (latlngs) {
 
8835                     flat = L.Polyline._flat(latlngs);
 
8837                 for (var i = 0, len = latlngs.length; i < len; i++) {
 
8839                                 result[i] = L.latLng(latlngs[i]);
 
8840                                 this._bounds.extend(result[i]);
 
8842                                 result[i] = this._convertLatLngs(latlngs[i]);
 
8849         _project: function () {
 
8850                 var pxBounds = new L.Bounds();
 
8852                 this._projectLatlngs(this._latlngs, this._rings, pxBounds);
 
8854                 var w = this._clickTolerance(),
 
8855                     p = new L.Point(w, w);
 
8857                 if (this._bounds.isValid() && pxBounds.isValid()) {
 
8858                         pxBounds.min._subtract(p);
 
8859                         pxBounds.max._add(p);
 
8860                         this._pxBounds = pxBounds;
 
8864         // recursively turns latlngs into a set of rings with projected coordinates
 
8865         _projectLatlngs: function (latlngs, result, projectedBounds) {
 
8866                 var flat = latlngs[0] instanceof L.LatLng,
 
8867                     len = latlngs.length,
 
8872                         for (i = 0; i < len; i++) {
 
8873                                 ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
 
8874                                 projectedBounds.extend(ring[i]);
 
8878                         for (i = 0; i < len; i++) {
 
8879                                 this._projectLatlngs(latlngs[i], result, projectedBounds);
 
8884         // clip polyline by renderer bounds so that we have less to render for performance
 
8885         _clipPoints: function () {
 
8886                 var bounds = this._renderer._bounds;
 
8889                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
8893                 if (this.options.noClip) {
 
8894                         this._parts = this._rings;
 
8898                 var parts = this._parts,
 
8899                     i, j, k, len, len2, segment, points;
 
8901                 for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
 
8902                         points = this._rings[i];
 
8904                         for (j = 0, len2 = points.length; j < len2 - 1; j++) {
 
8905                                 segment = L.LineUtil.clipSegment(points[j], points[j + 1], bounds, j, true);
 
8907                                 if (!segment) { continue; }
 
8909                                 parts[k] = parts[k] || [];
 
8910                                 parts[k].push(segment[0]);
 
8912                                 // if segment goes out of screen, or it's the last one, it's the end of the line part
 
8913                                 if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
 
8914                                         parts[k].push(segment[1]);
 
8921         // simplify each clipped part of the polyline for performance
 
8922         _simplifyPoints: function () {
 
8923                 var parts = this._parts,
 
8924                     tolerance = this.options.smoothFactor;
 
8926                 for (var i = 0, len = parts.length; i < len; i++) {
 
8927                         parts[i] = L.LineUtil.simplify(parts[i], tolerance);
 
8931         _update: function () {
 
8932                 if (!this._map) { return; }
 
8935                 this._simplifyPoints();
 
8939         _updatePath: function () {
 
8940                 this._renderer._updatePoly(this);
 
8944 // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
 
8945 // Instantiates a polyline object given an array of geographical points and
 
8946 // optionally an options object. You can create a `Polyline` object with
 
8947 // multiple separate lines (`MultiPolyline`) by passing an array of arrays
 
8948 // of geographic points.
 
8949 L.polyline = function (latlngs, options) {
 
8950         return new L.Polyline(latlngs, options);
 
8953 L.Polyline._flat = function (latlngs) {
 
8954         // true if it's a flat array of latlngs; false if nested
 
8955         return !L.Util.isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
 
8961  * @namespace PolyUtil
 
8962  * Various utility functions for polygon geometries.
 
8967 /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
 
8968  * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgeman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)).
 
8969  * Used by Leaflet to only show polygon points that are on the screen or near, increasing
 
8970  * performance. Note that polygon points needs different algorithm for clipping
 
8971  * than polyline, so there's a seperate method for it.
 
8973 L.PolyUtil.clipPolygon = function (points, bounds, round) {
 
8975             edges = [1, 4, 2, 8],
 
8981         for (i = 0, len = points.length; i < len; i++) {
 
8982                 points[i]._code = lu._getBitCode(points[i], bounds);
 
8985         // for each edge (left, bottom, right, top)
 
8986         for (k = 0; k < 4; k++) {
 
8990                 for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
 
8994                         // if a is inside the clip window
 
8995                         if (!(a._code & edge)) {
 
8996                                 // if b is outside the clip window (a->b goes out of screen)
 
8997                                 if (b._code & edge) {
 
8998                                         p = lu._getEdgeIntersection(b, a, edge, bounds, round);
 
8999                                         p._code = lu._getBitCode(p, bounds);
 
9000                                         clippedPoints.push(p);
 
9002                                 clippedPoints.push(a);
 
9004                         // else if b is inside the clip window (a->b enters the screen)
 
9005                         } else if (!(b._code & edge)) {
 
9006                                 p = lu._getEdgeIntersection(b, a, edge, bounds, round);
 
9007                                 p._code = lu._getBitCode(p, bounds);
 
9008                                 clippedPoints.push(p);
 
9011                 points = clippedPoints;
 
9022  * @inherits Polyline
 
9024  * A class for drawing polygon overlays on a map. Extends `Polyline`.
 
9026  * 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.
 
9032  * // create a red polygon from an array of LatLng points
 
9033  * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
 
9035  * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
 
9037  * // zoom the map to the polygon
 
9038  * map.fitBounds(polygon.getBounds());
 
9041  * 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:
 
9045  *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
 
9046  *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
 
9050  * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
 
9054  *   [ // first polygon
 
9055  *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
 
9056  *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
 
9058  *   [ // second polygon
 
9059  *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
 
9065 L.Polygon = L.Polyline.extend({
 
9071         isEmpty: function () {
 
9072                 return !this._latlngs.length || !this._latlngs[0].length;
 
9075         getCenter: function () {
 
9076                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
9078                         throw new Error('Must add layer to map before using getCenter()');
 
9081                 var i, j, p1, p2, f, area, x, y, center,
 
9082                     points = this._rings[0],
 
9083                     len = points.length;
 
9085                 if (!len) { return null; }
 
9087                 // polygon centroid algorithm; only uses the first ring if there are multiple
 
9091                 for (i = 0, j = len - 1; i < len; j = i++) {
 
9095                         f = p1.y * p2.x - p2.y * p1.x;
 
9096                         x += (p1.x + p2.x) * f;
 
9097                         y += (p1.y + p2.y) * f;
 
9102                         // Polygon is so small that all points are on same pixel.
 
9105                         center = [x / area, y / area];
 
9107                 return this._map.layerPointToLatLng(center);
 
9110         _convertLatLngs: function (latlngs) {
 
9111                 var result = L.Polyline.prototype._convertLatLngs.call(this, latlngs),
 
9112                     len = result.length;
 
9114                 // remove last point if it equals first one
 
9115                 if (len >= 2 && result[0] instanceof L.LatLng && result[0].equals(result[len - 1])) {
 
9121         _setLatLngs: function (latlngs) {
 
9122                 L.Polyline.prototype._setLatLngs.call(this, latlngs);
 
9123                 if (L.Polyline._flat(this._latlngs)) {
 
9124                         this._latlngs = [this._latlngs];
 
9128         _defaultShape: function () {
 
9129                 return L.Polyline._flat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
 
9132         _clipPoints: function () {
 
9133                 // polygons need a different clipping algorithm so we redefine that
 
9135                 var bounds = this._renderer._bounds,
 
9136                     w = this.options.weight,
 
9137                     p = new L.Point(w, w);
 
9139                 // increase clip padding by stroke width to avoid stroke on clip edges
 
9140                 bounds = new L.Bounds(bounds.min.subtract(p), bounds.max.add(p));
 
9143                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
9147                 if (this.options.noClip) {
 
9148                         this._parts = this._rings;
 
9152                 for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
 
9153                         clipped = L.PolyUtil.clipPolygon(this._rings[i], bounds, true);
 
9154                         if (clipped.length) {
 
9155                                 this._parts.push(clipped);
 
9160         _updatePath: function () {
 
9161                 this._renderer._updatePoly(this, true);
 
9166 // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
 
9167 L.polygon = function (latlngs, options) {
 
9168         return new L.Polygon(latlngs, options);
 
9174  * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
 
9182  * A class for drawing rectangle overlays on a map. Extends `Polygon`.
 
9187  * // define rectangle geographical bounds
 
9188  * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
 
9190  * // create an orange rectangle
 
9191  * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
 
9193  * // zoom the map to the rectangle bounds
 
9194  * map.fitBounds(bounds);
 
9200 L.Rectangle = L.Polygon.extend({
 
9201         initialize: function (latLngBounds, options) {
 
9202                 L.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
 
9205         // @method setBounds(latLngBounds: LatLngBounds): this
 
9206         // Redraws the rectangle with the passed bounds.
 
9207         setBounds: function (latLngBounds) {
 
9208                 return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
 
9211         _boundsToLatLngs: function (latLngBounds) {
 
9212                 latLngBounds = L.latLngBounds(latLngBounds);
 
9214                         latLngBounds.getSouthWest(),
 
9215                         latLngBounds.getNorthWest(),
 
9216                         latLngBounds.getNorthEast(),
 
9217                         latLngBounds.getSouthEast()
 
9223 // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
 
9224 L.rectangle = function (latLngBounds, options) {
 
9225         return new L.Rectangle(latLngBounds, options);
 
9231  * @class CircleMarker
 
9232  * @aka L.CircleMarker
 
9235  * A circle of a fixed size with radius specified in pixels. Extends `Path`.
 
9238 L.CircleMarker = L.Path.extend({
 
9241         // @aka CircleMarker options
 
9245                 // @option radius: Number = 10
 
9246                 // Radius of the circle marker, in pixels
 
9250         initialize: function (latlng, options) {
 
9251                 L.setOptions(this, options);
 
9252                 this._latlng = L.latLng(latlng);
 
9253                 this._radius = this.options.radius;
 
9256         // @method setLatLng(latLng: LatLng): this
 
9257         // Sets the position of a circle marker to a new location.
 
9258         setLatLng: function (latlng) {
 
9259                 this._latlng = L.latLng(latlng);
 
9261                 return this.fire('move', {latlng: this._latlng});
 
9264         // @method getLatLng(): LatLng
 
9265         // Returns the current geographical position of the circle marker
 
9266         getLatLng: function () {
 
9267                 return this._latlng;
 
9270         // @method setRadius(radius: Number): this
 
9271         // Sets the radius of a circle marker. Units are in pixels.
 
9272         setRadius: function (radius) {
 
9273                 this.options.radius = this._radius = radius;
 
9274                 return this.redraw();
 
9277         // @method getRadius(): Number
 
9278         // Returns the current radius of the circle
 
9279         getRadius: function () {
 
9280                 return this._radius;
 
9283         setStyle : function (options) {
 
9284                 var radius = options && options.radius || this._radius;
 
9285                 L.Path.prototype.setStyle.call(this, options);
 
9286                 this.setRadius(radius);
 
9290         _project: function () {
 
9291                 this._point = this._map.latLngToLayerPoint(this._latlng);
 
9292                 this._updateBounds();
 
9295         _updateBounds: function () {
 
9296                 var r = this._radius,
 
9297                     r2 = this._radiusY || r,
 
9298                     w = this._clickTolerance(),
 
9299                     p = [r + w, r2 + w];
 
9300                 this._pxBounds = new L.Bounds(this._point.subtract(p), this._point.add(p));
 
9303         _update: function () {
 
9309         _updatePath: function () {
 
9310                 this._renderer._updateCircle(this);
 
9313         _empty: function () {
 
9314                 return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
 
9319 // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
 
9320 // Instantiates a circle marker object given a geographical point, and an optional options object.
 
9321 L.circleMarker = function (latlng, options) {
 
9322         return new L.CircleMarker(latlng, options);
 
9330  * @inherits CircleMarker
 
9332  * A class for drawing circle overlays on a map. Extends `CircleMarker`.
 
9334  * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
 
9339  * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
 
9343 L.Circle = L.CircleMarker.extend({
 
9345         initialize: function (latlng, options, legacyOptions) {
 
9346                 if (typeof options === 'number') {
 
9347                         // Backwards compatibility with 0.7.x factory (latlng, radius, options?)
 
9348                         options = L.extend({}, legacyOptions, {radius: options});
 
9350                 L.setOptions(this, options);
 
9351                 this._latlng = L.latLng(latlng);
 
9353                 if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
 
9356                 // @aka Circle options
 
9357                 // @option radius: Number; Radius of the circle, in meters.
 
9358                 this._mRadius = this.options.radius;
 
9361         // @method setRadius(radius: Number): this
 
9362         // Sets the radius of a circle. Units are in meters.
 
9363         setRadius: function (radius) {
 
9364                 this._mRadius = radius;
 
9365                 return this.redraw();
 
9368         // @method getRadius(): Number
 
9369         // Returns the current radius of a circle. Units are in meters.
 
9370         getRadius: function () {
 
9371                 return this._mRadius;
 
9374         // @method getBounds(): LatLngBounds
 
9375         // Returns the `LatLngBounds` of the path.
 
9376         getBounds: function () {
 
9377                 var half = [this._radius, this._radiusY || this._radius];
 
9379                 return new L.LatLngBounds(
 
9380                         this._map.layerPointToLatLng(this._point.subtract(half)),
 
9381                         this._map.layerPointToLatLng(this._point.add(half)));
 
9384         setStyle: L.Path.prototype.setStyle,
 
9386         _project: function () {
 
9388                 var lng = this._latlng.lng,
 
9389                     lat = this._latlng.lat,
 
9391                     crs = map.options.crs;
 
9393                 if (crs.distance === L.CRS.Earth.distance) {
 
9394                         var d = Math.PI / 180,
 
9395                             latR = (this._mRadius / L.CRS.Earth.R) / d,
 
9396                             top = map.project([lat + latR, lng]),
 
9397                             bottom = map.project([lat - latR, lng]),
 
9398                             p = top.add(bottom).divideBy(2),
 
9399                             lat2 = map.unproject(p).lat,
 
9400                             lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
 
9401                                     (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
 
9403                         if (isNaN(lngR) || lngR === 0) {
 
9404                                 lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
 
9407                         this._point = p.subtract(map.getPixelOrigin());
 
9408                         this._radius = isNaN(lngR) ? 0 : Math.max(Math.round(p.x - map.project([lat2, lng - lngR]).x), 1);
 
9409                         this._radiusY = Math.max(Math.round(p.y - top.y), 1);
 
9412                         var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
 
9414                         this._point = map.latLngToLayerPoint(this._latlng);
 
9415                         this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
 
9418                 this._updateBounds();
 
9422 // @factory L.circle(latlng: LatLng, options?: Circle options)
 
9423 // Instantiates a circle object given a geographical point, and an options object
 
9424 // which contains the circle radius.
 
9426 // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
 
9427 // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
 
9428 // Do not use in new applications or plugins.
 
9429 L.circle = function (latlng, options, legacyOptions) {
 
9430         return new L.Circle(latlng, options, legacyOptions);
 
9437  * @inherits Renderer
 
9440  * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
 
9441  * Inherits `Renderer`.
 
9443  * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
 
9444  * available in all web browsers, notably Android 2.x and 3.x.
 
9446  * Although SVG is not available on IE7 and IE8, these browsers support
 
9447  * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
 
9448  * (a now deprecated technology), and the SVG renderer will fall back to VML in
 
9453  * Use SVG by default for all paths in the map:
 
9456  * var map = L.map('map', {
 
9461  * Use a SVG renderer with extra padding for specific vector geometries:
 
9464  * var map = L.map('map');
 
9465  * var myRenderer = L.svg({ padding: 0.5 });
 
9466  * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
9467  * var circle = L.circle( center, { renderer: myRenderer } );
 
9471 L.SVG = L.Renderer.extend({
 
9473         getEvents: function () {
 
9474                 var events = L.Renderer.prototype.getEvents.call(this);
 
9475                 events.zoomstart = this._onZoomStart;
 
9479         _initContainer: function () {
 
9480                 this._container = L.SVG.create('svg');
 
9482                 // makes it possible to click through svg root; we'll reset it back in individual paths
 
9483                 this._container.setAttribute('pointer-events', 'none');
 
9485                 this._rootGroup = L.SVG.create('g');
 
9486                 this._container.appendChild(this._rootGroup);
 
9489         _onZoomStart: function () {
 
9490                 // Drag-then-pinch interactions might mess up the center and zoom.
 
9491                 // In this case, the easiest way to prevent this is re-do the renderer
 
9492                 //   bounds and padding when the zooming starts.
 
9496         _update: function () {
 
9497                 if (this._map._animatingZoom && this._bounds) { return; }
 
9499                 L.Renderer.prototype._update.call(this);
 
9501                 var b = this._bounds,
 
9503                     container = this._container;
 
9505                 // set size of svg-container if changed
 
9506                 if (!this._svgSize || !this._svgSize.equals(size)) {
 
9507                         this._svgSize = size;
 
9508                         container.setAttribute('width', size.x);
 
9509                         container.setAttribute('height', size.y);
 
9512                 // movement: update container viewBox so that we don't have to change coordinates of individual layers
 
9513                 L.DomUtil.setPosition(container, b.min);
 
9514                 container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
 
9516                 this.fire('update');
 
9519         // methods below are called by vector layers implementations
 
9521         _initPath: function (layer) {
 
9522                 var path = layer._path = L.SVG.create('path');
 
9525                 // @option className: String = null
 
9526                 // Custom class name set on an element. Only for SVG renderer.
 
9527                 if (layer.options.className) {
 
9528                         L.DomUtil.addClass(path, layer.options.className);
 
9531                 if (layer.options.interactive) {
 
9532                         L.DomUtil.addClass(path, 'leaflet-interactive');
 
9535                 this._updateStyle(layer);
 
9536                 this._layers[L.stamp(layer)] = layer;
 
9539         _addPath: function (layer) {
 
9540                 this._rootGroup.appendChild(layer._path);
 
9541                 layer.addInteractiveTarget(layer._path);
 
9544         _removePath: function (layer) {
 
9545                 L.DomUtil.remove(layer._path);
 
9546                 layer.removeInteractiveTarget(layer._path);
 
9547                 delete this._layers[L.stamp(layer)];
 
9550         _updatePath: function (layer) {
 
9555         _updateStyle: function (layer) {
 
9556                 var path = layer._path,
 
9557                     options = layer.options;
 
9559                 if (!path) { return; }
 
9561                 if (options.stroke) {
 
9562                         path.setAttribute('stroke', options.color);
 
9563                         path.setAttribute('stroke-opacity', options.opacity);
 
9564                         path.setAttribute('stroke-width', options.weight);
 
9565                         path.setAttribute('stroke-linecap', options.lineCap);
 
9566                         path.setAttribute('stroke-linejoin', options.lineJoin);
 
9568                         if (options.dashArray) {
 
9569                                 path.setAttribute('stroke-dasharray', options.dashArray);
 
9571                                 path.removeAttribute('stroke-dasharray');
 
9574                         if (options.dashOffset) {
 
9575                                 path.setAttribute('stroke-dashoffset', options.dashOffset);
 
9577                                 path.removeAttribute('stroke-dashoffset');
 
9580                         path.setAttribute('stroke', 'none');
 
9584                         path.setAttribute('fill', options.fillColor || options.color);
 
9585                         path.setAttribute('fill-opacity', options.fillOpacity);
 
9586                         path.setAttribute('fill-rule', options.fillRule || 'evenodd');
 
9588                         path.setAttribute('fill', 'none');
 
9592         _updatePoly: function (layer, closed) {
 
9593                 this._setPath(layer, L.SVG.pointsToPath(layer._parts, closed));
 
9596         _updateCircle: function (layer) {
 
9597                 var p = layer._point,
 
9599                     r2 = layer._radiusY || r,
 
9600                     arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
 
9602                 // drawing a circle with two half-arcs
 
9603                 var d = layer._empty() ? 'M0 0' :
 
9604                                 'M' + (p.x - r) + ',' + p.y +
 
9605                                 arc + (r * 2) + ',0 ' +
 
9606                                 arc + (-r * 2) + ',0 ';
 
9608                 this._setPath(layer, d);
 
9611         _setPath: function (layer, path) {
 
9612                 layer._path.setAttribute('d', path);
 
9615         // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
 
9616         _bringToFront: function (layer) {
 
9617                 L.DomUtil.toFront(layer._path);
 
9620         _bringToBack: function (layer) {
 
9621                 L.DomUtil.toBack(layer._path);
 
9626 // @namespace SVG; @section
 
9627 // There are several static functions which can be called without instantiating L.SVG:
 
9629         // @function create(name: String): SVGElement
 
9630         // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
 
9631         // corresponding to the class name passed. For example, using 'line' will return
 
9632         // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
 
9633         create: function (name) {
 
9634                 return document.createElementNS('http://www.w3.org/2000/svg', name);
 
9637         // @function pointsToPath(rings: Point[], closed: Boolean): String
 
9638         // Generates a SVG path string for multiple rings, with each ring turning
 
9639         // into "M..L..L.." instructions
 
9640         pointsToPath: function (rings, closed) {
 
9642                     i, j, len, len2, points, p;
 
9644                 for (i = 0, len = rings.length; i < len; i++) {
 
9647                         for (j = 0, len2 = points.length; j < len2; j++) {
 
9649                                 str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
 
9652                         // closes the ring for polygons; "x" is VML syntax
 
9653                         str += closed ? (L.Browser.svg ? 'z' : 'x') : '';
 
9656                 // SVG complains about empty path strings
 
9657                 return str || 'M0 0';
 
9661 // @namespace Browser; @property svg: Boolean
 
9662 // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
 
9663 L.Browser.svg = !!(document.createElementNS && L.SVG.create('svg').createSVGRect);
 
9667 // @factory L.svg(options?: Renderer options)
 
9668 // Creates a SVG renderer with the given options.
 
9669 L.svg = function (options) {
 
9670         return L.Browser.svg || L.Browser.vml ? new L.SVG(options) : null;
 
9676  * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
 
9682  * 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.
 
9684  * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
 
9685  * with old versions of Internet Explorer.
 
9688 // @namespace Browser; @property vml: Boolean
 
9689 // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
 
9690 L.Browser.vml = !L.Browser.svg && (function () {
 
9692                 var div = document.createElement('div');
 
9693                 div.innerHTML = '<v:shape adj="1"/>';
 
9695                 var shape = div.firstChild;
 
9696                 shape.style.behavior = 'url(#default#VML)';
 
9698                 return shape && (typeof shape.adj === 'object');
 
9705 // redefine some SVG methods to handle VML syntax which is similar but with some differences
 
9706 L.SVG.include(!L.Browser.vml ? {} : {
 
9708         _initContainer: function () {
 
9709                 this._container = L.DomUtil.create('div', 'leaflet-vml-container');
 
9712         _update: function () {
 
9713                 if (this._map._animatingZoom) { return; }
 
9714                 L.Renderer.prototype._update.call(this);
 
9715                 this.fire('update');
 
9718         _initPath: function (layer) {
 
9719                 var container = layer._container = L.SVG.create('shape');
 
9721                 L.DomUtil.addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
 
9723                 container.coordsize = '1 1';
 
9725                 layer._path = L.SVG.create('path');
 
9726                 container.appendChild(layer._path);
 
9728                 this._updateStyle(layer);
 
9729                 this._layers[L.stamp(layer)] = layer;
 
9732         _addPath: function (layer) {
 
9733                 var container = layer._container;
 
9734                 this._container.appendChild(container);
 
9736                 if (layer.options.interactive) {
 
9737                         layer.addInteractiveTarget(container);
 
9741         _removePath: function (layer) {
 
9742                 var container = layer._container;
 
9743                 L.DomUtil.remove(container);
 
9744                 layer.removeInteractiveTarget(container);
 
9745                 delete this._layers[L.stamp(layer)];
 
9748         _updateStyle: function (layer) {
 
9749                 var stroke = layer._stroke,
 
9751                     options = layer.options,
 
9752                     container = layer._container;
 
9754                 container.stroked = !!options.stroke;
 
9755                 container.filled = !!options.fill;
 
9757                 if (options.stroke) {
 
9759                                 stroke = layer._stroke = L.SVG.create('stroke');
 
9761                         container.appendChild(stroke);
 
9762                         stroke.weight = options.weight + 'px';
 
9763                         stroke.color = options.color;
 
9764                         stroke.opacity = options.opacity;
 
9766                         if (options.dashArray) {
 
9767                                 stroke.dashStyle = L.Util.isArray(options.dashArray) ?
 
9768                                     options.dashArray.join(' ') :
 
9769                                     options.dashArray.replace(/( *, *)/g, ' ');
 
9771                                 stroke.dashStyle = '';
 
9773                         stroke.endcap = options.lineCap.replace('butt', 'flat');
 
9774                         stroke.joinstyle = options.lineJoin;
 
9776                 } else if (stroke) {
 
9777                         container.removeChild(stroke);
 
9778                         layer._stroke = null;
 
9783                                 fill = layer._fill = L.SVG.create('fill');
 
9785                         container.appendChild(fill);
 
9786                         fill.color = options.fillColor || options.color;
 
9787                         fill.opacity = options.fillOpacity;
 
9790                         container.removeChild(fill);
 
9795         _updateCircle: function (layer) {
 
9796                 var p = layer._point.round(),
 
9797                     r = Math.round(layer._radius),
 
9798                     r2 = Math.round(layer._radiusY || r);
 
9800                 this._setPath(layer, layer._empty() ? 'M0 0' :
 
9801                                 'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
 
9804         _setPath: function (layer, path) {
 
9805                 layer._path.v = path;
 
9808         _bringToFront: function (layer) {
 
9809                 L.DomUtil.toFront(layer._container);
 
9812         _bringToBack: function (layer) {
 
9813                 L.DomUtil.toBack(layer._container);
 
9817 if (L.Browser.vml) {
 
9818         L.SVG.create = (function () {
 
9820                         document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
 
9821                         return function (name) {
 
9822                                 return document.createElement('<lvml:' + name + ' class="lvml">');
 
9825                         return function (name) {
 
9826                                 return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
 
9836  * @inherits Renderer
 
9839  * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
 
9840  * Inherits `Renderer`.
 
9842  * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
 
9843  * available in all web browsers, notably IE8, and overlapping geometries might
 
9844  * not display properly in some edge cases.
 
9848  * Use Canvas by default for all paths in the map:
 
9851  * var map = L.map('map', {
 
9852  *      renderer: L.canvas()
 
9856  * Use a Canvas renderer with extra padding for specific vector geometries:
 
9859  * var map = L.map('map');
 
9860  * var myRenderer = L.canvas({ padding: 0.5 });
 
9861  * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
9862  * var circle = L.circle( center, { renderer: myRenderer } );
 
9866 L.Canvas = L.Renderer.extend({
 
9867         getEvents: function () {
 
9868                 var events = L.Renderer.prototype.getEvents.call(this);
 
9869                 events.viewprereset = this._onViewPreReset;
 
9873         _onViewPreReset: function () {
 
9874                 // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
 
9875                 this._postponeUpdatePaths = true;
 
9878         onAdd: function () {
 
9879                 L.Renderer.prototype.onAdd.call(this);
 
9881                 // Redraw vectors since canvas is cleared upon removal,
 
9882                 // in case of removing the renderer itself from the map.
 
9886         _initContainer: function () {
 
9887                 var container = this._container = document.createElement('canvas');
 
9890                         .on(container, 'mousemove', L.Util.throttle(this._onMouseMove, 32, this), this)
 
9891                         .on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this)
 
9892                         .on(container, 'mouseout', this._handleMouseOut, this);
 
9894                 this._ctx = container.getContext('2d');
 
9897         _updatePaths: function () {
 
9898                 if (this._postponeUpdatePaths) { return; }
 
9901                 this._redrawBounds = null;
 
9902                 for (var id in this._layers) {
 
9903                         layer = this._layers[id];
 
9909         _update: function () {
 
9910                 if (this._map._animatingZoom && this._bounds) { return; }
 
9912                 this._drawnLayers = {};
 
9914                 L.Renderer.prototype._update.call(this);
 
9916                 var b = this._bounds,
 
9917                     container = this._container,
 
9919                     m = L.Browser.retina ? 2 : 1;
 
9921                 L.DomUtil.setPosition(container, b.min);
 
9923                 // set canvas size (also clearing it); use double size on retina
 
9924                 container.width = m * size.x;
 
9925                 container.height = m * size.y;
 
9926                 container.style.width = size.x + 'px';
 
9927                 container.style.height = size.y + 'px';
 
9929                 if (L.Browser.retina) {
 
9930                         this._ctx.scale(2, 2);
 
9933                 // translate so we use the same path coordinates after canvas element moves
 
9934                 this._ctx.translate(-b.min.x, -b.min.y);
 
9936                 // Tell paths to redraw themselves
 
9937                 this.fire('update');
 
9940         _reset: function () {
 
9941                 L.Renderer.prototype._reset.call(this);
 
9943                 if (this._postponeUpdatePaths) {
 
9944                         this._postponeUpdatePaths = false;
 
9945                         this._updatePaths();
 
9949         _initPath: function (layer) {
 
9950                 this._updateDashArray(layer);
 
9951                 this._layers[L.stamp(layer)] = layer;
 
9953                 var order = layer._order = {
 
9955                         prev: this._drawLast,
 
9958                 if (this._drawLast) { this._drawLast.next = order; }
 
9959                 this._drawLast = order;
 
9960                 this._drawFirst = this._drawFirst || this._drawLast;
 
9963         _addPath: function (layer) {
 
9964                 this._requestRedraw(layer);
 
9967         _removePath: function (layer) {
 
9968                 var order = layer._order;
 
9969                 var next = order.next;
 
9970                 var prev = order.prev;
 
9975                         this._drawLast = prev;
 
9980                         this._drawFirst = next;
 
9983                 delete layer._order;
 
9985                 delete this._layers[L.stamp(layer)];
 
9987                 this._requestRedraw(layer);
 
9990         _updatePath: function (layer) {
 
9991                 // Redraw the union of the layer's old pixel
 
9992                 // bounds and the new pixel bounds.
 
9993                 this._extendRedrawBounds(layer);
 
9996                 // The redraw will extend the redraw bounds
 
9997                 // with the new pixel bounds.
 
9998                 this._requestRedraw(layer);
 
10001         _updateStyle: function (layer) {
 
10002                 this._updateDashArray(layer);
 
10003                 this._requestRedraw(layer);
 
10006         _updateDashArray: function (layer) {
 
10007                 if (layer.options.dashArray) {
 
10008                         var parts = layer.options.dashArray.split(','),
 
10011                         for (i = 0; i < parts.length; i++) {
 
10012                                 dashArray.push(Number(parts[i]));
 
10014                         layer.options._dashArray = dashArray;
 
10018         _requestRedraw: function (layer) {
 
10019                 if (!this._map) { return; }
 
10021                 this._extendRedrawBounds(layer);
 
10022                 this._redrawRequest = this._redrawRequest || L.Util.requestAnimFrame(this._redraw, this);
 
10025         _extendRedrawBounds: function (layer) {
 
10026                 var padding = (layer.options.weight || 0) + 1;
 
10027                 this._redrawBounds = this._redrawBounds || new L.Bounds();
 
10028                 this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
 
10029                 this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
 
10032         _redraw: function () {
 
10033                 this._redrawRequest = null;
 
10035                 if (this._redrawBounds) {
 
10036                         this._redrawBounds.min._floor();
 
10037                         this._redrawBounds.max._ceil();
 
10040                 this._clear(); // clear layers in redraw bounds
 
10041                 this._draw(); // draw layers
 
10043                 this._redrawBounds = null;
 
10046         _clear: function () {
 
10047                 var bounds = this._redrawBounds;
 
10049                         var size = bounds.getSize();
 
10050                         this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
 
10052                         this._ctx.clearRect(0, 0, this._container.width, this._container.height);
 
10056         _draw: function () {
 
10057                 var layer, bounds = this._redrawBounds;
 
10060                         var size = bounds.getSize();
 
10061                         this._ctx.beginPath();
 
10062                         this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
 
10066                 this._drawing = true;
 
10068                 for (var order = this._drawFirst; order; order = order.next) {
 
10069                         layer = order.layer;
 
10070                         if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
 
10071                                 layer._updatePath();
 
10075                 this._drawing = false;
 
10077                 this._ctx.restore();  // Restore state before clipping.
 
10080         _updatePoly: function (layer, closed) {
 
10081                 if (!this._drawing) { return; }
 
10084                     parts = layer._parts,
 
10085                     len = parts.length,
 
10088                 if (!len) { return; }
 
10090                 this._drawnLayers[layer._leaflet_id] = layer;
 
10094                 if (ctx.setLineDash) {
 
10095                         ctx.setLineDash(layer.options && layer.options._dashArray || []);
 
10098                 for (i = 0; i < len; i++) {
 
10099                         for (j = 0, len2 = parts[i].length; j < len2; j++) {
 
10101                                 ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
 
10108                 this._fillStroke(ctx, layer);
 
10110                 // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
 
10113         _updateCircle: function (layer) {
 
10115                 if (!this._drawing || layer._empty()) { return; }
 
10117                 var p = layer._point,
 
10120                     s = (layer._radiusY || r) / r;
 
10122                 this._drawnLayers[layer._leaflet_id] = layer;
 
10130                 ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
 
10136                 this._fillStroke(ctx, layer);
 
10139         _fillStroke: function (ctx, layer) {
 
10140                 var options = layer.options;
 
10142                 if (options.fill) {
 
10143                         ctx.globalAlpha = options.fillOpacity;
 
10144                         ctx.fillStyle = options.fillColor || options.color;
 
10145                         ctx.fill(options.fillRule || 'evenodd');
 
10148                 if (options.stroke && options.weight !== 0) {
 
10149                         ctx.globalAlpha = options.opacity;
 
10150                         ctx.lineWidth = options.weight;
 
10151                         ctx.strokeStyle = options.color;
 
10152                         ctx.lineCap = options.lineCap;
 
10153                         ctx.lineJoin = options.lineJoin;
 
10158         // Canvas obviously doesn't have mouse events for individual drawn objects,
 
10159         // so we emulate that by calculating what's under the mouse on mousemove/click manually
 
10161         _onClick: function (e) {
 
10162                 var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
 
10164                 for (var order = this._drawFirst; order; order = order.next) {
 
10165                         layer = order.layer;
 
10166                         if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
 
10167                                 clickedLayer = layer;
 
10170                 if (clickedLayer)  {
 
10171                         L.DomEvent._fakeStop(e);
 
10172                         this._fireEvent([clickedLayer], e);
 
10176         _onMouseMove: function (e) {
 
10177                 if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
 
10179                 var point = this._map.mouseEventToLayerPoint(e);
 
10180                 this._handleMouseHover(e, point);
 
10184         _handleMouseOut: function (e) {
 
10185                 var layer = this._hoveredLayer;
 
10187                         // if we're leaving the layer, fire mouseout
 
10188                         L.DomUtil.removeClass(this._container, 'leaflet-interactive');
 
10189                         this._fireEvent([layer], e, 'mouseout');
 
10190                         this._hoveredLayer = null;
 
10194         _handleMouseHover: function (e, point) {
 
10195                 var layer, candidateHoveredLayer;
 
10197                 for (var order = this._drawFirst; order; order = order.next) {
 
10198                         layer = order.layer;
 
10199                         if (layer.options.interactive && layer._containsPoint(point)) {
 
10200                                 candidateHoveredLayer = layer;
 
10204                 if (candidateHoveredLayer !== this._hoveredLayer) {
 
10205                         this._handleMouseOut(e);
 
10207                         if (candidateHoveredLayer) {
 
10208                                 L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor
 
10209                                 this._fireEvent([candidateHoveredLayer], e, 'mouseover');
 
10210                                 this._hoveredLayer = candidateHoveredLayer;
 
10214                 if (this._hoveredLayer) {
 
10215                         this._fireEvent([this._hoveredLayer], e);
 
10219         _fireEvent: function (layers, e, type) {
 
10220                 this._map._fireDOMEvent(e, type || e.type, layers);
 
10223         _bringToFront: function (layer) {
 
10224                 var order = layer._order;
 
10225                 var next = order.next;
 
10226                 var prev = order.prev;
 
10237                         // Update first entry unless this is the
 
10239                         this._drawFirst = next;
 
10242                 order.prev = this._drawLast;
 
10243                 this._drawLast.next = order;
 
10246                 this._drawLast = order;
 
10248                 this._requestRedraw(layer);
 
10251         _bringToBack: function (layer) {
 
10252                 var order = layer._order;
 
10253                 var next = order.next;
 
10254                 var prev = order.prev;
 
10265                         // Update last entry unless this is the
 
10267                         this._drawLast = prev;
 
10272                 order.next = this._drawFirst;
 
10273                 this._drawFirst.prev = order;
 
10274                 this._drawFirst = order;
 
10276                 this._requestRedraw(layer);
 
10280 // @namespace Browser; @property canvas: Boolean
 
10281 // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
 
10282 L.Browser.canvas = (function () {
 
10283         return !!document.createElement('canvas').getContext;
 
10286 // @namespace Canvas
 
10287 // @factory L.canvas(options?: Renderer options)
 
10288 // Creates a Canvas renderer with the given options.
 
10289 L.canvas = function (options) {
 
10290         return L.Browser.canvas ? new L.Canvas(options) : null;
 
10293 L.Polyline.prototype._containsPoint = function (p, closed) {
 
10294         var i, j, k, len, len2, part,
 
10295             w = this._clickTolerance();
 
10297         if (!this._pxBounds.contains(p)) { return false; }
 
10299         // hit detection for polylines
 
10300         for (i = 0, len = this._parts.length; i < len; i++) {
 
10301                 part = this._parts[i];
 
10303                 for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
10304                         if (!closed && (j === 0)) { continue; }
 
10306                         if (L.LineUtil.pointToSegmentDistance(p, part[k], part[j]) <= w) {
 
10314 L.Polygon.prototype._containsPoint = function (p) {
 
10315         var inside = false,
 
10316             part, p1, p2, i, j, k, len, len2;
 
10318         if (!this._pxBounds.contains(p)) { return false; }
 
10320         // ray casting algorithm for detecting if point is in polygon
 
10321         for (i = 0, len = this._parts.length; i < len; i++) {
 
10322                 part = this._parts[i];
 
10324                 for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
10328                         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)) {
 
10334         // also check if it's on polygon stroke
 
10335         return inside || L.Polyline.prototype._containsPoint.call(this, p, true);
 
10338 L.CircleMarker.prototype._containsPoint = function (p) {
 
10339         return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
 
10347  * @inherits FeatureGroup
 
10349  * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
 
10350  * GeoJSON data and display it on the map. Extends `FeatureGroup`.
 
10355  * L.geoJSON(data, {
 
10356  *      style: function (feature) {
 
10357  *              return {color: feature.properties.color};
 
10359  * }).bindPopup(function (layer) {
 
10360  *      return layer.feature.properties.description;
 
10365 L.GeoJSON = L.FeatureGroup.extend({
 
10368          * @aka GeoJSON options
 
10370          * @option pointToLayer: Function = *
 
10371          * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
 
10372          * called when data is added, passing the GeoJSON point feature and its `LatLng`.
 
10373          * The default is to spawn a default `Marker`:
 
10375          * function(geoJsonPoint, latlng) {
 
10376          *      return L.marker(latlng);
 
10380          * @option style: Function = *
 
10381          * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
 
10382          * called internally when data is added.
 
10383          * The default value is to not override any defaults:
 
10385          * function (geoJsonFeature) {
 
10390          * @option onEachFeature: Function = *
 
10391          * A `Function` that will be called once for each created `Feature`, after it has
 
10392          * been created and styled. Useful for attaching events and popups to features.
 
10393          * The default is to do nothing with the newly created layers:
 
10395          * function (feature, layer) {}
 
10398          * @option filter: Function = *
 
10399          * A `Function` that will be used to decide whether to include a feature or not.
 
10400          * The default is to include all features:
 
10402          * function (geoJsonFeature) {
 
10406          * Note: dynamically changing the `filter` option will have effect only on newly
 
10407          * added data. It will _not_ re-evaluate already included features.
 
10409          * @option coordsToLatLng: Function = *
 
10410          * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
 
10411          * The default is the `coordsToLatLng` static method.
 
10414         initialize: function (geojson, options) {
 
10415                 L.setOptions(this, options);
 
10420                         this.addData(geojson);
 
10424         // @method addData( <GeoJSON> data ): this
 
10425         // Adds a GeoJSON object to the layer.
 
10426         addData: function (geojson) {
 
10427                 var features = L.Util.isArray(geojson) ? geojson : geojson.features,
 
10431                         for (i = 0, len = features.length; i < len; i++) {
 
10432                                 // only add this if geometry or geometries are set and not null
 
10433                                 feature = features[i];
 
10434                                 if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
 
10435                                         this.addData(feature);
 
10441                 var options = this.options;
 
10443                 if (options.filter && !options.filter(geojson)) { return this; }
 
10445                 var layer = L.GeoJSON.geometryToLayer(geojson, options);
 
10449                 layer.feature = L.GeoJSON.asFeature(geojson);
 
10451                 layer.defaultOptions = layer.options;
 
10452                 this.resetStyle(layer);
 
10454                 if (options.onEachFeature) {
 
10455                         options.onEachFeature(geojson, layer);
 
10458                 return this.addLayer(layer);
 
10461         // @method resetStyle( <Path> layer ): this
 
10462         // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
 
10463         resetStyle: function (layer) {
 
10464                 // reset any custom styles
 
10465                 layer.options = L.Util.extend({}, layer.defaultOptions);
 
10466                 this._setLayerStyle(layer, this.options.style);
 
10470         // @method setStyle( <Function> style ): this
 
10471         // Changes styles of GeoJSON vector layers with the given style function.
 
10472         setStyle: function (style) {
 
10473                 return this.eachLayer(function (layer) {
 
10474                         this._setLayerStyle(layer, style);
 
10478         _setLayerStyle: function (layer, style) {
 
10479                 if (typeof style === 'function') {
 
10480                         style = style(layer.feature);
 
10482                 if (layer.setStyle) {
 
10483                         layer.setStyle(style);
 
10489 // There are several static functions which can be called without instantiating L.GeoJSON:
 
10490 L.extend(L.GeoJSON, {
 
10491         // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
 
10492         // Creates a `Layer` from a given GeoJSON feature. Can use a custom
 
10493         // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
 
10494         // functions if provided as options.
 
10495         geometryToLayer: function (geojson, options) {
 
10497                 var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
 
10498                     coords = geometry ? geometry.coordinates : null,
 
10500                     pointToLayer = options && options.pointToLayer,
 
10501                     coordsToLatLng = options && options.coordsToLatLng || this.coordsToLatLng,
 
10502                     latlng, latlngs, i, len;
 
10504                 if (!coords && !geometry) {
 
10508                 switch (geometry.type) {
 
10510                         latlng = coordsToLatLng(coords);
 
10511                         return pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng);
 
10514                         for (i = 0, len = coords.length; i < len; i++) {
 
10515                                 latlng = coordsToLatLng(coords[i]);
 
10516                                 layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng));
 
10518                         return new L.FeatureGroup(layers);
 
10521                 case 'MultiLineString':
 
10522                         latlngs = this.coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, coordsToLatLng);
 
10523                         return new L.Polyline(latlngs, options);
 
10526                 case 'MultiPolygon':
 
10527                         latlngs = this.coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, coordsToLatLng);
 
10528                         return new L.Polygon(latlngs, options);
 
10530                 case 'GeometryCollection':
 
10531                         for (i = 0, len = geometry.geometries.length; i < len; i++) {
 
10532                                 var layer = this.geometryToLayer({
 
10533                                         geometry: geometry.geometries[i],
 
10535                                         properties: geojson.properties
 
10539                                         layers.push(layer);
 
10542                         return new L.FeatureGroup(layers);
 
10545                         throw new Error('Invalid GeoJSON object.');
 
10549         // @function coordsToLatLng(coords: Array): LatLng
 
10550         // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
 
10551         // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
 
10552         coordsToLatLng: function (coords) {
 
10553                 return new L.LatLng(coords[1], coords[0], coords[2]);
 
10556         // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
 
10557         // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
 
10558         // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
 
10559         // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
 
10560         coordsToLatLngs: function (coords, levelsDeep, coordsToLatLng) {
 
10563                 for (var i = 0, len = coords.length, latlng; i < len; i++) {
 
10564                         latlng = levelsDeep ?
 
10565                                 this.coordsToLatLngs(coords[i], levelsDeep - 1, coordsToLatLng) :
 
10566                                 (coordsToLatLng || this.coordsToLatLng)(coords[i]);
 
10568                         latlngs.push(latlng);
 
10574         // @function latLngToCoords(latlng: LatLng): Array
 
10575         // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
 
10576         latLngToCoords: function (latlng) {
 
10577                 return latlng.alt !== undefined ?
 
10578                                 [latlng.lng, latlng.lat, latlng.alt] :
 
10579                                 [latlng.lng, latlng.lat];
 
10582         // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
 
10583         // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
 
10584         // `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.
 
10585         latLngsToCoords: function (latlngs, levelsDeep, closed) {
 
10588                 for (var i = 0, len = latlngs.length; i < len; i++) {
 
10589                         coords.push(levelsDeep ?
 
10590                                 L.GeoJSON.latLngsToCoords(latlngs[i], levelsDeep - 1, closed) :
 
10591                                 L.GeoJSON.latLngToCoords(latlngs[i]));
 
10594                 if (!levelsDeep && closed) {
 
10595                         coords.push(coords[0]);
 
10601         getFeature: function (layer, newGeometry) {
 
10602                 return layer.feature ?
 
10603                                 L.extend({}, layer.feature, {geometry: newGeometry}) :
 
10604                                 L.GeoJSON.asFeature(newGeometry);
 
10607         // @function asFeature(geojson: Object): Object
 
10608         // Normalize GeoJSON geometries/features into GeoJSON features.
 
10609         asFeature: function (geojson) {
 
10610                 if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
 
10622 var PointToGeoJSON = {
 
10623         toGeoJSON: function () {
 
10624                 return L.GeoJSON.getFeature(this, {
 
10626                         coordinates: L.GeoJSON.latLngToCoords(this.getLatLng())
 
10631 // @namespace Marker
 
10632 // @method toGeoJSON(): Object
 
10633 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
 
10634 L.Marker.include(PointToGeoJSON);
 
10636 // @namespace CircleMarker
 
10637 // @method toGeoJSON(): Object
 
10638 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
 
10639 L.Circle.include(PointToGeoJSON);
 
10640 L.CircleMarker.include(PointToGeoJSON);
 
10643 // @namespace Polyline
 
10644 // @method toGeoJSON(): Object
 
10645 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
 
10646 L.Polyline.prototype.toGeoJSON = function () {
 
10647         var multi = !L.Polyline._flat(this._latlngs);
 
10649         var coords = L.GeoJSON.latLngsToCoords(this._latlngs, multi ? 1 : 0);
 
10651         return L.GeoJSON.getFeature(this, {
 
10652                 type: (multi ? 'Multi' : '') + 'LineString',
 
10653                 coordinates: coords
 
10657 // @namespace Polygon
 
10658 // @method toGeoJSON(): Object
 
10659 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
 
10660 L.Polygon.prototype.toGeoJSON = function () {
 
10661         var holes = !L.Polyline._flat(this._latlngs),
 
10662             multi = holes && !L.Polyline._flat(this._latlngs[0]);
 
10664         var coords = L.GeoJSON.latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true);
 
10670         return L.GeoJSON.getFeature(this, {
 
10671                 type: (multi ? 'Multi' : '') + 'Polygon',
 
10672                 coordinates: coords
 
10677 // @namespace LayerGroup
 
10678 L.LayerGroup.include({
 
10679         toMultiPoint: function () {
 
10682                 this.eachLayer(function (layer) {
 
10683                         coords.push(layer.toGeoJSON().geometry.coordinates);
 
10686                 return L.GeoJSON.getFeature(this, {
 
10687                         type: 'MultiPoint',
 
10688                         coordinates: coords
 
10692         // @method toGeoJSON(): Object
 
10693         // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `GeometryCollection`).
 
10694         toGeoJSON: function () {
 
10696                 var type = this.feature && this.feature.geometry && this.feature.geometry.type;
 
10698                 if (type === 'MultiPoint') {
 
10699                         return this.toMultiPoint();
 
10702                 var isGeometryCollection = type === 'GeometryCollection',
 
10705                 this.eachLayer(function (layer) {
 
10706                         if (layer.toGeoJSON) {
 
10707                                 var json = layer.toGeoJSON();
 
10708                                 jsons.push(isGeometryCollection ? json.geometry : L.GeoJSON.asFeature(json));
 
10712                 if (isGeometryCollection) {
 
10713                         return L.GeoJSON.getFeature(this, {
 
10715                                 type: 'GeometryCollection'
 
10720                         type: 'FeatureCollection',
 
10726 // @namespace GeoJSON
 
10727 // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
 
10728 // Creates a GeoJSON layer. Optionally accepts an object in
 
10729 // [GeoJSON format](http://geojson.org/geojson-spec.html) to display on the map
 
10730 // (you can alternatively add it later with `addData` method) and an `options` object.
 
10731 L.geoJSON = function (geojson, options) {
 
10732         return new L.GeoJSON(geojson, options);
 
10734 // Backward compatibility.
 
10735 L.geoJson = L.geoJSON;
 
10742  * @inherits Evented
 
10744  * A class for making DOM elements draggable (including touch support).
 
10745  * Used internally for map and marker dragging. Only works for elements
 
10746  * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
 
10750  * var draggable = new L.Draggable(elementToDrag);
 
10751  * draggable.enable();
 
10755 L.Draggable = L.Evented.extend({
 
10758                 // @option clickTolerance: Number = 3
 
10759                 // The max number of pixels a user can shift the mouse pointer during a click
 
10760                 // for it to be considered a valid click (as opposed to a mouse drag).
 
10765                 START: L.Browser.touch ? ['touchstart', 'mousedown'] : ['mousedown'],
 
10767                         mousedown: 'mouseup',
 
10768                         touchstart: 'touchend',
 
10769                         pointerdown: 'touchend',
 
10770                         MSPointerDown: 'touchend'
 
10773                         mousedown: 'mousemove',
 
10774                         touchstart: 'touchmove',
 
10775                         pointerdown: 'touchmove',
 
10776                         MSPointerDown: 'touchmove'
 
10780         // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline: Boolean)
 
10781         // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
 
10782         initialize: function (element, dragStartTarget, preventOutline) {
 
10783                 this._element = element;
 
10784                 this._dragStartTarget = dragStartTarget || element;
 
10785                 this._preventOutline = preventOutline;
 
10788         // @method enable()
 
10789         // Enables the dragging ability
 
10790         enable: function () {
 
10791                 if (this._enabled) { return; }
 
10793                 L.DomEvent.on(this._dragStartTarget, L.Draggable.START.join(' '), this._onDown, this);
 
10795                 this._enabled = true;
 
10798         // @method disable()
 
10799         // Disables the dragging ability
 
10800         disable: function () {
 
10801                 if (!this._enabled) { return; }
 
10803                 // If we're currently dragging this draggable,
 
10804                 // disabling it counts as first ending the drag.
 
10805                 if (L.Draggable._dragging === this) {
 
10809                 L.DomEvent.off(this._dragStartTarget, L.Draggable.START.join(' '), this._onDown, this);
 
10811                 this._enabled = false;
 
10812                 this._moved = false;
 
10815         _onDown: function (e) {
 
10816                 // Ignore simulated events, since we handle both touch and
 
10817                 // mouse explicitly; otherwise we risk getting duplicates of
 
10818                 // touch events, see #4315.
 
10819                 // Also ignore the event if disabled; this happens in IE11
 
10820                 // under some circumstances, see #3666.
 
10821                 if (e._simulated || !this._enabled) { return; }
 
10823                 this._moved = false;
 
10825                 if (L.DomUtil.hasClass(this._element, 'leaflet-zoom-anim')) { return; }
 
10827                 if (L.Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
 
10828                 L.Draggable._dragging = this;  // Prevent dragging multiple objects at once.
 
10830                 if (this._preventOutline) {
 
10831                         L.DomUtil.preventOutline(this._element);
 
10834                 L.DomUtil.disableImageDrag();
 
10835                 L.DomUtil.disableTextSelection();
 
10837                 if (this._moving) { return; }
 
10839                 // @event down: Event
 
10840                 // Fired when a drag is about to start.
 
10843                 var first = e.touches ? e.touches[0] : e;
 
10845                 this._startPoint = new L.Point(first.clientX, first.clientY);
 
10848                         .on(document, L.Draggable.MOVE[e.type], this._onMove, this)
 
10849                         .on(document, L.Draggable.END[e.type], this._onUp, this);
 
10852         _onMove: function (e) {
 
10853                 // Ignore simulated events, since we handle both touch and
 
10854                 // mouse explicitly; otherwise we risk getting duplicates of
 
10855                 // touch events, see #4315.
 
10856                 // Also ignore the event if disabled; this happens in IE11
 
10857                 // under some circumstances, see #3666.
 
10858                 if (e._simulated || !this._enabled) { return; }
 
10860                 if (e.touches && e.touches.length > 1) {
 
10861                         this._moved = true;
 
10865                 var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
 
10866                     newPoint = new L.Point(first.clientX, first.clientY),
 
10867                     offset = newPoint.subtract(this._startPoint);
 
10869                 if (!offset.x && !offset.y) { return; }
 
10870                 if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
 
10872                 L.DomEvent.preventDefault(e);
 
10874                 if (!this._moved) {
 
10875                         // @event dragstart: Event
 
10876                         // Fired when a drag starts
 
10877                         this.fire('dragstart');
 
10879                         this._moved = true;
 
10880                         this._startPos = L.DomUtil.getPosition(this._element).subtract(offset);
 
10882                         L.DomUtil.addClass(document.body, 'leaflet-dragging');
 
10884                         this._lastTarget = e.target || e.srcElement;
 
10885                         // IE and Edge do not give the <use> element, so fetch it
 
10887                         if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
 
10888                                 this._lastTarget = this._lastTarget.correspondingUseElement;
 
10890                         L.DomUtil.addClass(this._lastTarget, 'leaflet-drag-target');
 
10893                 this._newPos = this._startPos.add(offset);
 
10894                 this._moving = true;
 
10896                 L.Util.cancelAnimFrame(this._animRequest);
 
10897                 this._lastEvent = e;
 
10898                 this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true);
 
10901         _updatePosition: function () {
 
10902                 var e = {originalEvent: this._lastEvent};
 
10904                 // @event predrag: Event
 
10905                 // Fired continuously during dragging *before* each corresponding
 
10906                 // update of the element's position.
 
10907                 this.fire('predrag', e);
 
10908                 L.DomUtil.setPosition(this._element, this._newPos);
 
10910                 // @event drag: Event
 
10911                 // Fired continuously during dragging.
 
10912                 this.fire('drag', e);
 
10915         _onUp: function (e) {
 
10916                 // Ignore simulated events, since we handle both touch and
 
10917                 // mouse explicitly; otherwise we risk getting duplicates of
 
10918                 // touch events, see #4315.
 
10919                 // Also ignore the event if disabled; this happens in IE11
 
10920                 // under some circumstances, see #3666.
 
10921                 if (e._simulated || !this._enabled) { return; }
 
10925         finishDrag: function () {
 
10926                 L.DomUtil.removeClass(document.body, 'leaflet-dragging');
 
10928                 if (this._lastTarget) {
 
10929                         L.DomUtil.removeClass(this._lastTarget, 'leaflet-drag-target');
 
10930                         this._lastTarget = null;
 
10933                 for (var i in L.Draggable.MOVE) {
 
10935                                 .off(document, L.Draggable.MOVE[i], this._onMove, this)
 
10936                                 .off(document, L.Draggable.END[i], this._onUp, this);
 
10939                 L.DomUtil.enableImageDrag();
 
10940                 L.DomUtil.enableTextSelection();
 
10942                 if (this._moved && this._moving) {
 
10943                         // ensure drag is not fired after dragend
 
10944                         L.Util.cancelAnimFrame(this._animRequest);
 
10946                         // @event dragend: DragEndEvent
 
10947                         // Fired when the drag ends.
 
10948                         this.fire('dragend', {
 
10949                                 distance: this._newPos.distanceTo(this._startPos)
 
10953                 this._moving = false;
 
10954                 L.Draggable._dragging = false;
 
10962         L.Handler is a base class for handler classes that are used internally to inject
 
10963         interaction features like dragging to classes like Map and Marker.
 
10968 // Abstract class for map interaction handlers
 
10970 L.Handler = L.Class.extend({
 
10971         initialize: function (map) {
 
10975         // @method enable(): this
 
10976         // Enables the handler
 
10977         enable: function () {
 
10978                 if (this._enabled) { return this; }
 
10980                 this._enabled = true;
 
10985         // @method disable(): this
 
10986         // Disables the handler
 
10987         disable: function () {
 
10988                 if (!this._enabled) { return this; }
 
10990                 this._enabled = false;
 
10991                 this.removeHooks();
 
10995         // @method enabled(): Boolean
 
10996         // Returns `true` if the handler is enabled
 
10997         enabled: function () {
 
10998                 return !!this._enabled;
 
11001         // @section Extension methods
 
11002         // Classes inheriting from `Handler` must implement the two following methods:
 
11003         // @method addHooks()
 
11004         // Called when the handler is enabled, should add event hooks.
 
11005         // @method removeHooks()
 
11006         // Called when the handler is disabled, should remove the event hooks added previously.
 
11012  * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
 
11016 // @section Interaction Options
 
11017 L.Map.mergeOptions({
 
11018         // @option dragging: Boolean = true
 
11019         // Whether the map be draggable with mouse/touch or not.
 
11022         // @section Panning Inertia Options
 
11023         // @option inertia: Boolean = *
 
11024         // If enabled, panning of the map will have an inertia effect where
 
11025         // the map builds momentum while dragging and continues moving in
 
11026         // the same direction for some time. Feels especially nice on touch
 
11027         // devices. Enabled by default unless running on old Android devices.
 
11028         inertia: !L.Browser.android23,
 
11030         // @option inertiaDeceleration: Number = 3000
 
11031         // The rate with which the inertial movement slows down, in pixels/second².
 
11032         inertiaDeceleration: 3400, // px/s^2
 
11034         // @option inertiaMaxSpeed: Number = Infinity
 
11035         // Max speed of the inertial movement, in pixels/second.
 
11036         inertiaMaxSpeed: Infinity, // px/s
 
11038         // @option easeLinearity: Number = 0.2
 
11039         easeLinearity: 0.2,
 
11041         // TODO refactor, move to CRS
 
11042         // @option worldCopyJump: Boolean = false
 
11043         // With this option enabled, the map tracks when you pan to another "copy"
 
11044         // of the world and seamlessly jumps to the original one so that all overlays
 
11045         // like markers and vector layers are still visible.
 
11046         worldCopyJump: false,
 
11048         // @option maxBoundsViscosity: Number = 0.0
 
11049         // If `maxBounds` is set, this option will control how solid the bounds
 
11050         // are when dragging the map around. The default value of `0.0` allows the
 
11051         // user to drag outside the bounds at normal speed, higher values will
 
11052         // slow down map dragging outside bounds, and `1.0` makes the bounds fully
 
11053         // solid, preventing the user from dragging outside the bounds.
 
11054         maxBoundsViscosity: 0.0
 
11057 L.Map.Drag = L.Handler.extend({
 
11058         addHooks: function () {
 
11059                 if (!this._draggable) {
 
11060                         var map = this._map;
 
11062                         this._draggable = new L.Draggable(map._mapPane, map._container);
 
11064                         this._draggable.on({
 
11065                                 down: this._onDown,
 
11066                                 dragstart: this._onDragStart,
 
11067                                 drag: this._onDrag,
 
11068                                 dragend: this._onDragEnd
 
11071                         this._draggable.on('predrag', this._onPreDragLimit, this);
 
11072                         if (map.options.worldCopyJump) {
 
11073                                 this._draggable.on('predrag', this._onPreDragWrap, this);
 
11074                                 map.on('zoomend', this._onZoomEnd, this);
 
11076                                 map.whenReady(this._onZoomEnd, this);
 
11079                 L.DomUtil.addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
 
11080                 this._draggable.enable();
 
11081                 this._positions = [];
 
11085         removeHooks: function () {
 
11086                 L.DomUtil.removeClass(this._map._container, 'leaflet-grab');
 
11087                 L.DomUtil.removeClass(this._map._container, 'leaflet-touch-drag');
 
11088                 this._draggable.disable();
 
11091         moved: function () {
 
11092                 return this._draggable && this._draggable._moved;
 
11095         moving: function () {
 
11096                 return this._draggable && this._draggable._moving;
 
11099         _onDown: function () {
 
11103         _onDragStart: function () {
 
11104                 var map = this._map;
 
11106                 if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
 
11107                         var bounds = L.latLngBounds(this._map.options.maxBounds);
 
11109                         this._offsetLimit = L.bounds(
 
11110                                 this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
 
11111                                 this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
 
11112                                         .add(this._map.getSize()));
 
11114                         this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
 
11116                         this._offsetLimit = null;
 
11121                     .fire('dragstart');
 
11123                 if (map.options.inertia) {
 
11124                         this._positions = [];
 
11129         _onDrag: function (e) {
 
11130                 if (this._map.options.inertia) {
 
11131                         var time = this._lastTime = +new Date(),
 
11132                             pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
 
11134                         this._positions.push(pos);
 
11135                         this._times.push(time);
 
11137                         if (time - this._times[0] > 50) {
 
11138                                 this._positions.shift();
 
11139                                 this._times.shift();
 
11148         _onZoomEnd: function () {
 
11149                 var pxCenter = this._map.getSize().divideBy(2),
 
11150                     pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
 
11152                 this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
 
11153                 this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
 
11156         _viscousLimit: function (value, threshold) {
 
11157                 return value - (value - threshold) * this._viscosity;
 
11160         _onPreDragLimit: function () {
 
11161                 if (!this._viscosity || !this._offsetLimit) { return; }
 
11163                 var offset = this._draggable._newPos.subtract(this._draggable._startPos);
 
11165                 var limit = this._offsetLimit;
 
11166                 if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
 
11167                 if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
 
11168                 if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
 
11169                 if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
 
11171                 this._draggable._newPos = this._draggable._startPos.add(offset);
 
11174         _onPreDragWrap: function () {
 
11175                 // TODO refactor to be able to adjust map pane position after zoom
 
11176                 var worldWidth = this._worldWidth,
 
11177                     halfWidth = Math.round(worldWidth / 2),
 
11178                     dx = this._initialWorldOffset,
 
11179                     x = this._draggable._newPos.x,
 
11180                     newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
 
11181                     newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
 
11182                     newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
 
11184                 this._draggable._absPos = this._draggable._newPos.clone();
 
11185                 this._draggable._newPos.x = newX;
 
11188         _onDragEnd: function (e) {
 
11189                 var map = this._map,
 
11190                     options = map.options,
 
11192                     noInertia = !options.inertia || this._times.length < 2;
 
11194                 map.fire('dragend', e);
 
11197                         map.fire('moveend');
 
11201                         var direction = this._lastPos.subtract(this._positions[0]),
 
11202                             duration = (this._lastTime - this._times[0]) / 1000,
 
11203                             ease = options.easeLinearity,
 
11205                             speedVector = direction.multiplyBy(ease / duration),
 
11206                             speed = speedVector.distanceTo([0, 0]),
 
11208                             limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
 
11209                             limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
 
11211                             decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
 
11212                             offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
 
11214                         if (!offset.x && !offset.y) {
 
11215                                 map.fire('moveend');
 
11218                                 offset = map._limitOffset(offset, map.options.maxBounds);
 
11220                                 L.Util.requestAnimFrame(function () {
 
11221                                         map.panBy(offset, {
 
11222                                                 duration: decelerationDuration,
 
11223                                                 easeLinearity: ease,
 
11233 // @section Handlers
 
11234 // @property dragging: Handler
 
11235 // Map dragging handler (by both mouse and touch).
 
11236 L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag);
 
11241  * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
 
11245 // @section Interaction Options
 
11247 L.Map.mergeOptions({
 
11248         // @option doubleClickZoom: Boolean|String = true
 
11249         // Whether the map can be zoomed in by double clicking on it and
 
11250         // zoomed out by double clicking while holding shift. If passed
 
11251         // `'center'`, double-click zoom will zoom to the center of the
 
11252         //  view regardless of where the mouse was.
 
11253         doubleClickZoom: true
 
11256 L.Map.DoubleClickZoom = L.Handler.extend({
 
11257         addHooks: function () {
 
11258                 this._map.on('dblclick', this._onDoubleClick, this);
 
11261         removeHooks: function () {
 
11262                 this._map.off('dblclick', this._onDoubleClick, this);
 
11265         _onDoubleClick: function (e) {
 
11266                 var map = this._map,
 
11267                     oldZoom = map.getZoom(),
 
11268                     delta = map.options.zoomDelta,
 
11269                     zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
 
11271                 if (map.options.doubleClickZoom === 'center') {
 
11274                         map.setZoomAround(e.containerPoint, zoom);
 
11279 // @section Handlers
 
11281 // Map properties include interaction handlers that allow you to control
 
11282 // interaction behavior in runtime, enabling or disabling certain features such
 
11283 // as dragging or touch zoom (see `Handler` methods). For example:
 
11286 // map.doubleClickZoom.disable();
 
11289 // @property doubleClickZoom: Handler
 
11290 // Double click zoom handler.
 
11291 L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom);
 
11296  * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
 
11300 // @section Interaction Options
 
11301 L.Map.mergeOptions({
 
11302         // @section Mousewheel options
 
11303         // @option scrollWheelZoom: Boolean|String = true
 
11304         // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
 
11305         // it will zoom to the center of the view regardless of where the mouse was.
 
11306         scrollWheelZoom: true,
 
11308         // @option wheelDebounceTime: Number = 40
 
11309         // Limits the rate at which a wheel can fire (in milliseconds). By default
 
11310         // user can't zoom via wheel more often than once per 40 ms.
 
11311         wheelDebounceTime: 40,
 
11313         // @option wheelPxPerZoomLevel: Number = 60
 
11314         // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
 
11315         // mean a change of one full zoom level. Smaller values will make wheel-zooming
 
11316         // faster (and vice versa).
 
11317         wheelPxPerZoomLevel: 60
 
11320 L.Map.ScrollWheelZoom = L.Handler.extend({
 
11321         addHooks: function () {
 
11322                 L.DomEvent.on(this._map._container, 'mousewheel', this._onWheelScroll, this);
 
11327         removeHooks: function () {
 
11328                 L.DomEvent.off(this._map._container, 'mousewheel', this._onWheelScroll, this);
 
11331         _onWheelScroll: function (e) {
 
11332                 var delta = L.DomEvent.getWheelDelta(e);
 
11334                 var debounce = this._map.options.wheelDebounceTime;
 
11336                 this._delta += delta;
 
11337                 this._lastMousePos = this._map.mouseEventToContainerPoint(e);
 
11339                 if (!this._startTime) {
 
11340                         this._startTime = +new Date();
 
11343                 var left = Math.max(debounce - (+new Date() - this._startTime), 0);
 
11345                 clearTimeout(this._timer);
 
11346                 this._timer = setTimeout(L.bind(this._performZoom, this), left);
 
11348                 L.DomEvent.stop(e);
 
11351         _performZoom: function () {
 
11352                 var map = this._map,
 
11353                     zoom = map.getZoom(),
 
11354                     snap = this._map.options.zoomSnap || 0;
 
11356                 map._stop(); // stop panning and fly animations if any
 
11358                 // map the delta with a sigmoid function to -4..4 range leaning on -1..1
 
11359                 var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
 
11360                     d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
 
11361                     d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
 
11362                     delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
 
11365                 this._startTime = null;
 
11367                 if (!delta) { return; }
 
11369                 if (map.options.scrollWheelZoom === 'center') {
 
11370                         map.setZoom(zoom + delta);
 
11372                         map.setZoomAround(this._lastMousePos, zoom + delta);
 
11377 // @section Handlers
 
11378 // @property scrollWheelZoom: Handler
 
11379 // Scroll wheel zoom handler.
 
11380 L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom);
 
11385  * Extends the event handling code with double tap support for mobile browsers.
 
11388 L.extend(L.DomEvent, {
 
11390         _touchstart: L.Browser.msPointer ? 'MSPointerDown' : L.Browser.pointer ? 'pointerdown' : 'touchstart',
 
11391         _touchend: L.Browser.msPointer ? 'MSPointerUp' : L.Browser.pointer ? 'pointerup' : 'touchend',
 
11393         // inspired by Zepto touch code by Thomas Fuchs
 
11394         addDoubleTapListener: function (obj, handler, id) {
 
11399                 function onTouchStart(e) {
 
11402                         if (L.Browser.pointer) {
 
11403                                 if ((!L.Browser.edge) || e.pointerType === 'mouse') { return; }
 
11404                                 count = L.DomEvent._pointersCount;
 
11406                                 count = e.touches.length;
 
11409                         if (count > 1) { return; }
 
11411                         var now = Date.now(),
 
11412                             delta = now - (last || now);
 
11414                         touch = e.touches ? e.touches[0] : e;
 
11415                         doubleTap = (delta > 0 && delta <= delay);
 
11419                 function onTouchEnd(e) {
 
11420                         if (doubleTap && !touch.cancelBubble) {
 
11421                                 if (L.Browser.pointer) {
 
11422                                         if ((!L.Browser.edge) || e.pointerType === 'mouse') { return; }
 
11424                                         // work around .type being readonly with MSPointer* events
 
11430                                                 newTouch[i] = prop && prop.bind ? prop.bind(touch) : prop;
 
11434                                 touch.type = 'dblclick';
 
11440                 var pre = '_leaflet_',
 
11441                     touchstart = this._touchstart,
 
11442                     touchend = this._touchend;
 
11444                 obj[pre + touchstart + id] = onTouchStart;
 
11445                 obj[pre + touchend + id] = onTouchEnd;
 
11446                 obj[pre + 'dblclick' + id] = handler;
 
11448                 obj.addEventListener(touchstart, onTouchStart, false);
 
11449                 obj.addEventListener(touchend, onTouchEnd, false);
 
11451                 // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse),
 
11452                 // the browser doesn't fire touchend/pointerup events but does fire
 
11453                 // native dblclicks. See #4127.
 
11454                 // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180.
 
11455                 obj.addEventListener('dblclick', handler, false);
 
11460         removeDoubleTapListener: function (obj, id) {
 
11461                 var pre = '_leaflet_',
 
11462                     touchstart = obj[pre + this._touchstart + id],
 
11463                     touchend = obj[pre + this._touchend + id],
 
11464                     dblclick = obj[pre + 'dblclick' + id];
 
11466                 obj.removeEventListener(this._touchstart, touchstart, false);
 
11467                 obj.removeEventListener(this._touchend, touchend, false);
 
11468                 if (!L.Browser.edge) {
 
11469                         obj.removeEventListener('dblclick', dblclick, false);
 
11479  * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
 
11482 L.extend(L.DomEvent, {
 
11484         POINTER_DOWN:   L.Browser.msPointer ? 'MSPointerDown'   : 'pointerdown',
 
11485         POINTER_MOVE:   L.Browser.msPointer ? 'MSPointerMove'   : 'pointermove',
 
11486         POINTER_UP:     L.Browser.msPointer ? 'MSPointerUp'     : 'pointerup',
 
11487         POINTER_CANCEL: L.Browser.msPointer ? 'MSPointerCancel' : 'pointercancel',
 
11488         TAG_WHITE_LIST: ['INPUT', 'SELECT', 'OPTION'],
 
11493         // Provides a touch events wrapper for (ms)pointer events.
 
11494         // ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
 
11496         addPointerListener: function (obj, type, handler, id) {
 
11498                 if (type === 'touchstart') {
 
11499                         this._addPointerStart(obj, handler, id);
 
11501                 } else if (type === 'touchmove') {
 
11502                         this._addPointerMove(obj, handler, id);
 
11504                 } else if (type === 'touchend') {
 
11505                         this._addPointerEnd(obj, handler, id);
 
11511         removePointerListener: function (obj, type, id) {
 
11512                 var handler = obj['_leaflet_' + type + id];
 
11514                 if (type === 'touchstart') {
 
11515                         obj.removeEventListener(this.POINTER_DOWN, handler, false);
 
11517                 } else if (type === 'touchmove') {
 
11518                         obj.removeEventListener(this.POINTER_MOVE, handler, false);
 
11520                 } else if (type === 'touchend') {
 
11521                         obj.removeEventListener(this.POINTER_UP, handler, false);
 
11522                         obj.removeEventListener(this.POINTER_CANCEL, handler, false);
 
11528         _addPointerStart: function (obj, handler, id) {
 
11529                 var onDown = L.bind(function (e) {
 
11530                         if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
 
11531                                 // In IE11, some touch events needs to fire for form controls, or
 
11532                                 // the controls will stop working. We keep a whitelist of tag names that
 
11533                                 // need these events. For other target tags, we prevent default on the event.
 
11534                                 if (this.TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
 
11535                                         L.DomEvent.preventDefault(e);
 
11541                         this._handlePointer(e, handler);
 
11544                 obj['_leaflet_touchstart' + id] = onDown;
 
11545                 obj.addEventListener(this.POINTER_DOWN, onDown, false);
 
11547                 // need to keep track of what pointers and how many are active to provide e.touches emulation
 
11548                 if (!this._pointerDocListener) {
 
11549                         var pointerUp = L.bind(this._globalPointerUp, this);
 
11551                         // we listen documentElement as any drags that end by moving the touch off the screen get fired there
 
11552                         document.documentElement.addEventListener(this.POINTER_DOWN, L.bind(this._globalPointerDown, this), true);
 
11553                         document.documentElement.addEventListener(this.POINTER_MOVE, L.bind(this._globalPointerMove, this), true);
 
11554                         document.documentElement.addEventListener(this.POINTER_UP, pointerUp, true);
 
11555                         document.documentElement.addEventListener(this.POINTER_CANCEL, pointerUp, true);
 
11557                         this._pointerDocListener = true;
 
11561         _globalPointerDown: function (e) {
 
11562                 this._pointers[e.pointerId] = e;
 
11563                 this._pointersCount++;
 
11566         _globalPointerMove: function (e) {
 
11567                 if (this._pointers[e.pointerId]) {
 
11568                         this._pointers[e.pointerId] = e;
 
11572         _globalPointerUp: function (e) {
 
11573                 delete this._pointers[e.pointerId];
 
11574                 this._pointersCount--;
 
11577         _handlePointer: function (e, handler) {
 
11579                 for (var i in this._pointers) {
 
11580                         e.touches.push(this._pointers[i]);
 
11582                 e.changedTouches = [e];
 
11587         _addPointerMove: function (obj, handler, id) {
 
11588                 var onMove = L.bind(function (e) {
 
11589                         // don't fire touch moves when mouse isn't down
 
11590                         if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
 
11592                         this._handlePointer(e, handler);
 
11595                 obj['_leaflet_touchmove' + id] = onMove;
 
11596                 obj.addEventListener(this.POINTER_MOVE, onMove, false);
 
11599         _addPointerEnd: function (obj, handler, id) {
 
11600                 var onUp = L.bind(function (e) {
 
11601                         this._handlePointer(e, handler);
 
11604                 obj['_leaflet_touchend' + id] = onUp;
 
11605                 obj.addEventListener(this.POINTER_UP, onUp, false);
 
11606                 obj.addEventListener(this.POINTER_CANCEL, onUp, false);
 
11613  * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
 
11617 // @section Interaction Options
 
11618 L.Map.mergeOptions({
 
11619         // @section Touch interaction options
 
11620         // @option touchZoom: Boolean|String = *
 
11621         // Whether the map can be zoomed by touch-dragging with two fingers. If
 
11622         // passed `'center'`, it will zoom to the center of the view regardless of
 
11623         // where the touch events (fingers) were. Enabled for touch-capable web
 
11624         // browsers except for old Androids.
 
11625         touchZoom: L.Browser.touch && !L.Browser.android23,
 
11627         // @option bounceAtZoomLimits: Boolean = true
 
11628         // Set it to false if you don't want the map to zoom beyond min/max zoom
 
11629         // and then bounce back when pinch-zooming.
 
11630         bounceAtZoomLimits: true
 
11633 L.Map.TouchZoom = L.Handler.extend({
 
11634         addHooks: function () {
 
11635                 L.DomUtil.addClass(this._map._container, 'leaflet-touch-zoom');
 
11636                 L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);
 
11639         removeHooks: function () {
 
11640                 L.DomUtil.removeClass(this._map._container, 'leaflet-touch-zoom');
 
11641                 L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this);
 
11644         _onTouchStart: function (e) {
 
11645                 var map = this._map;
 
11646                 if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
 
11648                 var p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
11649                     p2 = map.mouseEventToContainerPoint(e.touches[1]);
 
11651                 this._centerPoint = map.getSize()._divideBy(2);
 
11652                 this._startLatLng = map.containerPointToLatLng(this._centerPoint);
 
11653                 if (map.options.touchZoom !== 'center') {
 
11654                         this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
 
11657                 this._startDist = p1.distanceTo(p2);
 
11658                 this._startZoom = map.getZoom();
 
11660                 this._moved = false;
 
11661                 this._zooming = true;
 
11666                     .on(document, 'touchmove', this._onTouchMove, this)
 
11667                     .on(document, 'touchend', this._onTouchEnd, this);
 
11669                 L.DomEvent.preventDefault(e);
 
11672         _onTouchMove: function (e) {
 
11673                 if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
 
11675                 var map = this._map,
 
11676                     p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
11677                     p2 = map.mouseEventToContainerPoint(e.touches[1]),
 
11678                     scale = p1.distanceTo(p2) / this._startDist;
 
11681                 this._zoom = map.getScaleZoom(scale, this._startZoom);
 
11683                 if (!map.options.bounceAtZoomLimits && (
 
11684                         (this._zoom < map.getMinZoom() && scale < 1) ||
 
11685                         (this._zoom > map.getMaxZoom() && scale > 1))) {
 
11686                         this._zoom = map._limitZoom(this._zoom);
 
11689                 if (map.options.touchZoom === 'center') {
 
11690                         this._center = this._startLatLng;
 
11691                         if (scale === 1) { return; }
 
11693                         // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
 
11694                         var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
 
11695                         if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
 
11696                         this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
 
11699                 if (!this._moved) {
 
11700                         map._moveStart(true);
 
11701                         this._moved = true;
 
11704                 L.Util.cancelAnimFrame(this._animRequest);
 
11706                 var moveFn = L.bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
 
11707                 this._animRequest = L.Util.requestAnimFrame(moveFn, this, true);
 
11709                 L.DomEvent.preventDefault(e);
 
11712         _onTouchEnd: function () {
 
11713                 if (!this._moved || !this._zooming) {
 
11714                         this._zooming = false;
 
11718                 this._zooming = false;
 
11719                 L.Util.cancelAnimFrame(this._animRequest);
 
11722                     .off(document, 'touchmove', this._onTouchMove)
 
11723                     .off(document, 'touchend', this._onTouchEnd);
 
11725                 // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
 
11726                 if (this._map.options.zoomAnimation) {
 
11727                         this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
 
11729                         this._map._resetView(this._center, this._map._limitZoom(this._zoom));
 
11734 // @section Handlers
 
11735 // @property touchZoom: Handler
 
11736 // Touch zoom handler.
 
11737 L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);
 
11742  * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
 
11746 // @section Interaction Options
 
11747 L.Map.mergeOptions({
 
11748         // @section Touch interaction options
 
11749         // @option tap: Boolean = true
 
11750         // Enables mobile hacks for supporting instant taps (fixing 200ms click
 
11751         // delay on iOS/Android) and touch holds (fired as `contextmenu` events).
 
11754         // @option tapTolerance: Number = 15
 
11755         // The max number of pixels a user can shift his finger during touch
 
11756         // for it to be considered a valid tap.
 
11760 L.Map.Tap = L.Handler.extend({
 
11761         addHooks: function () {
 
11762                 L.DomEvent.on(this._map._container, 'touchstart', this._onDown, this);
 
11765         removeHooks: function () {
 
11766                 L.DomEvent.off(this._map._container, 'touchstart', this._onDown, this);
 
11769         _onDown: function (e) {
 
11770                 if (!e.touches) { return; }
 
11772                 L.DomEvent.preventDefault(e);
 
11774                 this._fireClick = true;
 
11776                 // don't simulate click or track longpress if more than 1 touch
 
11777                 if (e.touches.length > 1) {
 
11778                         this._fireClick = false;
 
11779                         clearTimeout(this._holdTimeout);
 
11783                 var first = e.touches[0],
 
11786                 this._startPos = this._newPos = new L.Point(first.clientX, first.clientY);
 
11788                 // if touching a link, highlight it
 
11789                 if (el.tagName && el.tagName.toLowerCase() === 'a') {
 
11790                         L.DomUtil.addClass(el, 'leaflet-active');
 
11793                 // simulate long hold but setting a timeout
 
11794                 this._holdTimeout = setTimeout(L.bind(function () {
 
11795                         if (this._isTapValid()) {
 
11796                                 this._fireClick = false;
 
11798                                 this._simulateEvent('contextmenu', first);
 
11802                 this._simulateEvent('mousedown', first);
 
11804                 L.DomEvent.on(document, {
 
11805                         touchmove: this._onMove,
 
11806                         touchend: this._onUp
 
11810         _onUp: function (e) {
 
11811                 clearTimeout(this._holdTimeout);
 
11813                 L.DomEvent.off(document, {
 
11814                         touchmove: this._onMove,
 
11815                         touchend: this._onUp
 
11818                 if (this._fireClick && e && e.changedTouches) {
 
11820                         var first = e.changedTouches[0],
 
11823                         if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
 
11824                                 L.DomUtil.removeClass(el, 'leaflet-active');
 
11827                         this._simulateEvent('mouseup', first);
 
11829                         // simulate click if the touch didn't move too much
 
11830                         if (this._isTapValid()) {
 
11831                                 this._simulateEvent('click', first);
 
11836         _isTapValid: function () {
 
11837                 return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
 
11840         _onMove: function (e) {
 
11841                 var first = e.touches[0];
 
11842                 this._newPos = new L.Point(first.clientX, first.clientY);
 
11843                 this._simulateEvent('mousemove', first);
 
11846         _simulateEvent: function (type, e) {
 
11847                 var simulatedEvent = document.createEvent('MouseEvents');
 
11849                 simulatedEvent._simulated = true;
 
11850                 e.target._simulatedClick = true;
 
11852                 simulatedEvent.initMouseEvent(
 
11853                         type, true, true, window, 1,
 
11854                         e.screenX, e.screenY,
 
11855                         e.clientX, e.clientY,
 
11856                         false, false, false, false, 0, null);
 
11858                 e.target.dispatchEvent(simulatedEvent);
 
11862 // @section Handlers
 
11863 // @property tap: Handler
 
11864 // Mobile touch hacks (quick tap and touch hold) handler.
 
11865 if (L.Browser.touch && !L.Browser.pointer) {
 
11866         L.Map.addInitHook('addHandler', 'tap', L.Map.Tap);
 
11872  * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
 
11873  * (zoom to a selected bounding box), enabled by default.
 
11877 // @section Interaction Options
 
11878 L.Map.mergeOptions({
 
11879         // @option boxZoom: Boolean = true
 
11880         // Whether the map can be zoomed to a rectangular area specified by
 
11881         // dragging the mouse while pressing the shift key.
 
11885 L.Map.BoxZoom = L.Handler.extend({
 
11886         initialize: function (map) {
 
11888                 this._container = map._container;
 
11889                 this._pane = map._panes.overlayPane;
 
11892         addHooks: function () {
 
11893                 L.DomEvent.on(this._container, 'mousedown', this._onMouseDown, this);
 
11896         removeHooks: function () {
 
11897                 L.DomEvent.off(this._container, 'mousedown', this._onMouseDown, this);
 
11900         moved: function () {
 
11901                 return this._moved;
 
11904         _resetState: function () {
 
11905                 this._moved = false;
 
11908         _onMouseDown: function (e) {
 
11909                 if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
 
11911                 this._resetState();
 
11913                 L.DomUtil.disableTextSelection();
 
11914                 L.DomUtil.disableImageDrag();
 
11916                 this._startPoint = this._map.mouseEventToContainerPoint(e);
 
11918                 L.DomEvent.on(document, {
 
11919                         contextmenu: L.DomEvent.stop,
 
11920                         mousemove: this._onMouseMove,
 
11921                         mouseup: this._onMouseUp,
 
11922                         keydown: this._onKeyDown
 
11926         _onMouseMove: function (e) {
 
11927                 if (!this._moved) {
 
11928                         this._moved = true;
 
11930                         this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._container);
 
11931                         L.DomUtil.addClass(this._container, 'leaflet-crosshair');
 
11933                         this._map.fire('boxzoomstart');
 
11936                 this._point = this._map.mouseEventToContainerPoint(e);
 
11938                 var bounds = new L.Bounds(this._point, this._startPoint),
 
11939                     size = bounds.getSize();
 
11941                 L.DomUtil.setPosition(this._box, bounds.min);
 
11943                 this._box.style.width  = size.x + 'px';
 
11944                 this._box.style.height = size.y + 'px';
 
11947         _finish: function () {
 
11949                         L.DomUtil.remove(this._box);
 
11950                         L.DomUtil.removeClass(this._container, 'leaflet-crosshair');
 
11953                 L.DomUtil.enableTextSelection();
 
11954                 L.DomUtil.enableImageDrag();
 
11956                 L.DomEvent.off(document, {
 
11957                         contextmenu: L.DomEvent.stop,
 
11958                         mousemove: this._onMouseMove,
 
11959                         mouseup: this._onMouseUp,
 
11960                         keydown: this._onKeyDown
 
11964         _onMouseUp: function (e) {
 
11965                 if ((e.which !== 1) && (e.button !== 1)) { return; }
 
11969                 if (!this._moved) { return; }
 
11970                 // Postpone to next JS tick so internal click event handling
 
11971                 // still see it as "moved".
 
11972                 setTimeout(L.bind(this._resetState, this), 0);
 
11974                 var bounds = new L.LatLngBounds(
 
11975                         this._map.containerPointToLatLng(this._startPoint),
 
11976                         this._map.containerPointToLatLng(this._point));
 
11980                         .fire('boxzoomend', {boxZoomBounds: bounds});
 
11983         _onKeyDown: function (e) {
 
11984                 if (e.keyCode === 27) {
 
11990 // @section Handlers
 
11991 // @property boxZoom: Handler
 
11992 // Box (shift-drag with mouse) zoom handler.
 
11993 L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom);
 
11998  * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
 
12002 // @section Keyboard Navigation Options
 
12003 L.Map.mergeOptions({
 
12004         // @option keyboard: Boolean = true
 
12005         // Makes the map focusable and allows users to navigate the map with keyboard
 
12006         // arrows and `+`/`-` keys.
 
12009         // @option keyboardPanDelta: Number = 80
 
12010         // Amount of pixels to pan when pressing an arrow key.
 
12011         keyboardPanDelta: 80
 
12014 L.Map.Keyboard = L.Handler.extend({
 
12021                 zoomIn:  [187, 107, 61, 171],
 
12022                 zoomOut: [189, 109, 54, 173]
 
12025         initialize: function (map) {
 
12028                 this._setPanDelta(map.options.keyboardPanDelta);
 
12029                 this._setZoomDelta(map.options.zoomDelta);
 
12032         addHooks: function () {
 
12033                 var container = this._map._container;
 
12035                 // make the container focusable by tabbing
 
12036                 if (container.tabIndex <= 0) {
 
12037                         container.tabIndex = '0';
 
12040                 L.DomEvent.on(container, {
 
12041                         focus: this._onFocus,
 
12042                         blur: this._onBlur,
 
12043                         mousedown: this._onMouseDown
 
12047                         focus: this._addHooks,
 
12048                         blur: this._removeHooks
 
12052         removeHooks: function () {
 
12053                 this._removeHooks();
 
12055                 L.DomEvent.off(this._map._container, {
 
12056                         focus: this._onFocus,
 
12057                         blur: this._onBlur,
 
12058                         mousedown: this._onMouseDown
 
12062                         focus: this._addHooks,
 
12063                         blur: this._removeHooks
 
12067         _onMouseDown: function () {
 
12068                 if (this._focused) { return; }
 
12070                 var body = document.body,
 
12071                     docEl = document.documentElement,
 
12072                     top = body.scrollTop || docEl.scrollTop,
 
12073                     left = body.scrollLeft || docEl.scrollLeft;
 
12075                 this._map._container.focus();
 
12077                 window.scrollTo(left, top);
 
12080         _onFocus: function () {
 
12081                 this._focused = true;
 
12082                 this._map.fire('focus');
 
12085         _onBlur: function () {
 
12086                 this._focused = false;
 
12087                 this._map.fire('blur');
 
12090         _setPanDelta: function (panDelta) {
 
12091                 var keys = this._panKeys = {},
 
12092                     codes = this.keyCodes,
 
12095                 for (i = 0, len = codes.left.length; i < len; i++) {
 
12096                         keys[codes.left[i]] = [-1 * panDelta, 0];
 
12098                 for (i = 0, len = codes.right.length; i < len; i++) {
 
12099                         keys[codes.right[i]] = [panDelta, 0];
 
12101                 for (i = 0, len = codes.down.length; i < len; i++) {
 
12102                         keys[codes.down[i]] = [0, panDelta];
 
12104                 for (i = 0, len = codes.up.length; i < len; i++) {
 
12105                         keys[codes.up[i]] = [0, -1 * panDelta];
 
12109         _setZoomDelta: function (zoomDelta) {
 
12110                 var keys = this._zoomKeys = {},
 
12111                     codes = this.keyCodes,
 
12114                 for (i = 0, len = codes.zoomIn.length; i < len; i++) {
 
12115                         keys[codes.zoomIn[i]] = zoomDelta;
 
12117                 for (i = 0, len = codes.zoomOut.length; i < len; i++) {
 
12118                         keys[codes.zoomOut[i]] = -zoomDelta;
 
12122         _addHooks: function () {
 
12123                 L.DomEvent.on(document, 'keydown', this._onKeyDown, this);
 
12126         _removeHooks: function () {
 
12127                 L.DomEvent.off(document, 'keydown', this._onKeyDown, this);
 
12130         _onKeyDown: function (e) {
 
12131                 if (e.altKey || e.ctrlKey || e.metaKey) { return; }
 
12133                 var key = e.keyCode,
 
12137                 if (key in this._panKeys) {
 
12139                         if (map._panAnim && map._panAnim._inProgress) { return; }
 
12141                         offset = this._panKeys[key];
 
12143                                 offset = L.point(offset).multiplyBy(3);
 
12148                         if (map.options.maxBounds) {
 
12149                                 map.panInsideBounds(map.options.maxBounds);
 
12152                 } else if (key in this._zoomKeys) {
 
12153                         map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
 
12155                 } else if (key === 27) {
 
12162                 L.DomEvent.stop(e);
 
12166 // @section Handlers
 
12167 // @section Handlers
 
12168 // @property keyboard: Handler
 
12169 // Keyboard navigation handler.
 
12170 L.Map.addInitHook('addHandler', 'keyboard', L.Map.Keyboard);
 
12175  * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
 
12179 /* @namespace Marker
 
12180  * @section Interaction handlers
 
12182  * 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:
 
12185  * marker.dragging.disable();
 
12188  * @property dragging: Handler
 
12189  * Marker dragging handler (by both mouse and touch).
 
12192 L.Handler.MarkerDrag = L.Handler.extend({
 
12193         initialize: function (marker) {
 
12194                 this._marker = marker;
 
12197         addHooks: function () {
 
12198                 var icon = this._marker._icon;
 
12200                 if (!this._draggable) {
 
12201                         this._draggable = new L.Draggable(icon, icon, true);
 
12204                 this._draggable.on({
 
12205                         dragstart: this._onDragStart,
 
12206                         drag: this._onDrag,
 
12207                         dragend: this._onDragEnd
 
12210                 L.DomUtil.addClass(icon, 'leaflet-marker-draggable');
 
12213         removeHooks: function () {
 
12214                 this._draggable.off({
 
12215                         dragstart: this._onDragStart,
 
12216                         drag: this._onDrag,
 
12217                         dragend: this._onDragEnd
 
12218                 }, this).disable();
 
12220                 if (this._marker._icon) {
 
12221                         L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable');
 
12225         moved: function () {
 
12226                 return this._draggable && this._draggable._moved;
 
12229         _onDragStart: function () {
 
12230                 // @section Dragging events
 
12231                 // @event dragstart: Event
 
12232                 // Fired when the user starts dragging the marker.
 
12234                 // @event movestart: Event
 
12235                 // Fired when the marker starts moving (because of dragging).
 
12237                 this._oldLatLng = this._marker.getLatLng();
 
12241                     .fire('dragstart');
 
12244         _onDrag: function (e) {
 
12245                 var marker = this._marker,
 
12246                     shadow = marker._shadow,
 
12247                     iconPos = L.DomUtil.getPosition(marker._icon),
 
12248                     latlng = marker._map.layerPointToLatLng(iconPos);
 
12250                 // update shadow position
 
12252                         L.DomUtil.setPosition(shadow, iconPos);
 
12255                 marker._latlng = latlng;
 
12257                 e.oldLatLng = this._oldLatLng;
 
12259                 // @event drag: Event
 
12260                 // Fired repeatedly while the user drags the marker.
 
12266         _onDragEnd: function (e) {
 
12267                 // @event dragend: DragEndEvent
 
12268                 // Fired when the user stops dragging the marker.
 
12270                 // @event moveend: Event
 
12271                 // Fired when the marker stops moving (because of dragging).
 
12272                 delete this._oldLatLng;
 
12275                     .fire('dragend', e);
 
12286  * L.Control is a base class for implementing map controls. Handles positioning.
 
12287  * All other controls extend from this class.
 
12290 L.Control = L.Class.extend({
 
12292         // @aka Control options
 
12294                 // @option position: String = 'topright'
 
12295                 // The position of the control (one of the map corners). Possible values are `'topleft'`,
 
12296                 // `'topright'`, `'bottomleft'` or `'bottomright'`
 
12297                 position: 'topright'
 
12300         initialize: function (options) {
 
12301                 L.setOptions(this, options);
 
12305          * Classes extending L.Control will inherit the following methods:
 
12307          * @method getPosition: string
 
12308          * Returns the position of the control.
 
12310         getPosition: function () {
 
12311                 return this.options.position;
 
12314         // @method setPosition(position: string): this
 
12315         // Sets the position of the control.
 
12316         setPosition: function (position) {
 
12317                 var map = this._map;
 
12320                         map.removeControl(this);
 
12323                 this.options.position = position;
 
12326                         map.addControl(this);
 
12332         // @method getContainer: HTMLElement
 
12333         // Returns the HTMLElement that contains the control.
 
12334         getContainer: function () {
 
12335                 return this._container;
 
12338         // @method addTo(map: Map): this
 
12339         // Adds the control to the given map.
 
12340         addTo: function (map) {
 
12344                 var container = this._container = this.onAdd(map),
 
12345                     pos = this.getPosition(),
 
12346                     corner = map._controlCorners[pos];
 
12348                 L.DomUtil.addClass(container, 'leaflet-control');
 
12350                 if (pos.indexOf('bottom') !== -1) {
 
12351                         corner.insertBefore(container, corner.firstChild);
 
12353                         corner.appendChild(container);
 
12359         // @method remove: this
 
12360         // Removes the control from the map it is currently active on.
 
12361         remove: function () {
 
12366                 L.DomUtil.remove(this._container);
 
12368                 if (this.onRemove) {
 
12369                         this.onRemove(this._map);
 
12377         _refocusOnMap: function (e) {
 
12378                 // if map exists and event is not a keyboard event
 
12379                 if (this._map && e && e.screenX > 0 && e.screenY > 0) {
 
12380                         this._map.getContainer().focus();
 
12385 L.control = function (options) {
 
12386         return new L.Control(options);
 
12389 /* @section Extension methods
 
12392  * Every control should extend from `L.Control` and (re-)implement the following methods.
 
12394  * @method onAdd(map: Map): HTMLElement
 
12395  * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
 
12397  * @method onRemove(map: Map)
 
12398  * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
 
12402  * @section Methods for Layers and Controls
 
12405         // @method addControl(control: Control): this
 
12406         // Adds the given control to the map
 
12407         addControl: function (control) {
 
12408                 control.addTo(this);
 
12412         // @method removeControl(control: Control): this
 
12413         // Removes the given control from the map
 
12414         removeControl: function (control) {
 
12419         _initControlPos: function () {
 
12420                 var corners = this._controlCorners = {},
 
12422                     container = this._controlContainer =
 
12423                             L.DomUtil.create('div', l + 'control-container', this._container);
 
12425                 function createCorner(vSide, hSide) {
 
12426                         var className = l + vSide + ' ' + l + hSide;
 
12428                         corners[vSide + hSide] = L.DomUtil.create('div', className, container);
 
12431                 createCorner('top', 'left');
 
12432                 createCorner('top', 'right');
 
12433                 createCorner('bottom', 'left');
 
12434                 createCorner('bottom', 'right');
 
12437         _clearControlPos: function () {
 
12438                 L.DomUtil.remove(this._controlContainer);
 
12445  * @class Control.Zoom
 
12446  * @aka L.Control.Zoom
 
12447  * @inherits Control
 
12449  * 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`.
 
12452 L.Control.Zoom = L.Control.extend({
 
12454         // @aka Control.Zoom options
 
12456                 position: 'topleft',
 
12458                 // @option zoomInText: String = '+'
 
12459                 // The text set on the 'zoom in' button.
 
12462                 // @option zoomInTitle: String = 'Zoom in'
 
12463                 // The title set on the 'zoom in' button.
 
12464                 zoomInTitle: 'Zoom in',
 
12466                 // @option zoomOutText: String = '-'
 
12467                 // The text set on the 'zoom out' button.
 
12470                 // @option zoomOutTitle: String = 'Zoom out'
 
12471                 // The title set on the 'zoom out' button.
 
12472                 zoomOutTitle: 'Zoom out'
 
12475         onAdd: function (map) {
 
12476                 var zoomName = 'leaflet-control-zoom',
 
12477                     container = L.DomUtil.create('div', zoomName + ' leaflet-bar'),
 
12478                     options = this.options;
 
12480                 this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
 
12481                         zoomName + '-in',  container, this._zoomIn);
 
12482                 this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
 
12483                         zoomName + '-out', container, this._zoomOut);
 
12485                 this._updateDisabled();
 
12486                 map.on('zoomend zoomlevelschange', this._updateDisabled, this);
 
12491         onRemove: function (map) {
 
12492                 map.off('zoomend zoomlevelschange', this._updateDisabled, this);
 
12495         disable: function () {
 
12496                 this._disabled = true;
 
12497                 this._updateDisabled();
 
12501         enable: function () {
 
12502                 this._disabled = false;
 
12503                 this._updateDisabled();
 
12507         _zoomIn: function (e) {
 
12508                 if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
 
12509                         this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
 
12513         _zoomOut: function (e) {
 
12514                 if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
 
12515                         this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
 
12519         _createButton: function (html, title, className, container, fn) {
 
12520                 var link = L.DomUtil.create('a', className, container);
 
12521                 link.innerHTML = html;
 
12523                 link.title = title;
 
12526                  * Will force screen readers like VoiceOver to read this as "Zoom in - button"
 
12528                 link.setAttribute('role', 'button');
 
12529                 link.setAttribute('aria-label', title);
 
12532                     .on(link, 'mousedown dblclick', L.DomEvent.stopPropagation)
 
12533                     .on(link, 'click', L.DomEvent.stop)
 
12534                     .on(link, 'click', fn, this)
 
12535                     .on(link, 'click', this._refocusOnMap, this);
 
12540         _updateDisabled: function () {
 
12541                 var map = this._map,
 
12542                     className = 'leaflet-disabled';
 
12544                 L.DomUtil.removeClass(this._zoomInButton, className);
 
12545                 L.DomUtil.removeClass(this._zoomOutButton, className);
 
12547                 if (this._disabled || map._zoom === map.getMinZoom()) {
 
12548                         L.DomUtil.addClass(this._zoomOutButton, className);
 
12550                 if (this._disabled || map._zoom === map.getMaxZoom()) {
 
12551                         L.DomUtil.addClass(this._zoomInButton, className);
 
12557 // @section Control options
 
12558 // @option zoomControl: Boolean = true
 
12559 // Whether a [zoom control](#control-zoom) is added to the map by default.
 
12560 L.Map.mergeOptions({
 
12564 L.Map.addInitHook(function () {
 
12565         if (this.options.zoomControl) {
 
12566                 this.zoomControl = new L.Control.Zoom();
 
12567                 this.addControl(this.zoomControl);
 
12571 // @namespace Control.Zoom
 
12572 // @factory L.control.zoom(options: Control.Zoom options)
 
12573 // Creates a zoom control
 
12574 L.control.zoom = function (options) {
 
12575         return new L.Control.Zoom(options);
 
12581  * @class Control.Attribution
 
12582  * @aka L.Control.Attribution
 
12583  * @inherits Control
 
12585  * 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.
 
12588 L.Control.Attribution = L.Control.extend({
 
12590         // @aka Control.Attribution options
 
12592                 position: 'bottomright',
 
12594                 // @option prefix: String = 'Leaflet'
 
12595                 // The HTML text shown before the attributions. Pass `false` to disable.
 
12596                 prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
 
12599         initialize: function (options) {
 
12600                 L.setOptions(this, options);
 
12602                 this._attributions = {};
 
12605         onAdd: function (map) {
 
12606                 map.attributionControl = this;
 
12607                 this._container = L.DomUtil.create('div', 'leaflet-control-attribution');
 
12609                         L.DomEvent.disableClickPropagation(this._container);
 
12612                 // TODO ugly, refactor
 
12613                 for (var i in map._layers) {
 
12614                         if (map._layers[i].getAttribution) {
 
12615                                 this.addAttribution(map._layers[i].getAttribution());
 
12621                 return this._container;
 
12624         // @method setPrefix(prefix: String): this
 
12625         // Sets the text before the attributions.
 
12626         setPrefix: function (prefix) {
 
12627                 this.options.prefix = prefix;
 
12632         // @method addAttribution(text: String): this
 
12633         // Adds an attribution text (e.g. `'Vector data © Mapbox'`).
 
12634         addAttribution: function (text) {
 
12635                 if (!text) { return this; }
 
12637                 if (!this._attributions[text]) {
 
12638                         this._attributions[text] = 0;
 
12640                 this._attributions[text]++;
 
12647         // @method removeAttribution(text: String): this
 
12648         // Removes an attribution text.
 
12649         removeAttribution: function (text) {
 
12650                 if (!text) { return this; }
 
12652                 if (this._attributions[text]) {
 
12653                         this._attributions[text]--;
 
12660         _update: function () {
 
12661                 if (!this._map) { return; }
 
12665                 for (var i in this._attributions) {
 
12666                         if (this._attributions[i]) {
 
12671                 var prefixAndAttribs = [];
 
12673                 if (this.options.prefix) {
 
12674                         prefixAndAttribs.push(this.options.prefix);
 
12676                 if (attribs.length) {
 
12677                         prefixAndAttribs.push(attribs.join(', '));
 
12680                 this._container.innerHTML = prefixAndAttribs.join(' | ');
 
12685 // @section Control options
 
12686 // @option attributionControl: Boolean = true
 
12687 // Whether a [attribution control](#control-attribution) is added to the map by default.
 
12688 L.Map.mergeOptions({
 
12689         attributionControl: true
 
12692 L.Map.addInitHook(function () {
 
12693         if (this.options.attributionControl) {
 
12694                 new L.Control.Attribution().addTo(this);
 
12698 // @namespace Control.Attribution
 
12699 // @factory L.control.attribution(options: Control.Attribution options)
 
12700 // Creates an attribution control.
 
12701 L.control.attribution = function (options) {
 
12702         return new L.Control.Attribution(options);
 
12708  * @class Control.Scale
 
12709  * @aka L.Control.Scale
 
12710  * @inherits Control
 
12712  * 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`.
 
12717  * L.control.scale().addTo(map);
 
12721 L.Control.Scale = L.Control.extend({
 
12723         // @aka Control.Scale options
 
12725                 position: 'bottomleft',
 
12727                 // @option maxWidth: Number = 100
 
12728                 // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
 
12731                 // @option metric: Boolean = True
 
12732                 // Whether to show the metric scale line (m/km).
 
12735                 // @option imperial: Boolean = True
 
12736                 // Whether to show the imperial scale line (mi/ft).
 
12739                 // @option updateWhenIdle: Boolean = false
 
12740                 // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
 
12743         onAdd: function (map) {
 
12744                 var className = 'leaflet-control-scale',
 
12745                     container = L.DomUtil.create('div', className),
 
12746                     options = this.options;
 
12748                 this._addScales(options, className + '-line', container);
 
12750                 map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
12751                 map.whenReady(this._update, this);
 
12756         onRemove: function (map) {
 
12757                 map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
12760         _addScales: function (options, className, container) {
 
12761                 if (options.metric) {
 
12762                         this._mScale = L.DomUtil.create('div', className, container);
 
12764                 if (options.imperial) {
 
12765                         this._iScale = L.DomUtil.create('div', className, container);
 
12769         _update: function () {
 
12770                 var map = this._map,
 
12771                     y = map.getSize().y / 2;
 
12773                 var maxMeters = map.distance(
 
12774                                 map.containerPointToLatLng([0, y]),
 
12775                                 map.containerPointToLatLng([this.options.maxWidth, y]));
 
12777                 this._updateScales(maxMeters);
 
12780         _updateScales: function (maxMeters) {
 
12781                 if (this.options.metric && maxMeters) {
 
12782                         this._updateMetric(maxMeters);
 
12784                 if (this.options.imperial && maxMeters) {
 
12785                         this._updateImperial(maxMeters);
 
12789         _updateMetric: function (maxMeters) {
 
12790                 var meters = this._getRoundNum(maxMeters),
 
12791                     label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
 
12793                 this._updateScale(this._mScale, label, meters / maxMeters);
 
12796         _updateImperial: function (maxMeters) {
 
12797                 var maxFeet = maxMeters * 3.2808399,
 
12798                     maxMiles, miles, feet;
 
12800                 if (maxFeet > 5280) {
 
12801                         maxMiles = maxFeet / 5280;
 
12802                         miles = this._getRoundNum(maxMiles);
 
12803                         this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
 
12806                         feet = this._getRoundNum(maxFeet);
 
12807                         this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
 
12811         _updateScale: function (scale, text, ratio) {
 
12812                 scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
 
12813                 scale.innerHTML = text;
 
12816         _getRoundNum: function (num) {
 
12817                 var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
 
12830 // @factory L.control.scale(options?: Control.Scale options)
 
12831 // Creates an scale control with the given options.
 
12832 L.control.scale = function (options) {
 
12833         return new L.Control.Scale(options);
 
12839  * @class Control.Layers
 
12840  * @aka L.Control.Layers
 
12841  * @inherits Control
 
12843  * 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.html)). Extends `Control`.
 
12848  * var baseLayers = {
 
12849  *      "Mapbox": mapbox,
 
12850  *      "OpenStreetMap": osm
 
12854  *      "Marker": marker,
 
12855  *      "Roads": roadsLayer
 
12858  * L.control.layers(baseLayers, overlays).addTo(map);
 
12861  * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
 
12865  *     "<someName1>": layer1,
 
12866  *     "<someName2>": layer2
 
12870  * The layer names can contain HTML, which allows you to add additional styling to the items:
 
12873  * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
 
12878 L.Control.Layers = L.Control.extend({
 
12880         // @aka Control.Layers options
 
12882                 // @option collapsed: Boolean = true
 
12883                 // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
 
12885                 position: 'topright',
 
12887                 // @option autoZIndex: Boolean = true
 
12888                 // 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.
 
12891                 // @option hideSingleBase: Boolean = false
 
12892                 // If `true`, the base layers in the control will be hidden when there is only one.
 
12893                 hideSingleBase: false,
 
12895                 // @option sortLayers: Boolean = false
 
12896                 // Whether to sort the layers. When `false`, layers will keep the order
 
12897                 // in which they were added to the control.
 
12900                 // @option sortFunction: Function = *
 
12901                 // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
 
12902                 // that will be used for sorting the layers, when `sortLayers` is `true`.
 
12903                 // The function receives both the `L.Layer` instances and their names, as in
 
12904                 // `sortFunction(layerA, layerB, nameA, nameB)`.
 
12905                 // By default, it sorts layers alphabetically by their name.
 
12906                 sortFunction: function (layerA, layerB, nameA, nameB) {
 
12907                         return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
 
12911         initialize: function (baseLayers, overlays, options) {
 
12912                 L.setOptions(this, options);
 
12915                 this._lastZIndex = 0;
 
12916                 this._handlingClick = false;
 
12918                 for (var i in baseLayers) {
 
12919                         this._addLayer(baseLayers[i], i);
 
12922                 for (i in overlays) {
 
12923                         this._addLayer(overlays[i], i, true);
 
12927         onAdd: function (map) {
 
12928                 this._initLayout();
 
12932                 map.on('zoomend', this._checkDisabledLayers, this);
 
12934                 return this._container;
 
12937         onRemove: function () {
 
12938                 this._map.off('zoomend', this._checkDisabledLayers, this);
 
12940                 for (var i = 0; i < this._layers.length; i++) {
 
12941                         this._layers[i].layer.off('add remove', this._onLayerChange, this);
 
12945         // @method addBaseLayer(layer: Layer, name: String): this
 
12946         // Adds a base layer (radio button entry) with the given name to the control.
 
12947         addBaseLayer: function (layer, name) {
 
12948                 this._addLayer(layer, name);
 
12949                 return (this._map) ? this._update() : this;
 
12952         // @method addOverlay(layer: Layer, name: String): this
 
12953         // Adds an overlay (checkbox entry) with the given name to the control.
 
12954         addOverlay: function (layer, name) {
 
12955                 this._addLayer(layer, name, true);
 
12956                 return (this._map) ? this._update() : this;
 
12959         // @method removeLayer(layer: Layer): this
 
12960         // Remove the given layer from the control.
 
12961         removeLayer: function (layer) {
 
12962                 layer.off('add remove', this._onLayerChange, this);
 
12964                 var obj = this._getLayer(L.stamp(layer));
 
12966                         this._layers.splice(this._layers.indexOf(obj), 1);
 
12968                 return (this._map) ? this._update() : this;
 
12971         // @method expand(): this
 
12972         // Expand the control container if collapsed.
 
12973         expand: function () {
 
12974                 L.DomUtil.addClass(this._container, 'leaflet-control-layers-expanded');
 
12975                 this._form.style.height = null;
 
12976                 var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
 
12977                 if (acceptableHeight < this._form.clientHeight) {
 
12978                         L.DomUtil.addClass(this._form, 'leaflet-control-layers-scrollbar');
 
12979                         this._form.style.height = acceptableHeight + 'px';
 
12981                         L.DomUtil.removeClass(this._form, 'leaflet-control-layers-scrollbar');
 
12983                 this._checkDisabledLayers();
 
12987         // @method collapse(): this
 
12988         // Collapse the control container if expanded.
 
12989         collapse: function () {
 
12990                 L.DomUtil.removeClass(this._container, 'leaflet-control-layers-expanded');
 
12994         _initLayout: function () {
 
12995                 var className = 'leaflet-control-layers',
 
12996                     container = this._container = L.DomUtil.create('div', className),
 
12997                     collapsed = this.options.collapsed;
 
12999                 // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
 
13000                 container.setAttribute('aria-haspopup', true);
 
13002                 L.DomEvent.disableClickPropagation(container);
 
13003                 if (!L.Browser.touch) {
 
13004                         L.DomEvent.disableScrollPropagation(container);
 
13007                 var form = this._form = L.DomUtil.create('form', className + '-list');
 
13010                         this._map.on('click', this.collapse, this);
 
13012                         if (!L.Browser.android) {
 
13013                                 L.DomEvent.on(container, {
 
13014                                         mouseenter: this.expand,
 
13015                                         mouseleave: this.collapse
 
13020                 var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container);
 
13022                 link.title = 'Layers';
 
13024                 if (L.Browser.touch) {
 
13026                             .on(link, 'click', L.DomEvent.stop)
 
13027                             .on(link, 'click', this.expand, this);
 
13029                         L.DomEvent.on(link, 'focus', this.expand, this);
 
13032                 // work around for Firefox Android issue https://github.com/Leaflet/Leaflet/issues/2033
 
13033                 L.DomEvent.on(form, 'click', function () {
 
13034                         setTimeout(L.bind(this._onInputClick, this), 0);
 
13037                 // TODO keyboard accessibility
 
13043                 this._baseLayersList = L.DomUtil.create('div', className + '-base', form);
 
13044                 this._separator = L.DomUtil.create('div', className + '-separator', form);
 
13045                 this._overlaysList = L.DomUtil.create('div', className + '-overlays', form);
 
13047                 container.appendChild(form);
 
13050         _getLayer: function (id) {
 
13051                 for (var i = 0; i < this._layers.length; i++) {
 
13053                         if (this._layers[i] && L.stamp(this._layers[i].layer) === id) {
 
13054                                 return this._layers[i];
 
13059         _addLayer: function (layer, name, overlay) {
 
13060                 layer.on('add remove', this._onLayerChange, this);
 
13062                 this._layers.push({
 
13068                 if (this.options.sortLayers) {
 
13069                         this._layers.sort(L.bind(function (a, b) {
 
13070                                 return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
 
13074                 if (this.options.autoZIndex && layer.setZIndex) {
 
13075                         this._lastZIndex++;
 
13076                         layer.setZIndex(this._lastZIndex);
 
13080         _update: function () {
 
13081                 if (!this._container) { return this; }
 
13083                 L.DomUtil.empty(this._baseLayersList);
 
13084                 L.DomUtil.empty(this._overlaysList);
 
13086                 var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
 
13088                 for (i = 0; i < this._layers.length; i++) {
 
13089                         obj = this._layers[i];
 
13090                         this._addItem(obj);
 
13091                         overlaysPresent = overlaysPresent || obj.overlay;
 
13092                         baseLayersPresent = baseLayersPresent || !obj.overlay;
 
13093                         baseLayersCount += !obj.overlay ? 1 : 0;
 
13096                 // Hide base layers section if there's only one layer.
 
13097                 if (this.options.hideSingleBase) {
 
13098                         baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
 
13099                         this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
 
13102                 this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
 
13107         _onLayerChange: function (e) {
 
13108                 if (!this._handlingClick) {
 
13112                 var obj = this._getLayer(L.stamp(e.target));
 
13115                 // @section Layer events
 
13116                 // @event baselayerchange: LayersControlEvent
 
13117                 // Fired when the base layer is changed through the [layer control](#control-layers).
 
13118                 // @event overlayadd: LayersControlEvent
 
13119                 // Fired when an overlay is selected through the [layer control](#control-layers).
 
13120                 // @event overlayremove: LayersControlEvent
 
13121                 // Fired when an overlay is deselected through the [layer control](#control-layers).
 
13122                 // @namespace Control.Layers
 
13123                 var type = obj.overlay ?
 
13124                         (e.type === 'add' ? 'overlayadd' : 'overlayremove') :
 
13125                         (e.type === 'add' ? 'baselayerchange' : null);
 
13128                         this._map.fire(type, obj);
 
13132         // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
 
13133         _createRadioElement: function (name, checked) {
 
13135                 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
 
13136                                 name + '"' + (checked ? ' checked="checked"' : '') + '/>';
 
13138                 var radioFragment = document.createElement('div');
 
13139                 radioFragment.innerHTML = radioHtml;
 
13141                 return radioFragment.firstChild;
 
13144         _addItem: function (obj) {
 
13145                 var label = document.createElement('label'),
 
13146                     checked = this._map.hasLayer(obj.layer),
 
13150                         input = document.createElement('input');
 
13151                         input.type = 'checkbox';
 
13152                         input.className = 'leaflet-control-layers-selector';
 
13153                         input.defaultChecked = checked;
 
13155                         input = this._createRadioElement('leaflet-base-layers', checked);
 
13158                 input.layerId = L.stamp(obj.layer);
 
13160                 L.DomEvent.on(input, 'click', this._onInputClick, this);
 
13162                 var name = document.createElement('span');
 
13163                 name.innerHTML = ' ' + obj.name;
 
13165                 // Helps from preventing layer control flicker when checkboxes are disabled
 
13166                 // https://github.com/Leaflet/Leaflet/issues/2771
 
13167                 var holder = document.createElement('div');
 
13169                 label.appendChild(holder);
 
13170                 holder.appendChild(input);
 
13171                 holder.appendChild(name);
 
13173                 var container = obj.overlay ? this._overlaysList : this._baseLayersList;
 
13174                 container.appendChild(label);
 
13176                 this._checkDisabledLayers();
 
13180         _onInputClick: function () {
 
13181                 var inputs = this._form.getElementsByTagName('input'),
 
13182                     input, layer, hasLayer;
 
13183                 var addedLayers = [],
 
13184                     removedLayers = [];
 
13186                 this._handlingClick = true;
 
13188                 for (var i = inputs.length - 1; i >= 0; i--) {
 
13190                         layer = this._getLayer(input.layerId).layer;
 
13191                         hasLayer = this._map.hasLayer(layer);
 
13193                         if (input.checked && !hasLayer) {
 
13194                                 addedLayers.push(layer);
 
13196                         } else if (!input.checked && hasLayer) {
 
13197                                 removedLayers.push(layer);
 
13201                 // Bugfix issue 2318: Should remove all old layers before readding new ones
 
13202                 for (i = 0; i < removedLayers.length; i++) {
 
13203                         this._map.removeLayer(removedLayers[i]);
 
13205                 for (i = 0; i < addedLayers.length; i++) {
 
13206                         this._map.addLayer(addedLayers[i]);
 
13209                 this._handlingClick = false;
 
13211                 this._refocusOnMap();
 
13214         _checkDisabledLayers: function () {
 
13215                 var inputs = this._form.getElementsByTagName('input'),
 
13218                     zoom = this._map.getZoom();
 
13220                 for (var i = inputs.length - 1; i >= 0; i--) {
 
13222                         layer = this._getLayer(input.layerId).layer;
 
13223                         input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
 
13224                                          (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
 
13229         _expand: function () {
 
13230                 // Backward compatibility, remove me in 1.1.
 
13231                 return this.expand();
 
13234         _collapse: function () {
 
13235                 // Backward compatibility, remove me in 1.1.
 
13236                 return this.collapse();
 
13242 // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
 
13243 // 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.
 
13244 L.control.layers = function (baseLayers, overlays, options) {
 
13245         return new L.Control.Layers(baseLayers, overlays, options);
 
13250 }(window, document));
 
13251 //# sourceMappingURL=leaflet-src.map