2  Leaflet 1.0.0, 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);
 
 519                 typeListeners.count++;
 
 522         _off: function (type, fn, context) {
 
 527                 if (!this._events) { return; }
 
 529                 listeners = this._events[type];
 
 536                         // Set all removed listeners to noop so they are not called if remove happens in fire
 
 537                         for (i = 0, len = listeners.length; i < len; i++) {
 
 538                                 listeners[i].fn = L.Util.falseFn;
 
 540                         // clear all listeners for a type if function isn't specified
 
 541                         delete this._events[type];
 
 545                 if (context === this) {
 
 551                         // find fn and remove it
 
 552                         for (i = 0, len = listeners.length; i < len; i++) {
 
 553                                 var l = listeners[i];
 
 554                                 if (l.ctx !== context) { continue; }
 
 557                                         // set the removed listener to noop so that's not called if remove happens in fire
 
 558                                         l.fn = L.Util.falseFn;
 
 560                                         if (this._firingCount) {
 
 561                                                 /* copy array in case events are being fired */
 
 562                                                 this._events[type] = listeners = listeners.slice();
 
 564                                         listeners.splice(i, 1);
 
 572         // @method fire(type: String, data?: Object, propagate?: Boolean): this
 
 573         // Fires an event of the specified type. You can optionally provide an data
 
 574         // object — the first argument of the listener function will contain its
 
 575         // properties. The event might can optionally be propagated to event parents.
 
 576         fire: function (type, data, propagate) {
 
 577                 if (!this.listens(type, propagate)) { return this; }
 
 579                 var event = L.Util.extend({}, data, {type: type, target: this});
 
 582                         var listeners = this._events[type];
 
 585                                 this._firingCount = (this._firingCount + 1) || 1;
 
 586                                 for (var i = 0, len = listeners.length; i < len; i++) {
 
 587                                         var l = listeners[i];
 
 588                                         l.fn.call(l.ctx || this, event);
 
 596                         // propagate the event to parents (set with addEventParent)
 
 597                         this._propagateEvent(event);
 
 603         // @method listens(type: String): Boolean
 
 604         // Returns `true` if a particular event type has any listeners attached to it.
 
 605         listens: function (type, propagate) {
 
 606                 var listeners = this._events && this._events[type];
 
 607                 if (listeners && listeners.length) { return true; }
 
 610                         // also check parents for listeners if event propagates
 
 611                         for (var id in this._eventParents) {
 
 612                                 if (this._eventParents[id].listens(type, propagate)) { return true; }
 
 618         // @method once(…): this
 
 619         // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
 
 620         once: function (types, fn, context) {
 
 622                 if (typeof types === 'object') {
 
 623                         for (var type in types) {
 
 624                                 this.once(type, types[type], fn);
 
 629                 var handler = L.bind(function () {
 
 631                             .off(types, fn, context)
 
 632                             .off(types, handler, context);
 
 635                 // add a listener that's executed once and removed after that
 
 637                     .on(types, fn, context)
 
 638                     .on(types, handler, context);
 
 641         // @method addEventParent(obj: Evented): this
 
 642         // Adds an event parent - an `Evented` that will receive propagated events
 
 643         addEventParent: function (obj) {
 
 644                 this._eventParents = this._eventParents || {};
 
 645                 this._eventParents[L.stamp(obj)] = obj;
 
 649         // @method removeEventParent(obj: Evented): this
 
 650         // Removes an event parent, so it will stop receiving propagated events
 
 651         removeEventParent: function (obj) {
 
 652                 if (this._eventParents) {
 
 653                         delete this._eventParents[L.stamp(obj)];
 
 658         _propagateEvent: function (e) {
 
 659                 for (var id in this._eventParents) {
 
 660                         this._eventParents[id].fire(e.type, L.extend({layer: e.target}, e), true);
 
 665 var proto = L.Evented.prototype;
 
 667 // aliases; we should ditch those eventually
 
 669 // @method addEventListener(…): this
 
 670 // Alias to [`on(…)`](#evented-on)
 
 671 proto.addEventListener = proto.on;
 
 673 // @method removeEventListener(…): this
 
 674 // Alias to [`off(…)`](#evented-off)
 
 676 // @method clearAllEventListeners(…): this
 
 677 // Alias to [`off()`](#evented-off)
 
 678 proto.removeEventListener = proto.clearAllEventListeners = proto.off;
 
 680 // @method addOneTimeEventListener(…): this
 
 681 // Alias to [`once(…)`](#evented-once)
 
 682 proto.addOneTimeEventListener = proto.once;
 
 684 // @method fireEvent(…): this
 
 685 // Alias to [`fire(…)`](#evented-fire)
 
 686 proto.fireEvent = proto.fire;
 
 688 // @method hasEventListeners(…): Boolean
 
 689 // Alias to [`listens(…)`](#evented-listens)
 
 690 proto.hasEventListeners = proto.listens;
 
 692 L.Mixin = {Events: proto};
 
 700  * A namespace with static properties for browser/feature detection used by Leaflet internally.
 
 705  * if (L.Browser.ielt9) {
 
 706  *   alert('Upgrade your browser, dude!');
 
 713         var ua = navigator.userAgent.toLowerCase(),
 
 714             doc = document.documentElement,
 
 716             ie = 'ActiveXObject' in window,
 
 718             webkit    = ua.indexOf('webkit') !== -1,
 
 719             phantomjs = ua.indexOf('phantom') !== -1,
 
 720             android23 = ua.search('android [23]') !== -1,
 
 721             chrome    = ua.indexOf('chrome') !== -1,
 
 722             gecko     = ua.indexOf('gecko') !== -1  && !webkit && !window.opera && !ie,
 
 724             win = navigator.platform.indexOf('Win') === 0,
 
 726             mobile = typeof orientation !== 'undefined' || ua.indexOf('mobile') !== -1,
 
 727             msPointer = !window.PointerEvent && window.MSPointerEvent,
 
 728             pointer = window.PointerEvent || msPointer,
 
 730             ie3d = ie && ('transition' in doc.style),
 
 731             webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23,
 
 732             gecko3d = 'MozPerspective' in doc.style,
 
 733             opera12 = 'OTransition' in doc.style;
 
 736         var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
 
 737                         (window.DocumentTouch && document instanceof window.DocumentTouch));
 
 741                 // @property ie: Boolean
 
 742                 // `true` for all Internet Explorer versions (not Edge).
 
 745                 // @property ielt9: Boolean
 
 746                 // `true` for Internet Explorer versions less than 9.
 
 747                 ielt9: ie && !document.addEventListener,
 
 749                 // @property edge: Boolean
 
 750                 // `true` for the Edge web browser.
 
 751                 edge: 'msLaunchUri' in navigator && !('documentMode' in document),
 
 753                 // @property webkit: Boolean
 
 754                 // `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
 
 757                 // @property gecko: Boolean
 
 758                 // `true` for gecko-based browsers like Firefox.
 
 761                 // @property android: Boolean
 
 762                 // `true` for any browser running on an Android platform.
 
 763                 android: ua.indexOf('android') !== -1,
 
 765                 // @property android23: Boolean
 
 766                 // `true` for browsers running on Android 2 or Android 3.
 
 767                 android23: android23,
 
 769                 // @property chrome: Boolean
 
 770                 // `true` for the Chrome browser.
 
 773                 // @property safari: Boolean
 
 774                 // `true` for the Safari browser.
 
 775                 safari: !chrome && ua.indexOf('safari') !== -1,
 
 778                 // @property win: Boolean
 
 779                 // `true` when the browser is running in a Windows platform
 
 783                 // @property ie3d: Boolean
 
 784                 // `true` for all Internet Explorer versions supporting CSS transforms.
 
 787                 // @property webkit3d: Boolean
 
 788                 // `true` for webkit-based browsers supporting CSS transforms.
 
 791                 // @property gecko3d: Boolean
 
 792                 // `true` for gecko-based browsers supporting CSS transforms.
 
 795                 // @property opera12: Boolean
 
 796                 // `true` for the Opera browser supporting CSS transforms (version 12 or later).
 
 799                 // @property any3d: Boolean
 
 800                 // `true` for all browsers supporting CSS transforms.
 
 801                 any3d: !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantomjs,
 
 804                 // @property mobile: Boolean
 
 805                 // `true` for all browsers running in a mobile device.
 
 808                 // @property mobileWebkit: Boolean
 
 809                 // `true` for all webkit-based browsers in a mobile device.
 
 810                 mobileWebkit: mobile && webkit,
 
 812                 // @property mobileWebkit3d: Boolean
 
 813                 // `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
 
 814                 mobileWebkit3d: mobile && webkit3d,
 
 816                 // @property mobileOpera: Boolean
 
 817                 // `true` for the Opera browser in a mobile device.
 
 818                 mobileOpera: mobile && window.opera,
 
 820                 // @property mobileGecko: Boolean
 
 821                 // `true` for gecko-based browsers running in a mobile device.
 
 822                 mobileGecko: mobile && gecko,
 
 825                 // @property touch: Boolean
 
 826                 // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
 
 829                 // @property msPointer: Boolean
 
 830                 // `true` for browsers implementing the Microsoft touch events model (notably IE10).
 
 831                 msPointer: !!msPointer,
 
 833                 // @property pointer: Boolean
 
 834                 // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
 
 838                 // @property retina: Boolean
 
 839                 // `true` for browsers on a high-resolution "retina" screen.
 
 840                 retina: (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1
 
 851  * Represents a point with `x` and `y` coordinates in pixels.
 
 856  * var point = L.point(200, 300);
 
 859  * 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:
 
 862  * map.panBy([200, 300]);
 
 863  * map.panBy(L.point(200, 300));
 
 867 L.Point = function (x, y, round) {
 
 868         this.x = (round ? Math.round(x) : x);
 
 869         this.y = (round ? Math.round(y) : y);
 
 872 L.Point.prototype = {
 
 874         // @method clone(): Point
 
 875         // Returns a copy of the current point.
 
 877                 return new L.Point(this.x, this.y);
 
 880         // @method add(otherPoint: Point): Point
 
 881         // Returns the result of addition of the current and the given points.
 
 882         add: function (point) {
 
 883                 // non-destructive, returns a new point
 
 884                 return this.clone()._add(L.point(point));
 
 887         _add: function (point) {
 
 888                 // destructive, used directly for performance in situations where it's safe to modify existing point
 
 894         // @method subtract(otherPoint: Point): Point
 
 895         // Returns the result of subtraction of the given point from the current.
 
 896         subtract: function (point) {
 
 897                 return this.clone()._subtract(L.point(point));
 
 900         _subtract: function (point) {
 
 906         // @method divideBy(num: Number): Point
 
 907         // Returns the result of division of the current point by the given number.
 
 908         divideBy: function (num) {
 
 909                 return this.clone()._divideBy(num);
 
 912         _divideBy: function (num) {
 
 918         // @method multiplyBy(num: Number): Point
 
 919         // Returns the result of multiplication of the current point by the given number.
 
 920         multiplyBy: function (num) {
 
 921                 return this.clone()._multiplyBy(num);
 
 924         _multiplyBy: function (num) {
 
 930         // @method scaleBy(scale: Point): Point
 
 931         // Multiply each coordinate of the current point by each coordinate of
 
 932         // `scale`. In linear algebra terms, multiply the point by the
 
 933         // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
 
 934         // defined by `scale`.
 
 935         scaleBy: function (point) {
 
 936                 return new L.Point(this.x * point.x, this.y * point.y);
 
 939         // @method unscaleBy(scale: Point): Point
 
 940         // Inverse of `scaleBy`. Divide each coordinate of the current point by
 
 941         // each coordinate of `scale`.
 
 942         unscaleBy: function (point) {
 
 943                 return new L.Point(this.x / point.x, this.y / point.y);
 
 946         // @method round(): Point
 
 947         // Returns a copy of the current point with rounded coordinates.
 
 949                 return this.clone()._round();
 
 952         _round: function () {
 
 953                 this.x = Math.round(this.x);
 
 954                 this.y = Math.round(this.y);
 
 958         // @method floor(): Point
 
 959         // Returns a copy of the current point with floored coordinates (rounded down).
 
 961                 return this.clone()._floor();
 
 964         _floor: function () {
 
 965                 this.x = Math.floor(this.x);
 
 966                 this.y = Math.floor(this.y);
 
 970         // @method ceil(): Point
 
 971         // Returns a copy of the current point with ceiled coordinates (rounded up).
 
 973                 return this.clone()._ceil();
 
 977                 this.x = Math.ceil(this.x);
 
 978                 this.y = Math.ceil(this.y);
 
 982         // @method distanceTo(otherPoint: Point): Number
 
 983         // Returns the cartesian distance between the current and the given points.
 
 984         distanceTo: function (point) {
 
 985                 point = L.point(point);
 
 987                 var x = point.x - this.x,
 
 988                     y = point.y - this.y;
 
 990                 return Math.sqrt(x * x + y * y);
 
 993         // @method equals(otherPoint: Point): Boolean
 
 994         // Returns `true` if the given point has the same coordinates.
 
 995         equals: function (point) {
 
 996                 point = L.point(point);
 
 998                 return point.x === this.x &&
 
1002         // @method contains(otherPoint: Point): Boolean
 
1003         // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
 
1004         contains: function (point) {
 
1005                 point = L.point(point);
 
1007                 return Math.abs(point.x) <= Math.abs(this.x) &&
 
1008                        Math.abs(point.y) <= Math.abs(this.y);
 
1011         // @method toString(): String
 
1012         // Returns a string representation of the point for debugging purposes.
 
1013         toString: function () {
 
1015                         L.Util.formatNum(this.x) + ', ' +
 
1016                         L.Util.formatNum(this.y) + ')';
 
1020 // @factory L.point(x: Number, y: Number, round?: Boolean)
 
1021 // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
 
1024 // @factory L.point(coords: Number[])
 
1025 // Expects an array of the form `[x, y]` instead.
 
1028 // @factory L.point(coords: Object)
 
1029 // Expects a plain object of the form `{x: Number, y: Number}` instead.
 
1030 L.point = function (x, y, round) {
 
1031         if (x instanceof L.Point) {
 
1034         if (L.Util.isArray(x)) {
 
1035                 return new L.Point(x[0], x[1]);
 
1037         if (x === undefined || x === null) {
 
1040         if (typeof x === 'object' && 'x' in x && 'y' in x) {
 
1041                 return new L.Point(x.x, x.y);
 
1043         return new L.Point(x, y, round);
 
1052  * Represents a rectangular area in pixel coordinates.
 
1057  * var p1 = L.point(10, 10),
 
1058  * p2 = L.point(40, 60),
 
1059  * bounds = L.bounds(p1, p2);
 
1062  * 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:
 
1065  * otherBounds.intersects([[10, 10], [40, 60]]);
 
1069 L.Bounds = function (a, b) {
 
1072         var points = b ? [a, b] : a;
 
1074         for (var i = 0, len = points.length; i < len; i++) {
 
1075                 this.extend(points[i]);
 
1079 L.Bounds.prototype = {
 
1080         // @method extend(point: Point): this
 
1081         // Extends the bounds to contain the given point.
 
1082         extend: function (point) { // (Point)
 
1083                 point = L.point(point);
 
1085                 // @property min: Point
 
1086                 // The top left corner of the rectangle.
 
1087                 // @property max: Point
 
1088                 // The bottom right corner of the rectangle.
 
1089                 if (!this.min && !this.max) {
 
1090                         this.min = point.clone();
 
1091                         this.max = point.clone();
 
1093                         this.min.x = Math.min(point.x, this.min.x);
 
1094                         this.max.x = Math.max(point.x, this.max.x);
 
1095                         this.min.y = Math.min(point.y, this.min.y);
 
1096                         this.max.y = Math.max(point.y, this.max.y);
 
1101         // @method getCenter(round?: Boolean): Point
 
1102         // Returns the center point of the bounds.
 
1103         getCenter: function (round) {
 
1105                         (this.min.x + this.max.x) / 2,
 
1106                         (this.min.y + this.max.y) / 2, round);
 
1109         // @method getBottomLeft(): Point
 
1110         // Returns the bottom-left point of the bounds.
 
1111         getBottomLeft: function () {
 
1112                 return new L.Point(this.min.x, this.max.y);
 
1115         // @method getTopRight(): Point
 
1116         // Returns the top-right point of the bounds.
 
1117         getTopRight: function () { // -> Point
 
1118                 return new L.Point(this.max.x, this.min.y);
 
1121         // @method getSize(): Point
 
1122         // Returns the size of the given bounds
 
1123         getSize: function () {
 
1124                 return this.max.subtract(this.min);
 
1127         // @method contains(otherBounds: Bounds): Boolean
 
1128         // Returns `true` if the rectangle contains the given one.
 
1130         // @method contains(point: Point): Boolean
 
1131         // Returns `true` if the rectangle contains the given point.
 
1132         contains: function (obj) {
 
1135                 if (typeof obj[0] === 'number' || obj instanceof L.Point) {
 
1138                         obj = L.bounds(obj);
 
1141                 if (obj instanceof L.Bounds) {
 
1148                 return (min.x >= this.min.x) &&
 
1149                        (max.x <= this.max.x) &&
 
1150                        (min.y >= this.min.y) &&
 
1151                        (max.y <= this.max.y);
 
1154         // @method intersects(otherBounds: Bounds): Boolean
 
1155         // Returns `true` if the rectangle intersects the given bounds. Two bounds
 
1156         // intersect if they have at least one point in common.
 
1157         intersects: function (bounds) { // (Bounds) -> Boolean
 
1158                 bounds = L.bounds(bounds);
 
1164                     xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
 
1165                     yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
 
1167                 return xIntersects && yIntersects;
 
1170         // @method overlaps(otherBounds: Bounds): Boolean
 
1171         // Returns `true` if the rectangle overlaps the given bounds. Two bounds
 
1172         // overlap if their intersection is an area.
 
1173         overlaps: function (bounds) { // (Bounds) -> Boolean
 
1174                 bounds = L.bounds(bounds);
 
1180                     xOverlaps = (max2.x > min.x) && (min2.x < max.x),
 
1181                     yOverlaps = (max2.y > min.y) && (min2.y < max.y);
 
1183                 return xOverlaps && yOverlaps;
 
1186         isValid: function () {
 
1187                 return !!(this.min && this.max);
 
1192 // @factory L.bounds(topLeft: Point, bottomRight: Point)
 
1193 // Creates a Bounds object from two coordinates (usually top-left and bottom-right corners).
 
1195 // @factory L.bounds(points: Point[])
 
1196 // Creates a Bounds object from the points it contains
 
1197 L.bounds = function (a, b) {
 
1198         if (!a || a instanceof L.Bounds) {
 
1201         return new L.Bounds(a, b);
 
1207  * @class Transformation
 
1208  * @aka L.Transformation
 
1210  * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
 
1211  * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
 
1212  * the reverse. Used by Leaflet in its projections code.
 
1217  * var transformation = new L.Transformation(2, 5, -1, 10),
 
1218  *      p = L.point(1, 2),
 
1219  *      p2 = transformation.transform(p), //  L.point(7, 8)
 
1220  *      p3 = transformation.untransform(p2); //  L.point(1, 2)
 
1225 // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
 
1226 // Creates a `Transformation` object with the given coefficients.
 
1227 L.Transformation = function (a, b, c, d) {
 
1234 L.Transformation.prototype = {
 
1235         // @method transform(point: Point, scale?: Number): Point
 
1236         // Returns a transformed point, optionally multiplied by the given scale.
 
1237         // Only accepts real `L.Point` instances, not arrays.
 
1238         transform: function (point, scale) { // (Point, Number) -> Point
 
1239                 return this._transform(point.clone(), scale);
 
1242         // destructive transform (faster)
 
1243         _transform: function (point, scale) {
 
1245                 point.x = scale * (this._a * point.x + this._b);
 
1246                 point.y = scale * (this._c * point.y + this._d);
 
1250         // @method untransform(point: Point, scale?: Number): Point
 
1251         // Returns the reverse transformation of the given point, optionally divided
 
1252         // by the given scale. Only accepts real `L.Point` instances, not arrays.
 
1253         untransform: function (point, scale) {
 
1256                         (point.x / scale - this._b) / this._a,
 
1257                         (point.y / scale - this._d) / this._c);
 
1264  * @namespace DomUtil
 
1266  * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
 
1267  * tree, used by Leaflet internally.
 
1269  * Most functions expecting or returning a `HTMLElement` also work for
 
1270  * SVG elements. The only difference is that classes refer to CSS classes
 
1271  * in HTML and SVG classes in SVG.
 
1276         // @function get(id: String|HTMLElement): HTMLElement
 
1277         // Returns an element given its DOM id, or returns the element itself
 
1278         // if it was passed directly.
 
1279         get: function (id) {
 
1280                 return typeof id === 'string' ? document.getElementById(id) : id;
 
1283         // @function getStyle(el: HTMLElement, styleAttrib: String): String
 
1284         // Returns the value for a certain style attribute on an element,
 
1285         // including computed values or values set through CSS.
 
1286         getStyle: function (el, style) {
 
1288                 var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
 
1290                 if ((!value || value === 'auto') && document.defaultView) {
 
1291                         var css = document.defaultView.getComputedStyle(el, null);
 
1292                         value = css ? css[style] : null;
 
1295                 return value === 'auto' ? null : value;
 
1298         // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
 
1299         // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
 
1300         create: function (tagName, className, container) {
 
1302                 var el = document.createElement(tagName);
 
1303                 el.className = className || '';
 
1306                         container.appendChild(el);
 
1312         // @function remove(el: HTMLElement)
 
1313         // Removes `el` from its parent element
 
1314         remove: function (el) {
 
1315                 var parent = el.parentNode;
 
1317                         parent.removeChild(el);
 
1321         // @function empty(el: HTMLElement)
 
1322         // Removes all of `el`'s children elements from `el`
 
1323         empty: function (el) {
 
1324                 while (el.firstChild) {
 
1325                         el.removeChild(el.firstChild);
 
1329         // @function toFront(el: HTMLElement)
 
1330         // Makes `el` the last children of its parent, so it renders in front of the other children.
 
1331         toFront: function (el) {
 
1332                 el.parentNode.appendChild(el);
 
1335         // @function toBack(el: HTMLElement)
 
1336         // Makes `el` the first children of its parent, so it renders back from the other children.
 
1337         toBack: function (el) {
 
1338                 var parent = el.parentNode;
 
1339                 parent.insertBefore(el, parent.firstChild);
 
1342         // @function hasClass(el: HTMLElement, name: String): Boolean
 
1343         // Returns `true` if the element's class attribute contains `name`.
 
1344         hasClass: function (el, name) {
 
1345                 if (el.classList !== undefined) {
 
1346                         return el.classList.contains(name);
 
1348                 var className = L.DomUtil.getClass(el);
 
1349                 return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
 
1352         // @function addClass(el: HTMLElement, name: String)
 
1353         // Adds `name` to the element's class attribute.
 
1354         addClass: function (el, name) {
 
1355                 if (el.classList !== undefined) {
 
1356                         var classes = L.Util.splitWords(name);
 
1357                         for (var i = 0, len = classes.length; i < len; i++) {
 
1358                                 el.classList.add(classes[i]);
 
1360                 } else if (!L.DomUtil.hasClass(el, name)) {
 
1361                         var className = L.DomUtil.getClass(el);
 
1362                         L.DomUtil.setClass(el, (className ? className + ' ' : '') + name);
 
1366         // @function removeClass(el: HTMLElement, name: String)
 
1367         // Removes `name` from the element's class attribute.
 
1368         removeClass: function (el, name) {
 
1369                 if (el.classList !== undefined) {
 
1370                         el.classList.remove(name);
 
1372                         L.DomUtil.setClass(el, L.Util.trim((' ' + L.DomUtil.getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
 
1376         // @function setClass(el: HTMLElement, name: String)
 
1377         // Sets the element's class.
 
1378         setClass: function (el, name) {
 
1379                 if (el.className.baseVal === undefined) {
 
1380                         el.className = name;
 
1382                         // in case of SVG element
 
1383                         el.className.baseVal = name;
 
1387         // @function getClass(el: HTMLElement): String
 
1388         // Returns the element's class.
 
1389         getClass: function (el) {
 
1390                 return el.className.baseVal === undefined ? el.className : el.className.baseVal;
 
1393         // @function setOpacity(el: HTMLElement, opacity: Number)
 
1394         // Set the opacity of an element (including old IE support).
 
1395         // `opacity` must be a number from `0` to `1`.
 
1396         setOpacity: function (el, value) {
 
1398                 if ('opacity' in el.style) {
 
1399                         el.style.opacity = value;
 
1401                 } else if ('filter' in el.style) {
 
1402                         L.DomUtil._setOpacityIE(el, value);
 
1406         _setOpacityIE: function (el, value) {
 
1408                     filterName = 'DXImageTransform.Microsoft.Alpha';
 
1410                 // filters collection throws an error if we try to retrieve a filter that doesn't exist
 
1412                         filter = el.filters.item(filterName);
 
1414                         // don't set opacity to 1 if we haven't already set an opacity,
 
1415                         // it isn't needed and breaks transparent pngs.
 
1416                         if (value === 1) { return; }
 
1419                 value = Math.round(value * 100);
 
1422                         filter.Enabled = (value !== 100);
 
1423                         filter.Opacity = value;
 
1425                         el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
 
1429         // @function testProp(props: String[]): String|false
 
1430         // Goes through the array of style names and returns the first name
 
1431         // that is a valid style name for an element. If no such name is found,
 
1432         // it returns false. Useful for vendor-prefixed styles like `transform`.
 
1433         testProp: function (props) {
 
1435                 var style = document.documentElement.style;
 
1437                 for (var i = 0; i < props.length; i++) {
 
1438                         if (props[i] in style) {
 
1445         // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
 
1446         // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
 
1447         // and optionally scaled by `scale`. Does not have an effect if the
 
1448         // browser doesn't support 3D CSS transforms.
 
1449         setTransform: function (el, offset, scale) {
 
1450                 var pos = offset || new L.Point(0, 0);
 
1452                 el.style[L.DomUtil.TRANSFORM] =
 
1454                                 'translate(' + pos.x + 'px,' + pos.y + 'px)' :
 
1455                                 'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
 
1456                         (scale ? ' scale(' + scale + ')' : '');
 
1459         // @function setPosition(el: HTMLElement, position: Point)
 
1460         // Sets the position of `el` to coordinates specified by `position`,
 
1461         // using CSS translate or top/left positioning depending on the browser
 
1462         // (used by Leaflet internally to position its layers).
 
1463         setPosition: function (el, point) { // (HTMLElement, Point[, Boolean])
 
1466                 el._leaflet_pos = point;
 
1469                 if (L.Browser.any3d) {
 
1470                         L.DomUtil.setTransform(el, point);
 
1472                         el.style.left = point.x + 'px';
 
1473                         el.style.top = point.y + 'px';
 
1477         // @function getPosition(el: HTMLElement): Point
 
1478         // Returns the coordinates of an element previously positioned with setPosition.
 
1479         getPosition: function (el) {
 
1480                 // this method is only used for elements previously positioned using setPosition,
 
1481                 // so it's safe to cache the position for performance
 
1483                 return el._leaflet_pos || new L.Point(0, 0);
 
1489         // prefix style property names
 
1491         // @property TRANSFORM: String
 
1492         // Vendor-prefixed fransform style name (e.g. `'webkitTransform'` for WebKit).
 
1493         L.DomUtil.TRANSFORM = L.DomUtil.testProp(
 
1494                         ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
 
1497         // webkitTransition comes first because some browser versions that drop vendor prefix don't do
 
1498         // the same for the transitionend event, in particular the Android 4.1 stock browser
 
1500         // @property TRANSITION: String
 
1501         // Vendor-prefixed transform style name.
 
1502         var transition = L.DomUtil.TRANSITION = L.DomUtil.testProp(
 
1503                         ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
 
1505         L.DomUtil.TRANSITION_END =
 
1506                         transition === 'webkitTransition' || transition === 'OTransition' ? transition + 'End' : 'transitionend';
 
1508         // @function disableTextSelection()
 
1509         // Prevents the user from generating `selectstart` DOM events, usually generated
 
1510         // when the user drags the mouse through a page with text. Used internally
 
1511         // by Leaflet to override the behaviour of any click-and-drag interaction on
 
1512         // the map. Affects drag interactions on the whole document.
 
1514         // @function enableTextSelection()
 
1515         // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
 
1516         if ('onselectstart' in document) {
 
1517                 L.DomUtil.disableTextSelection = function () {
 
1518                         L.DomEvent.on(window, 'selectstart', L.DomEvent.preventDefault);
 
1520                 L.DomUtil.enableTextSelection = function () {
 
1521                         L.DomEvent.off(window, 'selectstart', L.DomEvent.preventDefault);
 
1525                 var userSelectProperty = L.DomUtil.testProp(
 
1526                         ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
 
1528                 L.DomUtil.disableTextSelection = function () {
 
1529                         if (userSelectProperty) {
 
1530                                 var style = document.documentElement.style;
 
1531                                 this._userSelect = style[userSelectProperty];
 
1532                                 style[userSelectProperty] = 'none';
 
1535                 L.DomUtil.enableTextSelection = function () {
 
1536                         if (userSelectProperty) {
 
1537                                 document.documentElement.style[userSelectProperty] = this._userSelect;
 
1538                                 delete this._userSelect;
 
1543         // @function disableImageDrag()
 
1544         // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
 
1545         // for `dragstart` DOM events, usually generated when the user drags an image.
 
1546         L.DomUtil.disableImageDrag = function () {
 
1547                 L.DomEvent.on(window, 'dragstart', L.DomEvent.preventDefault);
 
1550         // @function enableImageDrag()
 
1551         // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
 
1552         L.DomUtil.enableImageDrag = function () {
 
1553                 L.DomEvent.off(window, 'dragstart', L.DomEvent.preventDefault);
 
1556         // @function preventOutline(el: HTMLElement)
 
1557         // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
 
1558         // of the element `el` invisible. Used internally by Leaflet to prevent
 
1559         // focusable elements from displaying an outline when the user performs a
 
1560         // drag interaction on them.
 
1561         L.DomUtil.preventOutline = function (element) {
 
1562                 while (element.tabIndex === -1) {
 
1563                         element = element.parentNode;
 
1565                 if (!element || !element.style) { return; }
 
1566                 L.DomUtil.restoreOutline();
 
1567                 this._outlineElement = element;
 
1568                 this._outlineStyle = element.style.outline;
 
1569                 element.style.outline = 'none';
 
1570                 L.DomEvent.on(window, 'keydown', L.DomUtil.restoreOutline, this);
 
1573         // @function restoreOutline()
 
1574         // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
 
1575         L.DomUtil.restoreOutline = function () {
 
1576                 if (!this._outlineElement) { return; }
 
1577                 this._outlineElement.style.outline = this._outlineStyle;
 
1578                 delete this._outlineElement;
 
1579                 delete this._outlineStyle;
 
1580                 L.DomEvent.off(window, 'keydown', L.DomUtil.restoreOutline, this);
 
1589  * Represents a geographical point with a certain latitude and longitude.
 
1594  * var latlng = L.latLng(50.5, 30.5);
 
1597  * 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:
 
1600  * map.panTo([50, 30]);
 
1601  * map.panTo({lon: 30, lat: 50});
 
1602  * map.panTo({lat: 50, lng: 30});
 
1603  * map.panTo(L.latLng(50, 30));
 
1607 L.LatLng = function (lat, lng, alt) {
 
1608         if (isNaN(lat) || isNaN(lng)) {
 
1609                 throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
 
1612         // @property lat: Number
 
1613         // Latitude in degrees
 
1616         // @property lng: Number
 
1617         // Longitude in degrees
 
1620         // @property alt: Number
 
1621         // Altitude in meters (optional)
 
1622         if (alt !== undefined) {
 
1627 L.LatLng.prototype = {
 
1628         // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
 
1629         // 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.
 
1630         equals: function (obj, maxMargin) {
 
1631                 if (!obj) { return false; }
 
1633                 obj = L.latLng(obj);
 
1635                 var margin = Math.max(
 
1636                         Math.abs(this.lat - obj.lat),
 
1637                         Math.abs(this.lng - obj.lng));
 
1639                 return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
 
1642         // @method toString(): String
 
1643         // Returns a string representation of the point (for debugging purposes).
 
1644         toString: function (precision) {
 
1646                         L.Util.formatNum(this.lat, precision) + ', ' +
 
1647                         L.Util.formatNum(this.lng, precision) + ')';
 
1650         // @method distanceTo(otherLatLng: LatLng): Number
 
1651         // Returns the distance (in meters) to the given `LatLng` calculated using the [Haversine formula](http://en.wikipedia.org/wiki/Haversine_formula).
 
1652         distanceTo: function (other) {
 
1653                 return L.CRS.Earth.distance(this, L.latLng(other));
 
1656         // @method wrap(): LatLng
 
1657         // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
 
1659                 return L.CRS.Earth.wrapLatLng(this);
 
1662         // @method toBounds(sizeInMeters: Number): LatLngBounds
 
1663         // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters` meters apart from the `LatLng`.
 
1664         toBounds: function (sizeInMeters) {
 
1665                 var latAccuracy = 180 * sizeInMeters / 40075017,
 
1666                     lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
 
1668                 return L.latLngBounds(
 
1669                         [this.lat - latAccuracy, this.lng - lngAccuracy],
 
1670                         [this.lat + latAccuracy, this.lng + lngAccuracy]);
 
1673         clone: function () {
 
1674                 return new L.LatLng(this.lat, this.lng, this.alt);
 
1680 // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
 
1681 // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
 
1684 // @factory L.latLng(coords: Array): LatLng
 
1685 // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
 
1688 // @factory L.latLng(coords: Object): LatLng
 
1689 // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
 
1691 L.latLng = function (a, b, c) {
 
1692         if (a instanceof L.LatLng) {
 
1695         if (L.Util.isArray(a) && typeof a[0] !== 'object') {
 
1696                 if (a.length === 3) {
 
1697                         return new L.LatLng(a[0], a[1], a[2]);
 
1699                 if (a.length === 2) {
 
1700                         return new L.LatLng(a[0], a[1]);
 
1704         if (a === undefined || a === null) {
 
1707         if (typeof a === 'object' && 'lat' in a) {
 
1708                 return new L.LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
 
1710         if (b === undefined) {
 
1713         return new L.LatLng(a, b, c);
 
1719  * @class LatLngBounds
 
1720  * @aka L.LatLngBounds
 
1722  * Represents a rectangular geographical area on a map.
 
1727  * var southWest = L.latLng(40.712, -74.227),
 
1728  * northEast = L.latLng(40.774, -74.125),
 
1729  * bounds = L.latLngBounds(southWest, northEast);
 
1732  * 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:
 
1736  *      [40.712, -74.227],
 
1742 L.LatLngBounds = function (southWest, northEast) { // (LatLng, LatLng) or (LatLng[])
 
1743         if (!southWest) { return; }
 
1745         var latlngs = northEast ? [southWest, northEast] : southWest;
 
1747         for (var i = 0, len = latlngs.length; i < len; i++) {
 
1748                 this.extend(latlngs[i]);
 
1752 L.LatLngBounds.prototype = {
 
1754         // @method extend(latlng: LatLng): this
 
1755         // Extend the bounds to contain the given point
 
1758         // @method extend(otherBounds: LatLngBounds): this
 
1759         // Extend the bounds to contain the given bounds
 
1760         extend: function (obj) {
 
1761                 var sw = this._southWest,
 
1762                     ne = this._northEast,
 
1765                 if (obj instanceof L.LatLng) {
 
1769                 } else if (obj instanceof L.LatLngBounds) {
 
1770                         sw2 = obj._southWest;
 
1771                         ne2 = obj._northEast;
 
1773                         if (!sw2 || !ne2) { return this; }
 
1776                         return obj ? this.extend(L.latLng(obj) || L.latLngBounds(obj)) : this;
 
1780                         this._southWest = new L.LatLng(sw2.lat, sw2.lng);
 
1781                         this._northEast = new L.LatLng(ne2.lat, ne2.lng);
 
1783                         sw.lat = Math.min(sw2.lat, sw.lat);
 
1784                         sw.lng = Math.min(sw2.lng, sw.lng);
 
1785                         ne.lat = Math.max(ne2.lat, ne.lat);
 
1786                         ne.lng = Math.max(ne2.lng, ne.lng);
 
1792         // @method pad(bufferRatio: Number): LatLngBounds
 
1793         // Returns bigger bounds created by extending the current bounds by a given percentage in each direction.
 
1794         pad: function (bufferRatio) {
 
1795                 var sw = this._southWest,
 
1796                     ne = this._northEast,
 
1797                     heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
 
1798                     widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
 
1800                 return new L.LatLngBounds(
 
1801                         new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
 
1802                         new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
 
1805         // @method getCenter(): LatLng
 
1806         // Returns the center point of the bounds.
 
1807         getCenter: function () {
 
1808                 return new L.LatLng(
 
1809                         (this._southWest.lat + this._northEast.lat) / 2,
 
1810                         (this._southWest.lng + this._northEast.lng) / 2);
 
1813         // @method getSouthWest(): LatLng
 
1814         // Returns the south-west point of the bounds.
 
1815         getSouthWest: function () {
 
1816                 return this._southWest;
 
1819         // @method getNorthEast(): LatLng
 
1820         // Returns the north-east point of the bounds.
 
1821         getNorthEast: function () {
 
1822                 return this._northEast;
 
1825         // @method getNorthWest(): LatLng
 
1826         // Returns the north-west point of the bounds.
 
1827         getNorthWest: function () {
 
1828                 return new L.LatLng(this.getNorth(), this.getWest());
 
1831         // @method getSouthEast(): LatLng
 
1832         // Returns the south-east point of the bounds.
 
1833         getSouthEast: function () {
 
1834                 return new L.LatLng(this.getSouth(), this.getEast());
 
1837         // @method getWest(): Number
 
1838         // Returns the west longitude of the bounds
 
1839         getWest: function () {
 
1840                 return this._southWest.lng;
 
1843         // @method getSouth(): Number
 
1844         // Returns the south latitude of the bounds
 
1845         getSouth: function () {
 
1846                 return this._southWest.lat;
 
1849         // @method getEast(): Number
 
1850         // Returns the east longitude of the bounds
 
1851         getEast: function () {
 
1852                 return this._northEast.lng;
 
1855         // @method getNorth(): Number
 
1856         // Returns the north latitude of the bounds
 
1857         getNorth: function () {
 
1858                 return this._northEast.lat;
 
1861         // @method contains(otherBounds: LatLngBounds): Boolean
 
1862         // Returns `true` if the rectangle contains the given one.
 
1865         // @method contains (latlng: LatLng): Boolean
 
1866         // Returns `true` if the rectangle contains the given point.
 
1867         contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
 
1868                 if (typeof obj[0] === 'number' || obj instanceof L.LatLng) {
 
1869                         obj = L.latLng(obj);
 
1871                         obj = L.latLngBounds(obj);
 
1874                 var sw = this._southWest,
 
1875                     ne = this._northEast,
 
1878                 if (obj instanceof L.LatLngBounds) {
 
1879                         sw2 = obj.getSouthWest();
 
1880                         ne2 = obj.getNorthEast();
 
1885                 return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
 
1886                        (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
 
1889         // @method intersects(otherBounds: LatLngBounds): Boolean
 
1890         // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
 
1891         intersects: function (bounds) {
 
1892                 bounds = L.latLngBounds(bounds);
 
1894                 var sw = this._southWest,
 
1895                     ne = this._northEast,
 
1896                     sw2 = bounds.getSouthWest(),
 
1897                     ne2 = bounds.getNorthEast(),
 
1899                     latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
 
1900                     lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
 
1902                 return latIntersects && lngIntersects;
 
1905         // @method overlaps(otherBounds: Bounds): Boolean
 
1906         // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
 
1907         overlaps: function (bounds) {
 
1908                 bounds = L.latLngBounds(bounds);
 
1910                 var sw = this._southWest,
 
1911                     ne = this._northEast,
 
1912                     sw2 = bounds.getSouthWest(),
 
1913                     ne2 = bounds.getNorthEast(),
 
1915                     latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
 
1916                     lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
 
1918                 return latOverlaps && lngOverlaps;
 
1921         // @method toBBoxString(): String
 
1922         // 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.
 
1923         toBBoxString: function () {
 
1924                 return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
 
1927         // @method equals(otherBounds: LatLngBounds): Boolean
 
1928         // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds.
 
1929         equals: function (bounds) {
 
1930                 if (!bounds) { return false; }
 
1932                 bounds = L.latLngBounds(bounds);
 
1934                 return this._southWest.equals(bounds.getSouthWest()) &&
 
1935                        this._northEast.equals(bounds.getNorthEast());
 
1938         // @method isValid(): Boolean
 
1939         // Returns `true` if the bounds are properly initialized.
 
1940         isValid: function () {
 
1941                 return !!(this._southWest && this._northEast);
 
1945 // TODO International date line?
 
1947 // @factory L.latLngBounds(southWest: LatLng, northEast: LatLng)
 
1948 // Creates a `LatLngBounds` object by defining south-west and north-east corners of the rectangle.
 
1951 // @factory L.latLngBounds(latlngs: LatLng[])
 
1952 // 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).
 
1953 L.latLngBounds = function (a, b) {
 
1954         if (a instanceof L.LatLngBounds) {
 
1957         return new L.LatLngBounds(a, b);
 
1963  * @namespace Projection
 
1965  * Leaflet comes with a set of already defined Projections out of the box:
 
1967  * @projection L.Projection.LonLat
 
1969  * Equirectangular, or Plate Carree projection — the most simple projection,
 
1970  * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
 
1971  * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
 
1972  * `EPSG:3395` and `Simple` CRS.
 
1977 L.Projection.LonLat = {
 
1978         project: function (latlng) {
 
1979                 return new L.Point(latlng.lng, latlng.lat);
 
1982         unproject: function (point) {
 
1983                 return new L.LatLng(point.y, point.x);
 
1986         bounds: L.bounds([-180, -90], [180, 90])
 
1992  * @namespace Projection
 
1993  * @projection L.Projection.SphericalMercator
 
1995  * Spherical Mercator projection — the most common projection for online maps,
 
1996  * used by almost all free and commercial tile providers. Assumes that Earth is
 
1997  * a sphere. Used by the `EPSG:3857` CRS.
 
2000 L.Projection.SphericalMercator = {
 
2003         MAX_LATITUDE: 85.0511287798,
 
2005         project: function (latlng) {
 
2006                 var d = Math.PI / 180,
 
2007                     max = this.MAX_LATITUDE,
 
2008                     lat = Math.max(Math.min(max, latlng.lat), -max),
 
2009                     sin = Math.sin(lat * d);
 
2012                                 this.R * latlng.lng * d,
 
2013                                 this.R * Math.log((1 + sin) / (1 - sin)) / 2);
 
2016         unproject: function (point) {
 
2017                 var d = 180 / Math.PI;
 
2019                 return new L.LatLng(
 
2020                         (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
 
2021                         point.x * d / this.R);
 
2024         bounds: (function () {
 
2025                 var d = 6378137 * Math.PI;
 
2026                 return L.bounds([-d, -d], [d, d]);
 
2035  * Abstract class that defines coordinate reference systems for projecting
 
2036  * geographical points into pixel (screen) coordinates and back (and to
 
2037  * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
 
2038  * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
 
2040  * Leaflet defines the most usual CRSs by default. If you want to use a
 
2041  * CRS not defined by default, take a look at the
 
2042  * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
 
2046         // @method latLngToPoint(latlng: LatLng, zoom: Number): Point
 
2047         // Projects geographical coordinates into pixel coordinates for a given zoom.
 
2048         latLngToPoint: function (latlng, zoom) {
 
2049                 var projectedPoint = this.projection.project(latlng),
 
2050                     scale = this.scale(zoom);
 
2052                 return this.transformation._transform(projectedPoint, scale);
 
2055         // @method pointToLatLng(point: Point, zoom: Number): LatLng
 
2056         // The inverse of `latLngToPoint`. Projects pixel coordinates on a given
 
2057         // zoom into geographical coordinates.
 
2058         pointToLatLng: function (point, zoom) {
 
2059                 var scale = this.scale(zoom),
 
2060                     untransformedPoint = this.transformation.untransform(point, scale);
 
2062                 return this.projection.unproject(untransformedPoint);
 
2065         // @method project(latlng: LatLng): Point
 
2066         // Projects geographical coordinates into coordinates in units accepted for
 
2067         // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
 
2068         project: function (latlng) {
 
2069                 return this.projection.project(latlng);
 
2072         // @method unproject(point: Point): LatLng
 
2073         // Given a projected coordinate returns the corresponding LatLng.
 
2074         // The inverse of `project`.
 
2075         unproject: function (point) {
 
2076                 return this.projection.unproject(point);
 
2079         // @method scale(zoom: Number): Number
 
2080         // Returns the scale used when transforming projected coordinates into
 
2081         // pixel coordinates for a particular zoom. For example, it returns
 
2082         // `256 * 2^zoom` for Mercator-based CRS.
 
2083         scale: function (zoom) {
 
2084                 return 256 * Math.pow(2, zoom);
 
2087         // @method zoom(scale: Number): Number
 
2088         // Inverse of `scale()`, returns the zoom level corresponding to a scale
 
2089         // factor of `scale`.
 
2090         zoom: function (scale) {
 
2091                 return Math.log(scale / 256) / Math.LN2;
 
2094         // @method getProjectedBounds(zoom: Number): Bounds
 
2095         // Returns the projection's bounds scaled and transformed for the provided `zoom`.
 
2096         getProjectedBounds: function (zoom) {
 
2097                 if (this.infinite) { return null; }
 
2099                 var b = this.projection.bounds,
 
2100                     s = this.scale(zoom),
 
2101                     min = this.transformation.transform(b.min, s),
 
2102                     max = this.transformation.transform(b.max, s);
 
2104                 return L.bounds(min, max);
 
2107         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
 
2108         // Returns the distance between two geographical coordinates.
 
2110         // @property code: String
 
2111         // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
 
2113         // @property wrapLng: Number[]
 
2114         // An array of two numbers defining whether the longitude (horizontal) coordinate
 
2115         // axis wraps around a given range and how. Defaults to `[-180, 180]` in most
 
2116         // geographical CRSs. If `undefined`, the longitude axis does not wrap around.
 
2118         // @property wrapLat: Number[]
 
2119         // Like `wrapLng`, but for the latitude (vertical) axis.
 
2121         // wrapLng: [min, max],
 
2122         // wrapLat: [min, max],
 
2124         // @property infinite: Boolean
 
2125         // If true, the coordinate space will be unbounded (infinite in both axes)
 
2128         // @method wrapLatLng(latlng: LatLng): LatLng
 
2129         // Returns a `LatLng` where lat and lng has been wrapped according to the
 
2130         // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
 
2131         wrapLatLng: function (latlng) {
 
2132                 var lng = this.wrapLng ? L.Util.wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
 
2133                     lat = this.wrapLat ? L.Util.wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
 
2136                 return L.latLng(lat, lng, alt);
 
2146  * A simple CRS that maps longitude and latitude into `x` and `y` directly.
 
2147  * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
 
2148  * axis should still be inverted (going from bottom to top). `distance()` returns
 
2149  * simple euclidean distance.
 
2152 L.CRS.Simple = L.extend({}, L.CRS, {
 
2153         projection: L.Projection.LonLat,
 
2154         transformation: new L.Transformation(1, 0, -1, 0),
 
2156         scale: function (zoom) {
 
2157                 return Math.pow(2, zoom);
 
2160         zoom: function (scale) {
 
2161                 return Math.log(scale) / Math.LN2;
 
2164         distance: function (latlng1, latlng2) {
 
2165                 var dx = latlng2.lng - latlng1.lng,
 
2166                     dy = latlng2.lat - latlng1.lat;
 
2168                 return Math.sqrt(dx * dx + dy * dy);
 
2180  * Serves as the base for CRS that are global such that they cover the earth.
 
2181  * Can only be used as the base for other CRS and cannot be used directly,
 
2182  * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
 
2186 L.CRS.Earth = L.extend({}, L.CRS, {
 
2187         wrapLng: [-180, 180],
 
2189         // Mean Earth Radius, as recommended for use by
 
2190         // the International Union of Geodesy and Geophysics,
 
2191         // see http://rosettacode.org/wiki/Haversine_formula
 
2194         // distance between two geographical points using spherical law of cosines approximation
 
2195         distance: function (latlng1, latlng2) {
 
2196                 var rad = Math.PI / 180,
 
2197                     lat1 = latlng1.lat * rad,
 
2198                     lat2 = latlng2.lat * rad,
 
2199                     a = Math.sin(lat1) * Math.sin(lat2) +
 
2200                         Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlng2.lng - latlng1.lng) * rad);
 
2202                 return this.R * Math.acos(Math.min(a, 1));
 
2210  * @crs L.CRS.EPSG3857
 
2212  * The most common CRS for online maps, used by almost all free and commercial
 
2213  * tile providers. Uses Spherical Mercator projection. Set in by default in
 
2214  * Map's `crs` option.
 
2217 L.CRS.EPSG3857 = L.extend({}, L.CRS.Earth, {
 
2219         projection: L.Projection.SphericalMercator,
 
2221         transformation: (function () {
 
2222                 var scale = 0.5 / (Math.PI * L.Projection.SphericalMercator.R);
 
2223                 return new L.Transformation(scale, 0.5, -scale, 0.5);
 
2227 L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, {
 
2235  * @crs L.CRS.EPSG4326
 
2237  * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
 
2240 L.CRS.EPSG4326 = L.extend({}, L.CRS.Earth, {
 
2242         projection: L.Projection.LonLat,
 
2243         transformation: new L.Transformation(1 / 180, 1, -1 / 180, 0.5)
 
2253  * The central class of the API — it is used to create a map on a page and manipulate it.
 
2258  * // initialize the map on the "map" div with a given center and zoom
 
2259  * var map = L.map('map', {
 
2260  *      center: [51.505, -0.09],
 
2267 L.Map = L.Evented.extend({
 
2270                 // @section Map State Options
 
2271                 // @option crs: CRS = L.CRS.EPSG3857
 
2272                 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
 
2273                 // sure what it means.
 
2274                 crs: L.CRS.EPSG3857,
 
2276                 // @option center: LatLng = undefined
 
2277                 // Initial geographic center of the map
 
2280                 // @option zoom: Number = undefined
 
2281                 // Initial map zoom level
 
2284                 // @option minZoom: Number = undefined
 
2285                 // Minimum zoom level of the map. Overrides any `minZoom` option set on map layers.
 
2288                 // @option maxZoom: Number = undefined
 
2289                 // Maximum zoom level of the map. Overrides any `maxZoom` option set on map layers.
 
2292                 // @option layers: Layer[] = []
 
2293                 // Array of layers that will be added to the map initially
 
2296                 // @option maxBounds: LatLngBounds = null
 
2297                 // When this option is set, the map restricts the view to the given
 
2298                 // geographical bounds, bouncing the user back when he tries to pan
 
2299                 // outside the view. To set the restriction dynamically, use
 
2300                 // [`setMaxBounds`](#map-setmaxbounds) method.
 
2301                 maxBounds: undefined,
 
2303                 // @option renderer: Renderer = *
 
2304                 // The default method for drawing vector layers on the map. `L.SVG`
 
2305                 // or `L.Canvas` by default depending on browser support.
 
2306                 renderer: undefined,
 
2309                 // @section Animation Options
 
2310                 // @option fadeAnimation: Boolean = true
 
2311                 // Whether the tile fade animation is enabled. By default it's enabled
 
2312                 // in all browsers that support CSS3 Transitions except Android.
 
2313                 fadeAnimation: true,
 
2315                 // @option markerZoomAnimation: Boolean = true
 
2316                 // Whether markers animate their zoom with the zoom animation, if disabled
 
2317                 // they will disappear for the length of the animation. By default it's
 
2318                 // enabled in all browsers that support CSS3 Transitions except Android.
 
2319                 markerZoomAnimation: true,
 
2321                 // @option transform3DLimit: Number = 2^23
 
2322                 // Defines the maximum size of a CSS translation transform. The default
 
2323                 // value should not be changed unless a web browser positions layers in
 
2324                 // the wrong place after doing a large `panBy`.
 
2325                 transform3DLimit: 8388608, // Precision limit of a 32-bit float
 
2327                 // @section Interaction Options
 
2328                 // @option zoomSnap: Number = 1
 
2329                 // Forces the map's zoom level to always be a multiple of this, particularly
 
2330                 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
 
2331                 // By default, the zoom level snaps to the nearest integer; lower values
 
2332                 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
 
2333                 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
 
2336                 // @option zoomDelta: Number = 1
 
2337                 // Controls how much the map's zoom level will change after a
 
2338                 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
 
2339                 // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
 
2340                 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
 
2343                 // @option trackResize: Boolean = true
 
2344                 // Whether the map automatically handles browser window resize to update itself.
 
2348         initialize: function (id, options) { // (HTMLElement or String, Object)
 
2349                 options = L.setOptions(this, options);
 
2351                 this._initContainer(id);
 
2354                 // hack for https://github.com/Leaflet/Leaflet/issues/1980
 
2355                 this._onResize = L.bind(this._onResize, this);
 
2359                 if (options.maxBounds) {
 
2360                         this.setMaxBounds(options.maxBounds);
 
2363                 if (options.zoom !== undefined) {
 
2364                         this._zoom = this._limitZoom(options.zoom);
 
2367                 if (options.center && options.zoom !== undefined) {
 
2368                         this.setView(L.latLng(options.center), options.zoom, {reset: true});
 
2371                 this._handlers = [];
 
2373                 this._zoomBoundLayers = {};
 
2374                 this._sizeChanged = true;
 
2376                 this.callInitHooks();
 
2378                 this._addLayers(this.options.layers);
 
2382         // @section Methods for modifying map state
 
2384         // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
 
2385         // Sets the view of the map (geographical center and zoom) with the given
 
2386         // animation options.
 
2387         setView: function (center, zoom) {
 
2388                 // replaced by animation-powered implementation in Map.PanAnimation.js
 
2389                 zoom = zoom === undefined ? this.getZoom() : zoom;
 
2390                 this._resetView(L.latLng(center), zoom);
 
2394         // @method setZoom(zoom: Number, options: Zoom/pan options): this
 
2395         // Sets the zoom of the map.
 
2396         setZoom: function (zoom, options) {
 
2397                 if (!this._loaded) {
 
2401                 return this.setView(this.getCenter(), zoom, {zoom: options});
 
2404         // @method zoomIn(delta?: Number, options?: Zoom options): this
 
2405         // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
 
2406         zoomIn: function (delta, options) {
 
2407                 delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1);
 
2408                 return this.setZoom(this._zoom + delta, options);
 
2411         // @method zoomOut(delta?: Number, options?: Zoom options): this
 
2412         // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
 
2413         zoomOut: function (delta, options) {
 
2414                 delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1);
 
2415                 return this.setZoom(this._zoom - delta, options);
 
2418         // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
 
2419         // Zooms the map while keeping a specified geographical point on the map
 
2420         // stationary (e.g. used internally for scroll zoom and double-click zoom).
 
2422         // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
 
2423         // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
 
2424         setZoomAround: function (latlng, zoom, options) {
 
2425                 var scale = this.getZoomScale(zoom),
 
2426                     viewHalf = this.getSize().divideBy(2),
 
2427                     containerPoint = latlng instanceof L.Point ? latlng : this.latLngToContainerPoint(latlng),
 
2429                     centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
 
2430                     newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
 
2432                 return this.setView(newCenter, zoom, {zoom: options});
 
2435         _getBoundsCenterZoom: function (bounds, options) {
 
2437                 options = options || {};
 
2438                 bounds = bounds.getBounds ? bounds.getBounds() : L.latLngBounds(bounds);
 
2440                 var paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
 
2441                     paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]),
 
2443                     zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
 
2445                 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
 
2447                 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
 
2449                     swPoint = this.project(bounds.getSouthWest(), zoom),
 
2450                     nePoint = this.project(bounds.getNorthEast(), zoom),
 
2451                     center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
 
2459         // @method fitBounds(bounds: LatLngBounds, options: fitBounds options): this
 
2460         // Sets a map view that contains the given geographical bounds with the
 
2461         // maximum zoom level possible.
 
2462         fitBounds: function (bounds, options) {
 
2464                 bounds = L.latLngBounds(bounds);
 
2466                 if (!bounds.isValid()) {
 
2467                         throw new Error('Bounds are not valid.');
 
2470                 var target = this._getBoundsCenterZoom(bounds, options);
 
2471                 return this.setView(target.center, target.zoom, options);
 
2474         // @method fitWorld(options?: fitBounds options): this
 
2475         // Sets a map view that mostly contains the whole world with the maximum
 
2476         // zoom level possible.
 
2477         fitWorld: function (options) {
 
2478                 return this.fitBounds([[-90, -180], [90, 180]], options);
 
2481         // @method panTo(latlng: LatLng, options?: Pan options): this
 
2482         // Pans the map to a given center.
 
2483         panTo: function (center, options) { // (LatLng)
 
2484                 return this.setView(center, this._zoom, {pan: options});
 
2487         // @method panBy(offset: Point): this
 
2488         // Pans the map by a given number of pixels (animated).
 
2489         panBy: function (offset) { // (Point)
 
2490                 // replaced with animated panBy in Map.PanAnimation.js
 
2491                 this.fire('movestart');
 
2493                 this._rawPanBy(L.point(offset));
 
2496                 return this.fire('moveend');
 
2499         // @method setMaxBounds(bounds: Bounds): this
 
2500         // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
 
2501         setMaxBounds: function (bounds) {
 
2502                 bounds = L.latLngBounds(bounds);
 
2504                 if (!bounds.isValid()) {
 
2505                         this.options.maxBounds = null;
 
2506                         return this.off('moveend', this._panInsideMaxBounds);
 
2507                 } else if (this.options.maxBounds) {
 
2508                         this.off('moveend', this._panInsideMaxBounds);
 
2511                 this.options.maxBounds = bounds;
 
2514                         this._panInsideMaxBounds();
 
2517                 return this.on('moveend', this._panInsideMaxBounds);
 
2520         // @method setMinZoom(zoom: Number): this
 
2521         // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
 
2522         setMinZoom: function (zoom) {
 
2523                 this.options.minZoom = zoom;
 
2525                 if (this._loaded && this.getZoom() < this.options.minZoom) {
 
2526                         return this.setZoom(zoom);
 
2532         // @method setMaxZoom(zoom: Number): this
 
2533         // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
 
2534         setMaxZoom: function (zoom) {
 
2535                 this.options.maxZoom = zoom;
 
2537                 if (this._loaded && (this.getZoom() > this.options.maxZoom)) {
 
2538                         return this.setZoom(zoom);
 
2544         // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
 
2545         // 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.
 
2546         panInsideBounds: function (bounds, options) {
 
2547                 this._enforcingBounds = true;
 
2548                 var center = this.getCenter(),
 
2549                     newCenter = this._limitCenter(center, this._zoom, L.latLngBounds(bounds));
 
2551                 if (!center.equals(newCenter)) {
 
2552                         this.panTo(newCenter, options);
 
2555                 this._enforcingBounds = false;
 
2559         // @method invalidateSize(options: Zoom/Pan options): this
 
2560         // Checks if the map container size changed and updates the map if so —
 
2561         // call it after you've changed the map size dynamically, also animating
 
2562         // pan by default. If `options.pan` is `false`, panning will not occur.
 
2563         // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
 
2564         // that it doesn't happen often even if the method is called many
 
2568         // @method invalidateSize(animate: Boolean): this
 
2569         // Checks if the map container size changed and updates the map if so —
 
2570         // call it after you've changed the map size dynamically, also animating
 
2572         invalidateSize: function (options) {
 
2573                 if (!this._loaded) { return this; }
 
2575                 options = L.extend({
 
2578                 }, options === true ? {animate: true} : options);
 
2580                 var oldSize = this.getSize();
 
2581                 this._sizeChanged = true;
 
2582                 this._lastCenter = null;
 
2584                 var newSize = this.getSize(),
 
2585                     oldCenter = oldSize.divideBy(2).round(),
 
2586                     newCenter = newSize.divideBy(2).round(),
 
2587                     offset = oldCenter.subtract(newCenter);
 
2589                 if (!offset.x && !offset.y) { return this; }
 
2591                 if (options.animate && options.pan) {
 
2596                                 this._rawPanBy(offset);
 
2601                         if (options.debounceMoveend) {
 
2602                                 clearTimeout(this._sizeTimer);
 
2603                                 this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200);
 
2605                                 this.fire('moveend');
 
2609                 // @section Map state change events
 
2610                 // @event resize: ResizeEvent
 
2611                 // Fired when the map is resized.
 
2612                 return this.fire('resize', {
 
2618         // @section Methods for modifying map state
 
2619         // @method stop(): this
 
2620         // Stops the currently running `panTo` or `flyTo` animation, if any.
 
2622                 this.setZoom(this._limitZoom(this._zoom));
 
2623                 if (!this.options.zoomSnap) {
 
2624                         this.fire('viewreset');
 
2626                 return this._stop();
 
2630         // TODO handler.addTo
 
2631         // TODO Appropiate docs section?
 
2632         // @section Other Methods
 
2633         // @method addHandler(name: String, HandlerClass: Function): this
 
2634         // Adds a new `Handler` to the map, given its name and constructor function.
 
2635         addHandler: function (name, HandlerClass) {
 
2636                 if (!HandlerClass) { return this; }
 
2638                 var handler = this[name] = new HandlerClass(this);
 
2640                 this._handlers.push(handler);
 
2642                 if (this.options[name]) {
 
2649         // @method remove(): this
 
2650         // Destroys the map and clears all related event listeners.
 
2651         remove: function () {
 
2653                 this._initEvents(true);
 
2655                 if (this._containerId !== this._container._leaflet_id) {
 
2656                         throw new Error('Map container is being reused by another instance');
 
2660                         // throws error in IE6-8
 
2661                         delete this._container._leaflet_id;
 
2662                         delete this._containerId;
 
2665                         this._container._leaflet_id = undefined;
 
2667                         this._containerId = undefined;
 
2670                 L.DomUtil.remove(this._mapPane);
 
2672                 if (this._clearControlPos) {
 
2673                         this._clearControlPos();
 
2676                 this._clearHandlers();
 
2679                         // @section Map state change events
 
2680                         // @event unload: Event
 
2681                         // Fired when the map is destroyed with [remove](#map-remove) method.
 
2682                         this.fire('unload');
 
2685                 for (var i in this._layers) {
 
2686                         this._layers[i].remove();
 
2692         // @section Other Methods
 
2693         // @method createPane(name: String, container?: HTMLElement): HTMLElement
 
2694         // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
 
2695         // then returns it. The pane is created as a children of `container`, or
 
2696         // as a children of the main map pane if not set.
 
2697         createPane: function (name, container) {
 
2698                 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
 
2699                     pane = L.DomUtil.create('div', className, container || this._mapPane);
 
2702                         this._panes[name] = pane;
 
2707         // @section Methods for Getting Map State
 
2709         // @method getCenter(): LatLng
 
2710         // Returns the geographical center of the map view
 
2711         getCenter: function () {
 
2712                 this._checkIfLoaded();
 
2714                 if (this._lastCenter && !this._moved()) {
 
2715                         return this._lastCenter;
 
2717                 return this.layerPointToLatLng(this._getCenterLayerPoint());
 
2720         // @method getZoom(): Number
 
2721         // Returns the current zoom level of the map view
 
2722         getZoom: function () {
 
2726         // @method getBounds(): LatLngBounds
 
2727         // Returns the geographical bounds visible in the current map view
 
2728         getBounds: function () {
 
2729                 var bounds = this.getPixelBounds(),
 
2730                     sw = this.unproject(bounds.getBottomLeft()),
 
2731                     ne = this.unproject(bounds.getTopRight());
 
2733                 return new L.LatLngBounds(sw, ne);
 
2736         // @method getMinZoom(): Number
 
2737         // 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.
 
2738         getMinZoom: function () {
 
2739                 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
 
2742         // @method getMaxZoom(): Number
 
2743         // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
 
2744         getMaxZoom: function () {
 
2745                 return this.options.maxZoom === undefined ?
 
2746                         (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
 
2747                         this.options.maxZoom;
 
2750         // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean): Number
 
2751         // Returns the maximum zoom level on which the given bounds fit to the map
 
2752         // view in its entirety. If `inside` (optional) is set to `true`, the method
 
2753         // instead returns the minimum zoom level on which the map view fits into
 
2754         // the given bounds in its entirety.
 
2755         getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
 
2756                 bounds = L.latLngBounds(bounds);
 
2757                 padding = L.point(padding || [0, 0]);
 
2759                 var zoom = this.getZoom() || 0,
 
2760                     min = this.getMinZoom(),
 
2761                     max = this.getMaxZoom(),
 
2762                     nw = bounds.getNorthWest(),
 
2763                     se = bounds.getSouthEast(),
 
2764                     size = this.getSize().subtract(padding),
 
2765                     boundsSize = this.project(se, zoom).subtract(this.project(nw, zoom)),
 
2766                     snap = L.Browser.any3d ? this.options.zoomSnap : 1;
 
2768                 var scale = Math.min(size.x / boundsSize.x, size.y / boundsSize.y);
 
2769                 zoom = this.getScaleZoom(scale, zoom);
 
2772                         zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
 
2773                         zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
 
2776                 return Math.max(min, Math.min(max, zoom));
 
2779         // @method getSize(): Point
 
2780         // Returns the current size of the map container (in pixels).
 
2781         getSize: function () {
 
2782                 if (!this._size || this._sizeChanged) {
 
2783                         this._size = new L.Point(
 
2784                                 this._container.clientWidth,
 
2785                                 this._container.clientHeight);
 
2787                         this._sizeChanged = false;
 
2789                 return this._size.clone();
 
2792         // @method getPixelBounds(): Bounds
 
2793         // Returns the bounds of the current map view in projected pixel
 
2794         // coordinates (sometimes useful in layer and overlay implementations).
 
2795         getPixelBounds: function (center, zoom) {
 
2796                 var topLeftPoint = this._getTopLeftPoint(center, zoom);
 
2797                 return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
 
2800         // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
 
2801         // the map pane? "left point of the map layer" can be confusing, specially
 
2802         // since there can be negative offsets.
 
2803         // @method getPixelOrigin(): Point
 
2804         // Returns the projected pixel coordinates of the top left point of
 
2805         // the map layer (useful in custom layer and overlay implementations).
 
2806         getPixelOrigin: function () {
 
2807                 this._checkIfLoaded();
 
2808                 return this._pixelOrigin;
 
2811         // @method getPixelWorldBounds(zoom?: Number): Bounds
 
2812         // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
 
2813         // If `zoom` is omitted, the map's current zoom level is used.
 
2814         getPixelWorldBounds: function (zoom) {
 
2815                 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
 
2818         // @section Other Methods
 
2820         // @method getPane(pane: String|HTMLElement): HTMLElement
 
2821         // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
 
2822         getPane: function (pane) {
 
2823                 return typeof pane === 'string' ? this._panes[pane] : pane;
 
2826         // @method getPanes(): Object
 
2827         // Returns a plain object containing the names of all [panes](#map-pane) as keys and
 
2828         // the panes as values.
 
2829         getPanes: function () {
 
2833         // @method getContainer: HTMLElement
 
2834         // Returns the HTML element that contains the map.
 
2835         getContainer: function () {
 
2836                 return this._container;
 
2840         // @section Conversion Methods
 
2842         // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
 
2843         // Returns the scale factor to be applied to a map transition from zoom level
 
2844         // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
 
2845         getZoomScale: function (toZoom, fromZoom) {
 
2846                 // TODO replace with universal implementation after refactoring projections
 
2847                 var crs = this.options.crs;
 
2848                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
 
2849                 return crs.scale(toZoom) / crs.scale(fromZoom);
 
2852         // @method getScaleZoom(scale: Number, fromZoom: Number): Number
 
2853         // Returns the zoom level that the map would end up at, if it is at `fromZoom`
 
2854         // level and everything is scaled by a factor of `scale`. Inverse of
 
2855         // [`getZoomScale`](#map-getZoomScale).
 
2856         getScaleZoom: function (scale, fromZoom) {
 
2857                 var crs = this.options.crs;
 
2858                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
 
2859                 var zoom = crs.zoom(scale * crs.scale(fromZoom));
 
2860                 return isNaN(zoom) ? Infinity : zoom;
 
2863         // @method project(latlng: LatLng, zoom: Number): Point
 
2864         // Projects a geographical coordinate `LatLng` according to the projection
 
2865         // of the map's CRS, then scales it according to `zoom` and the CRS's
 
2866         // `Transformation`. The result is pixel coordinate relative to
 
2868         project: function (latlng, zoom) {
 
2869                 zoom = zoom === undefined ? this._zoom : zoom;
 
2870                 return this.options.crs.latLngToPoint(L.latLng(latlng), zoom);
 
2873         // @method unproject(point: Point, zoom: Number): LatLng
 
2874         // Inverse of [`project`](#map-project).
 
2875         unproject: function (point, zoom) {
 
2876                 zoom = zoom === undefined ? this._zoom : zoom;
 
2877                 return this.options.crs.pointToLatLng(L.point(point), zoom);
 
2880         // @method layerPointToLatLng(point: Point): LatLng
 
2881         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
 
2882         // returns the corresponding geographical coordinate (for the current zoom level).
 
2883         layerPointToLatLng: function (point) {
 
2884                 var projectedPoint = L.point(point).add(this.getPixelOrigin());
 
2885                 return this.unproject(projectedPoint);
 
2888         // @method latLngToLayerPoint(latlng: LatLng): Point
 
2889         // Given a geographical coordinate, returns the corresponding pixel coordinate
 
2890         // relative to the [origin pixel](#map-getpixelorigin).
 
2891         latLngToLayerPoint: function (latlng) {
 
2892                 var projectedPoint = this.project(L.latLng(latlng))._round();
 
2893                 return projectedPoint._subtract(this.getPixelOrigin());
 
2896         // @method wrapLatLng(latlng: LatLng): LatLng
 
2897         // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
 
2898         // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
 
2900         // By default this means longitude is wrapped around the dateline so its
 
2901         // value is between -180 and +180 degrees.
 
2902         wrapLatLng: function (latlng) {
 
2903                 return this.options.crs.wrapLatLng(L.latLng(latlng));
 
2906         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
 
2907         // Returns the distance between two geographical coordinates according to
 
2908         // the map's CRS. By default this measures distance in meters.
 
2909         distance: function (latlng1, latlng2) {
 
2910                 return this.options.crs.distance(L.latLng(latlng1), L.latLng(latlng2));
 
2913         // @method containerPointToLayerPoint(point: Point): Point
 
2914         // Given a pixel coordinate relative to the map container, returns the corresponding
 
2915         // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
 
2916         containerPointToLayerPoint: function (point) { // (Point)
 
2917                 return L.point(point).subtract(this._getMapPanePos());
 
2920         // @method layerPointToContainerPoint(point: Point): Point
 
2921         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
 
2922         // returns the corresponding pixel coordinate relative to the map container.
 
2923         layerPointToContainerPoint: function (point) { // (Point)
 
2924                 return L.point(point).add(this._getMapPanePos());
 
2927         // @method containerPointToLatLng(point: Point): Point
 
2928         // Given a pixel coordinate relative to the map container, returns
 
2929         // the corresponding geographical coordinate (for the current zoom level).
 
2930         containerPointToLatLng: function (point) {
 
2931                 var layerPoint = this.containerPointToLayerPoint(L.point(point));
 
2932                 return this.layerPointToLatLng(layerPoint);
 
2935         // @method latLngToContainerPoint(latlng: LatLng): Point
 
2936         // Given a geographical coordinate, returns the corresponding pixel coordinate
 
2937         // relative to the map container.
 
2938         latLngToContainerPoint: function (latlng) {
 
2939                 return this.layerPointToContainerPoint(this.latLngToLayerPoint(L.latLng(latlng)));
 
2942         // @method mouseEventToContainerPoint(ev: MouseEvent): Point
 
2943         // Given a MouseEvent object, returns the pixel coordinate relative to the
 
2944         // map container where the event took place.
 
2945         mouseEventToContainerPoint: function (e) {
 
2946                 return L.DomEvent.getMousePosition(e, this._container);
 
2949         // @method mouseEventToLayerPoint(ev: MouseEvent): Point
 
2950         // Given a MouseEvent object, returns the pixel coordinate relative to
 
2951         // the [origin pixel](#map-getpixelorigin) where the event took place.
 
2952         mouseEventToLayerPoint: function (e) {
 
2953                 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
 
2956         // @method mouseEventToLatLng(ev: MouseEvent): LatLng
 
2957         // Given a MouseEvent object, returns geographical coordinate where the
 
2958         // event took place.
 
2959         mouseEventToLatLng: function (e) { // (MouseEvent)
 
2960                 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
 
2964         // map initialization methods
 
2966         _initContainer: function (id) {
 
2967                 var container = this._container = L.DomUtil.get(id);
 
2970                         throw new Error('Map container not found.');
 
2971                 } else if (container._leaflet_id) {
 
2972                         throw new Error('Map container is already initialized.');
 
2975                 L.DomEvent.addListener(container, 'scroll', this._onScroll, this);
 
2976                 this._containerId = L.Util.stamp(container);
 
2979         _initLayout: function () {
 
2980                 var container = this._container;
 
2982                 this._fadeAnimated = this.options.fadeAnimation && L.Browser.any3d;
 
2984                 L.DomUtil.addClass(container, 'leaflet-container' +
 
2985                         (L.Browser.touch ? ' leaflet-touch' : '') +
 
2986                         (L.Browser.retina ? ' leaflet-retina' : '') +
 
2987                         (L.Browser.ielt9 ? ' leaflet-oldie' : '') +
 
2988                         (L.Browser.safari ? ' leaflet-safari' : '') +
 
2989                         (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
 
2991                 var position = L.DomUtil.getStyle(container, 'position');
 
2993                 if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
 
2994                         container.style.position = 'relative';
 
2999                 if (this._initControlPos) {
 
3000                         this._initControlPos();
 
3004         _initPanes: function () {
 
3005                 var panes = this._panes = {};
 
3006                 this._paneRenderers = {};
 
3010                 // Panes are DOM elements used to control the ordering of layers on the map. You
 
3011                 // can access panes with [`map.getPane`](#map-getpane) or
 
3012                 // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
 
3013                 // [`map.createPane`](#map-createpane) method.
 
3015                 // Every map has the following default panes that differ only in zIndex.
 
3017                 // @pane mapPane: HTMLElement = 'auto'
 
3018                 // Pane that contains all other map panes
 
3020                 this._mapPane = this.createPane('mapPane', this._container);
 
3021                 L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
 
3023                 // @pane tilePane: HTMLElement = 200
 
3024                 // Pane for `GridLayer`s and `TileLayer`s
 
3025                 this.createPane('tilePane');
 
3026                 // @pane overlayPane: HTMLElement = 400
 
3027                 // Pane for vector overlays (`Path`s), like `Polyline`s and `Polygon`s
 
3028                 this.createPane('shadowPane');
 
3029                 // @pane shadowPane: HTMLElement = 500
 
3030                 // Pane for overlay shadows (e.g. `Marker` shadows)
 
3031                 this.createPane('overlayPane');
 
3032                 // @pane markerPane: HTMLElement = 600
 
3033                 // Pane for `Icon`s of `Marker`s
 
3034                 this.createPane('markerPane');
 
3035                 // @pane tooltipPane: HTMLElement = 650
 
3036                 // Pane for tooltip.
 
3037                 this.createPane('tooltipPane');
 
3038                 // @pane popupPane: HTMLElement = 700
 
3039                 // Pane for `Popup`s.
 
3040                 this.createPane('popupPane');
 
3042                 if (!this.options.markerZoomAnimation) {
 
3043                         L.DomUtil.addClass(panes.markerPane, 'leaflet-zoom-hide');
 
3044                         L.DomUtil.addClass(panes.shadowPane, 'leaflet-zoom-hide');
 
3049         // private methods that modify map state
 
3051         // @section Map state change events
 
3052         _resetView: function (center, zoom) {
 
3053                 L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
 
3055                 var loading = !this._loaded;
 
3056                 this._loaded = true;
 
3057                 zoom = this._limitZoom(zoom);
 
3059                 this.fire('viewprereset');
 
3061                 var zoomChanged = this._zoom !== zoom;
 
3063                         ._moveStart(zoomChanged)
 
3064                         ._move(center, zoom)
 
3065                         ._moveEnd(zoomChanged);
 
3067                 // @event viewreset: Event
 
3068                 // Fired when the map needs to redraw its content (this usually happens
 
3069                 // on map zoom or load). Very useful for creating custom overlays.
 
3070                 this.fire('viewreset');
 
3072                 // @event load: Event
 
3073                 // Fired when the map is initialized (when its center and zoom are set
 
3074                 // for the first time).
 
3080         _moveStart: function (zoomChanged) {
 
3081                 // @event zoomstart: Event
 
3082                 // Fired when the map zoom is about to change (e.g. before zoom animation).
 
3083                 // @event movestart: Event
 
3084                 // Fired when the view of the map starts changing (e.g. user starts dragging the map).
 
3086                         this.fire('zoomstart');
 
3088                 return this.fire('movestart');
 
3091         _move: function (center, zoom, data) {
 
3092                 if (zoom === undefined) {
 
3095                 var zoomChanged = this._zoom !== zoom;
 
3098                 this._lastCenter = center;
 
3099                 this._pixelOrigin = this._getNewPixelOrigin(center);
 
3101                 // @event zoom: Event
 
3102                 // Fired repeatedly during any change in zoom level, including zoom
 
3103                 // and fly animations.
 
3104                 if (zoomChanged || (data && data.pinch)) {      // Always fire 'zoom' if pinching because #3530
 
3105                         this.fire('zoom', data);
 
3108                 // @event move: Event
 
3109                 // Fired repeatedly during any movement of the map, including pan and
 
3111                 return this.fire('move', data);
 
3114         _moveEnd: function (zoomChanged) {
 
3115                 // @event zoomend: Event
 
3116                 // Fired when the map has changed, after any animations.
 
3118                         this.fire('zoomend');
 
3121                 // @event moveend: Event
 
3122                 // Fired when the center of the map stops changing (e.g. user stopped
 
3123                 // dragging the map).
 
3124                 return this.fire('moveend');
 
3127         _stop: function () {
 
3128                 L.Util.cancelAnimFrame(this._flyToFrame);
 
3129                 if (this._panAnim) {
 
3130                         this._panAnim.stop();
 
3135         _rawPanBy: function (offset) {
 
3136                 L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
 
3139         _getZoomSpan: function () {
 
3140                 return this.getMaxZoom() - this.getMinZoom();
 
3143         _panInsideMaxBounds: function () {
 
3144                 if (!this._enforcingBounds) {
 
3145                         this.panInsideBounds(this.options.maxBounds);
 
3149         _checkIfLoaded: function () {
 
3150                 if (!this._loaded) {
 
3151                         throw new Error('Set map center and zoom first.');
 
3155         // DOM event handling
 
3157         // @section Interaction events
 
3158         _initEvents: function (remove) {
 
3159                 if (!L.DomEvent) { return; }
 
3162                 this._targets[L.stamp(this._container)] = this;
 
3164                 var onOff = remove ? 'off' : 'on';
 
3166                 // @event click: MouseEvent
 
3167                 // Fired when the user clicks (or taps) the map.
 
3168                 // @event dblclick: MouseEvent
 
3169                 // Fired when the user double-clicks (or double-taps) the map.
 
3170                 // @event mousedown: MouseEvent
 
3171                 // Fired when the user pushes the mouse button on the map.
 
3172                 // @event mouseup: MouseEvent
 
3173                 // Fired when the user releases the mouse button on the map.
 
3174                 // @event mouseover: MouseEvent
 
3175                 // Fired when the mouse enters the map.
 
3176                 // @event mouseout: MouseEvent
 
3177                 // Fired when the mouse leaves the map.
 
3178                 // @event mousemove: MouseEvent
 
3179                 // Fired while the mouse moves over the map.
 
3180                 // @event contextmenu: MouseEvent
 
3181                 // Fired when the user pushes the right mouse button on the map, prevents
 
3182                 // default browser context menu from showing if there are listeners on
 
3183                 // this event. Also fired on mobile when the user holds a single touch
 
3184                 // for a second (also called long press).
 
3185                 // @event keypress: KeyboardEvent
 
3186                 // Fired when the user presses a key from the keyboard while the map is focused.
 
3187                 L.DomEvent[onOff](this._container, 'click dblclick mousedown mouseup ' +
 
3188                         'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
 
3190                 if (this.options.trackResize) {
 
3191                         L.DomEvent[onOff](window, 'resize', this._onResize, this);
 
3194                 if (L.Browser.any3d && this.options.transform3DLimit) {
 
3195                         this[onOff]('moveend', this._onMoveEnd);
 
3199         _onResize: function () {
 
3200                 L.Util.cancelAnimFrame(this._resizeRequest);
 
3201                 this._resizeRequest = L.Util.requestAnimFrame(
 
3202                         function () { this.invalidateSize({debounceMoveend: true}); }, this);
 
3205         _onScroll: function () {
 
3206                 this._container.scrollTop  = 0;
 
3207                 this._container.scrollLeft = 0;
 
3210         _onMoveEnd: function () {
 
3211                 var pos = this._getMapPanePos();
 
3212                 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
 
3213                         // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
 
3214                         // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
 
3215                         this._resetView(this.getCenter(), this.getZoom());
 
3219         _findEventTargets: function (e, type) {
 
3222                     isHover = type === 'mouseout' || type === 'mouseover',
 
3223                     src = e.target || e.srcElement,
 
3227                         target = this._targets[L.stamp(src)];
 
3228                         if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
 
3229                                 // Prevent firing click after you just dragged an object.
 
3233                         if (target && target.listens(type, true)) {
 
3234                                 if (isHover && !L.DomEvent._isExternalTarget(src, e)) { break; }
 
3235                                 targets.push(target);
 
3236                                 if (isHover) { break; }
 
3238                         if (src === this._container) { break; }
 
3239                         src = src.parentNode;
 
3241                 if (!targets.length && !dragging && !isHover && L.DomEvent._isExternalTarget(src, e)) {
 
3247         _handleDOMEvent: function (e) {
 
3248                 if (!this._loaded || L.DomEvent._skipped(e)) { return; }
 
3250                 var type = e.type === 'keypress' && e.keyCode === 13 ? 'click' : e.type;
 
3252                 if (type === 'mousedown') {
 
3253                         // prevents outline when clicking on keyboard-focusable element
 
3254                         L.DomUtil.preventOutline(e.target || e.srcElement);
 
3257                 this._fireDOMEvent(e, type);
 
3260         _fireDOMEvent: function (e, type, targets) {
 
3262                 if (e.type === 'click') {
 
3263                         // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
 
3264                         // @event preclick: MouseEvent
 
3265                         // Fired before mouse click on the map (sometimes useful when you
 
3266                         // want something to happen on click before any existing click
 
3267                         // handlers start running).
 
3268                         var synth = L.Util.extend({}, e);
 
3269                         synth.type = 'preclick';
 
3270                         this._fireDOMEvent(synth, synth.type, targets);
 
3273                 if (e._stopped) { return; }
 
3275                 // Find the layer the event is propagating from and its parents.
 
3276                 targets = (targets || []).concat(this._findEventTargets(e, type));
 
3278                 if (!targets.length) { return; }
 
3280                 var target = targets[0];
 
3281                 if (type === 'contextmenu' && target.listens(type, true)) {
 
3282                         L.DomEvent.preventDefault(e);
 
3289                 if (e.type !== 'keypress') {
 
3290                         var isMarker = target instanceof L.Marker;
 
3291                         data.containerPoint = isMarker ?
 
3292                                         this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
 
3293                         data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
 
3294                         data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
 
3297                 for (var i = 0; i < targets.length; i++) {
 
3298                         targets[i].fire(type, data, true);
 
3299                         if (data.originalEvent._stopped ||
 
3300                                 (targets[i].options.nonBubblingEvents && L.Util.indexOf(targets[i].options.nonBubblingEvents, type) !== -1)) { return; }
 
3304         _draggableMoved: function (obj) {
 
3305                 obj = obj.dragging && obj.dragging.enabled() ? obj : this;
 
3306                 return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
 
3309         _clearHandlers: function () {
 
3310                 for (var i = 0, len = this._handlers.length; i < len; i++) {
 
3311                         this._handlers[i].disable();
 
3315         // @section Other Methods
 
3317         // @method whenReady(fn: Function, context?: Object): this
 
3318         // Runs the given function `fn` when the map gets initialized with
 
3319         // a view (center and zoom) and at least one layer, or immediately
 
3320         // if it's already initialized, optionally passing a function context.
 
3321         whenReady: function (callback, context) {
 
3323                         callback.call(context || this, {target: this});
 
3325                         this.on('load', callback, context);
 
3331         // private methods for getting map state
 
3333         _getMapPanePos: function () {
 
3334                 return L.DomUtil.getPosition(this._mapPane) || new L.Point(0, 0);
 
3337         _moved: function () {
 
3338                 var pos = this._getMapPanePos();
 
3339                 return pos && !pos.equals([0, 0]);
 
3342         _getTopLeftPoint: function (center, zoom) {
 
3343                 var pixelOrigin = center && zoom !== undefined ?
 
3344                         this._getNewPixelOrigin(center, zoom) :
 
3345                         this.getPixelOrigin();
 
3346                 return pixelOrigin.subtract(this._getMapPanePos());
 
3349         _getNewPixelOrigin: function (center, zoom) {
 
3350                 var viewHalf = this.getSize()._divideBy(2);
 
3351                 return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
 
3354         _latLngToNewLayerPoint: function (latlng, zoom, center) {
 
3355                 var topLeft = this._getNewPixelOrigin(center, zoom);
 
3356                 return this.project(latlng, zoom)._subtract(topLeft);
 
3359         // layer point of the current center
 
3360         _getCenterLayerPoint: function () {
 
3361                 return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
 
3364         // offset of the specified place to the current center in pixels
 
3365         _getCenterOffset: function (latlng) {
 
3366                 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
 
3369         // adjust center for view to get inside bounds
 
3370         _limitCenter: function (center, zoom, bounds) {
 
3372                 if (!bounds) { return center; }
 
3374                 var centerPoint = this.project(center, zoom),
 
3375                     viewHalf = this.getSize().divideBy(2),
 
3376                     viewBounds = new L.Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
 
3377                     offset = this._getBoundsOffset(viewBounds, bounds, zoom);
 
3379                 // If offset is less than a pixel, ignore.
 
3380                 // This prevents unstable projections from getting into
 
3381                 // an infinite loop of tiny offsets.
 
3382                 if (offset.round().equals([0, 0])) {
 
3386                 return this.unproject(centerPoint.add(offset), zoom);
 
3389         // adjust offset for view to get inside bounds
 
3390         _limitOffset: function (offset, bounds) {
 
3391                 if (!bounds) { return offset; }
 
3393                 var viewBounds = this.getPixelBounds(),
 
3394                     newBounds = new L.Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
 
3396                 return offset.add(this._getBoundsOffset(newBounds, bounds));
 
3399         // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
 
3400         _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
 
3401                 var projectedMaxBounds = L.bounds(
 
3402                         this.project(maxBounds.getNorthEast(), zoom),
 
3403                         this.project(maxBounds.getSouthWest(), zoom)
 
3405                     minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
 
3406                     maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
 
3408                     dx = this._rebound(minOffset.x, -maxOffset.x),
 
3409                     dy = this._rebound(minOffset.y, -maxOffset.y);
 
3411                 return new L.Point(dx, dy);
 
3414         _rebound: function (left, right) {
 
3415                 return left + right > 0 ?
 
3416                         Math.round(left - right) / 2 :
 
3417                         Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
 
3420         _limitZoom: function (zoom) {
 
3421                 var min = this.getMinZoom(),
 
3422                     max = this.getMaxZoom(),
 
3423                     snap = L.Browser.any3d ? this.options.zoomSnap : 1;
 
3425                         zoom = Math.round(zoom / snap) * snap;
 
3427                 return Math.max(min, Math.min(max, zoom));
 
3433 // @factory L.map(id: String, options?: Map options)
 
3434 // Instantiates a map object given the DOM ID of a `<div>` element
 
3435 // and optionally an object literal with `Map options`.
 
3438 // @factory L.map(el: HTMLElement, options?: Map options)
 
3439 // Instantiates a map object given an instance of a `<div>` HTML element
 
3440 // and optionally an object literal with `Map options`.
 
3441 L.map = function (id, options) {
 
3442         return new L.Map(id, options);
 
3454  * A set of methods from the Layer base class that all Leaflet layers use.
 
3455  * Inherits all methods, options and events from `L.Evented`.
 
3460  * var layer = L.Marker(latlng).addTo(map);
 
3466  * Fired after the layer is added to a map
 
3468  * @event remove: Event
 
3469  * Fired after the layer is removed from a map
 
3473 L.Layer = L.Evented.extend({
 
3475         // Classes extending `L.Layer` will inherit the following options:
 
3477                 // @option pane: String = 'overlayPane'
 
3478                 // 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.
 
3479                 pane: 'overlayPane',
 
3480                 nonBubblingEvents: []  // Array of events that should not be bubbled to DOM parents (like the map)
 
3484          * Classes extending `L.Layer` will inherit the following methods:
 
3486          * @method addTo(map: Map): this
 
3487          * Adds the layer to the given map
 
3489         addTo: function (map) {
 
3494         // @method remove: this
 
3495         // Removes the layer from the map it is currently active on.
 
3496         remove: function () {
 
3497                 return this.removeFrom(this._map || this._mapToAdd);
 
3500         // @method removeFrom(map: Map): this
 
3501         // Removes the layer from the given map
 
3502         removeFrom: function (obj) {
 
3504                         obj.removeLayer(this);
 
3509         // @method getPane(name? : String): HTMLElement
 
3510         // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
 
3511         getPane: function (name) {
 
3512                 return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
 
3515         addInteractiveTarget: function (targetEl) {
 
3516                 this._map._targets[L.stamp(targetEl)] = this;
 
3520         removeInteractiveTarget: function (targetEl) {
 
3521                 delete this._map._targets[L.stamp(targetEl)];
 
3525         _layerAdd: function (e) {
 
3528                 // check in case layer gets added and then removed before the map is ready
 
3529                 if (!map.hasLayer(this)) { return; }
 
3532                 this._zoomAnimated = map._zoomAnimated;
 
3534                 if (this.getEvents) {
 
3535                         var events = this.getEvents();
 
3536                         map.on(events, this);
 
3537                         this.once('remove', function () {
 
3538                                 map.off(events, this);
 
3544                 if (this.getAttribution && this._map.attributionControl) {
 
3545                         this._map.attributionControl.addAttribution(this.getAttribution());
 
3549                 map.fire('layeradd', {layer: this});
 
3553 /* @section Extension methods
 
3556  * Every layer should extend from `L.Layer` and (re-)implement the following methods.
 
3558  * @method onAdd(map: Map): this
 
3559  * 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).
 
3561  * @method onRemove(map: Map): this
 
3562  * 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).
 
3564  * @method getEvents(): Object
 
3565  * 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.
 
3567  * @method getAttribution(): String
 
3568  * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
 
3570  * @method beforeAdd(map: Map): this
 
3571  * 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.
 
3576  * @section Layer events
 
3578  * @event layeradd: LayerEvent
 
3579  * Fired when a new layer is added to the map.
 
3581  * @event layerremove: LayerEvent
 
3582  * Fired when some layer is removed from the map
 
3584  * @section Methods for Layers and Controls
 
3587         // @method addLayer(layer: Layer): this
 
3588         // Adds the given layer to the map
 
3589         addLayer: function (layer) {
 
3590                 var id = L.stamp(layer);
 
3591                 if (this._layers[id]) { return this; }
 
3592                 this._layers[id] = layer;
 
3594                 layer._mapToAdd = this;
 
3596                 if (layer.beforeAdd) {
 
3597                         layer.beforeAdd(this);
 
3600                 this.whenReady(layer._layerAdd, layer);
 
3605         // @method removeLayer(layer: Layer): this
 
3606         // Removes the given layer from the map.
 
3607         removeLayer: function (layer) {
 
3608                 var id = L.stamp(layer);
 
3610                 if (!this._layers[id]) { return this; }
 
3613                         layer.onRemove(this);
 
3616                 if (layer.getAttribution && this.attributionControl) {
 
3617                         this.attributionControl.removeAttribution(layer.getAttribution());
 
3620                 delete this._layers[id];
 
3623                         this.fire('layerremove', {layer: layer});
 
3624                         layer.fire('remove');
 
3627                 layer._map = layer._mapToAdd = null;
 
3632         // @method hasLayer(layer: Layer): Boolean
 
3633         // Returns `true` if the given layer is currently added to the map
 
3634         hasLayer: function (layer) {
 
3635                 return !!layer && (L.stamp(layer) in this._layers);
 
3638         /* @method eachLayer(fn: Function, context?: Object): this
 
3639          * Iterates over the layers of the map, optionally specifying context of the iterator function.
 
3641          * map.eachLayer(function(layer){
 
3642          *     layer.bindPopup('Hello');
 
3646         eachLayer: function (method, context) {
 
3647                 for (var i in this._layers) {
 
3648                         method.call(context, this._layers[i]);
 
3653         _addLayers: function (layers) {
 
3654                 layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : [];
 
3656                 for (var i = 0, len = layers.length; i < len; i++) {
 
3657                         this.addLayer(layers[i]);
 
3661         _addZoomLimit: function (layer) {
 
3662                 if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
 
3663                         this._zoomBoundLayers[L.stamp(layer)] = layer;
 
3664                         this._updateZoomLevels();
 
3668         _removeZoomLimit: function (layer) {
 
3669                 var id = L.stamp(layer);
 
3671                 if (this._zoomBoundLayers[id]) {
 
3672                         delete this._zoomBoundLayers[id];
 
3673                         this._updateZoomLevels();
 
3677         _updateZoomLevels: function () {
 
3678                 var minZoom = Infinity,
 
3679                     maxZoom = -Infinity,
 
3680                     oldZoomSpan = this._getZoomSpan();
 
3682                 for (var i in this._zoomBoundLayers) {
 
3683                         var options = this._zoomBoundLayers[i].options;
 
3685                         minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
 
3686                         maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
 
3689                 this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
 
3690                 this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
 
3692                 // @section Map state change events
 
3693                 // @event zoomlevelschange: Event
 
3694                 // Fired when the number of zoomlevels on the map is changed due
 
3695                 // to adding or removing a layer.
 
3696                 if (oldZoomSpan !== this._getZoomSpan()) {
 
3697                         this.fire('zoomlevelschange');
 
3705  * @namespace Projection
 
3706  * @projection L.Projection.Mercator
 
3708  * 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.
 
3711 L.Projection.Mercator = {
 
3713         R_MINOR: 6356752.314245179,
 
3715         bounds: L.bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
 
3717         project: function (latlng) {
 
3718                 var d = Math.PI / 180,
 
3721                     tmp = this.R_MINOR / r,
 
3722                     e = Math.sqrt(1 - tmp * tmp),
 
3723                     con = e * Math.sin(y);
 
3725                 var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
 
3726                 y = -r * Math.log(Math.max(ts, 1E-10));
 
3728                 return new L.Point(latlng.lng * d * r, y);
 
3731         unproject: function (point) {
 
3732                 var d = 180 / Math.PI,
 
3734                     tmp = this.R_MINOR / r,
 
3735                     e = Math.sqrt(1 - tmp * tmp),
 
3736                     ts = Math.exp(-point.y / r),
 
3737                     phi = Math.PI / 2 - 2 * Math.atan(ts);
 
3739                 for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
 
3740                         con = e * Math.sin(phi);
 
3741                         con = Math.pow((1 - con) / (1 + con), e / 2);
 
3742                         dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
 
3746                 return new L.LatLng(phi * d, point.x * d / r);
 
3754  * @crs L.CRS.EPSG3395
 
3756  * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
 
3759 L.CRS.EPSG3395 = L.extend({}, L.CRS.Earth, {
 
3761         projection: L.Projection.Mercator,
 
3763         transformation: (function () {
 
3764                 var scale = 0.5 / (Math.PI * L.Projection.Mercator.R);
 
3765                 return new L.Transformation(scale, 0.5, -scale, 0.5);
 
3776  * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
 
3777  * 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.
 
3780  * @section Synchronous usage
 
3783  * 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.
 
3786  * var CanvasLayer = L.GridLayer.extend({
 
3787  *     createTile: function(coords){
 
3788  *         // create a <canvas> element for drawing
 
3789  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
3791  *         // setup tile width and height according to the options
 
3792  *         var size = this.getTileSize();
 
3793  *         tile.width = size.x;
 
3794  *         tile.height = size.y;
 
3796  *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
 
3797  *         var ctx = tile.getContext('2d');
 
3799  *         // return the tile so it can be rendered on screen
 
3805  * @section Asynchronous usage
 
3808  * 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.
 
3811  * var CanvasLayer = L.GridLayer.extend({
 
3812  *     createTile: function(coords, done){
 
3815  *         // create a <canvas> element for drawing
 
3816  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
 
3818  *         // setup tile width and height according to the options
 
3819  *         var size = this.getTileSize();
 
3820  *         tile.width = size.x;
 
3821  *         tile.height = size.y;
 
3823  *         // draw something asynchronously and pass the tile to the done() callback
 
3824  *         setTimeout(function() {
 
3825  *             done(error, tile);
 
3837 L.GridLayer = L.Layer.extend({
 
3840         // @aka GridLayer options
 
3842                 // @option tileSize: Number|Point = 256
 
3843                 // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
 
3846                 // @option opacity: Number = 1.0
 
3847                 // Opacity of the tiles. Can be used in the `createTile()` function.
 
3850                 // @option updateWhenIdle: Boolean = depends
 
3851                 // If `false`, new tiles are loaded during panning, otherwise only after it (for better performance). `true` by default on mobile browsers, otherwise `false`.
 
3852                 updateWhenIdle: L.Browser.mobile,
 
3854                 // @option updateWhenZooming: Boolean = true
 
3855                 // 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.
 
3856                 updateWhenZooming: true,
 
3858                 // @option updateInterval: Number = 200
 
3859                 // Tiles will not update more than once every `updateInterval` milliseconds when panning.
 
3860                 updateInterval: 200,
 
3862                 // @option attribution: String = null
 
3863                 // String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox".
 
3866                 // @option zIndex: Number = 1
 
3867                 // The explicit zIndex of the tile layer.
 
3870                 // @option bounds: LatLngBounds = undefined
 
3871                 // If set, tiles will only be loaded inside the set `LatLngBounds`.
 
3874                 // @option minZoom: Number = 0
 
3875                 // The minimum zoom level that tiles will be loaded at. By default the entire map.
 
3878                 // @option maxZoom: Number = undefined
 
3879                 // The maximum zoom level that tiles will be loaded at.
 
3882                 // @option noWrap: Boolean = false
 
3883                 // Whether the layer is wrapped around the antimeridian. If `true`, the
 
3884                 // GridLayer will only be displayed once at low zoom levels. Has no
 
3885                 // effect when the [map CRS](#map-crs) doesn't wrap around.
 
3888                 // @option pane: String = 'tilePane'
 
3889                 // `Map pane` where the grid layer will be added.
 
3892                 // @option className: String = ''
 
3893                 // A custom class name to assign to the tile layer. Empty by default.
 
3896                 // @option keepBuffer: Number = 2
 
3897                 // When panning the map, keep this many rows and columns of tiles before unloading them.
 
3901         initialize: function (options) {
 
3902                 L.setOptions(this, options);
 
3905         onAdd: function () {
 
3906                 this._initContainer();
 
3915         beforeAdd: function (map) {
 
3916                 map._addZoomLimit(this);
 
3919         onRemove: function (map) {
 
3920                 this._removeAllTiles();
 
3921                 L.DomUtil.remove(this._container);
 
3922                 map._removeZoomLimit(this);
 
3923                 this._container = null;
 
3924                 this._tileZoom = null;
 
3927         // @method bringToFront: this
 
3928         // Brings the tile layer to the top of all tile layers.
 
3929         bringToFront: function () {
 
3931                         L.DomUtil.toFront(this._container);
 
3932                         this._setAutoZIndex(Math.max);
 
3937         // @method bringToBack: this
 
3938         // Brings the tile layer to the bottom of all tile layers.
 
3939         bringToBack: function () {
 
3941                         L.DomUtil.toBack(this._container);
 
3942                         this._setAutoZIndex(Math.min);
 
3947         // @method getAttribution: String
 
3948         // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
 
3949         getAttribution: function () {
 
3950                 return this.options.attribution;
 
3953         // @method getContainer: HTMLElement
 
3954         // Returns the HTML element that contains the tiles for this layer.
 
3955         getContainer: function () {
 
3956                 return this._container;
 
3959         // @method setOpacity(opacity: Number): this
 
3960         // Changes the [opacity](#gridlayer-opacity) of the grid layer.
 
3961         setOpacity: function (opacity) {
 
3962                 this.options.opacity = opacity;
 
3963                 this._updateOpacity();
 
3967         // @method setZIndex(zIndex: Number): this
 
3968         // Changes the [zIndex](#gridlayer-zindex) of the grid layer.
 
3969         setZIndex: function (zIndex) {
 
3970                 this.options.zIndex = zIndex;
 
3971                 this._updateZIndex();
 
3976         // @method isLoading: Boolean
 
3977         // Returns `true` if any tile in the grid layer has not finished loading.
 
3978         isLoading: function () {
 
3979                 return this._loading;
 
3982         // @method redraw: this
 
3983         // Causes the layer to clear all the tiles and request them again.
 
3984         redraw: function () {
 
3986                         this._removeAllTiles();
 
3992         getEvents: function () {
 
3994                         viewprereset: this._invalidateAll,
 
3995                         viewreset: this._resetView,
 
3996                         zoom: this._resetView,
 
3997                         moveend: this._onMoveEnd
 
4000                 if (!this.options.updateWhenIdle) {
 
4001                         // update tiles on move, but not more often than once per given interval
 
4002                         if (!this._onMove) {
 
4003                                 this._onMove = L.Util.throttle(this._onMoveEnd, this.options.updateInterval, this);
 
4006                         events.move = this._onMove;
 
4009                 if (this._zoomAnimated) {
 
4010                         events.zoomanim = this._animateZoom;
 
4016         // @section Extension methods
 
4017         // Layers extending `GridLayer` shall reimplement the following method.
 
4018         // @method createTile(coords: Object, done?: Function): HTMLElement
 
4019         // Called only internally, must be overriden by classes extending `GridLayer`.
 
4020         // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
 
4021         // is specified, it must be called when the tile has finished loading and drawing.
 
4022         createTile: function () {
 
4023                 return document.createElement('div');
 
4027         // @method getTileSize: Point
 
4028         // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
 
4029         getTileSize: function () {
 
4030                 var s = this.options.tileSize;
 
4031                 return s instanceof L.Point ? s : new L.Point(s, s);
 
4034         _updateZIndex: function () {
 
4035                 if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
 
4036                         this._container.style.zIndex = this.options.zIndex;
 
4040         _setAutoZIndex: function (compare) {
 
4041                 // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
 
4043                 var layers = this.getPane().children,
 
4044                     edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
 
4046                 for (var i = 0, len = layers.length, zIndex; i < len; i++) {
 
4048                         zIndex = layers[i].style.zIndex;
 
4050                         if (layers[i] !== this._container && zIndex) {
 
4051                                 edgeZIndex = compare(edgeZIndex, +zIndex);
 
4055                 if (isFinite(edgeZIndex)) {
 
4056                         this.options.zIndex = edgeZIndex + compare(-1, 1);
 
4057                         this._updateZIndex();
 
4061         _updateOpacity: function () {
 
4062                 if (!this._map) { return; }
 
4064                 // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
 
4065                 if (L.Browser.ielt9) { return; }
 
4067                 L.DomUtil.setOpacity(this._container, this.options.opacity);
 
4069                 var now = +new Date(),
 
4073                 for (var key in this._tiles) {
 
4074                         var tile = this._tiles[key];
 
4075                         if (!tile.current || !tile.loaded) { continue; }
 
4077                         var fade = Math.min(1, (now - tile.loaded) / 200);
 
4079                         L.DomUtil.setOpacity(tile.el, fade);
 
4083                                 if (tile.active) { willPrune = true; }
 
4088                 if (willPrune && !this._noPrune) { this._pruneTiles(); }
 
4091                         L.Util.cancelAnimFrame(this._fadeFrame);
 
4092                         this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this);
 
4096         _initContainer: function () {
 
4097                 if (this._container) { return; }
 
4099                 this._container = L.DomUtil.create('div', 'leaflet-layer ' + (this.options.className || ''));
 
4100                 this._updateZIndex();
 
4102                 if (this.options.opacity < 1) {
 
4103                         this._updateOpacity();
 
4106                 this.getPane().appendChild(this._container);
 
4109         _updateLevels: function () {
 
4111                 var zoom = this._tileZoom,
 
4112                     maxZoom = this.options.maxZoom;
 
4114                 if (zoom === undefined) { return undefined; }
 
4116                 for (var z in this._levels) {
 
4117                         if (this._levels[z].el.children.length || z === zoom) {
 
4118                                 this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
 
4120                                 L.DomUtil.remove(this._levels[z].el);
 
4121                                 this._removeTilesAtZoom(z);
 
4122                                 delete this._levels[z];
 
4126                 var level = this._levels[zoom],
 
4130                         level = this._levels[zoom] = {};
 
4132                         level.el = L.DomUtil.create('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
 
4133                         level.el.style.zIndex = maxZoom;
 
4135                         level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
 
4138                         this._setZoomTransform(level, map.getCenter(), map.getZoom());
 
4140                         // force the browser to consider the newly added element for transition
 
4141                         L.Util.falseFn(level.el.offsetWidth);
 
4144                 this._level = level;
 
4149         _pruneTiles: function () {
 
4156                 var zoom = this._map.getZoom();
 
4157                 if (zoom > this.options.maxZoom ||
 
4158                         zoom < this.options.minZoom) {
 
4159                         this._removeAllTiles();
 
4163                 for (key in this._tiles) {
 
4164                         tile = this._tiles[key];
 
4165                         tile.retain = tile.current;
 
4168                 for (key in this._tiles) {
 
4169                         tile = this._tiles[key];
 
4170                         if (tile.current && !tile.active) {
 
4171                                 var coords = tile.coords;
 
4172                                 if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
 
4173                                         this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
 
4178                 for (key in this._tiles) {
 
4179                         if (!this._tiles[key].retain) {
 
4180                                 this._removeTile(key);
 
4185         _removeTilesAtZoom: function (zoom) {
 
4186                 for (var key in this._tiles) {
 
4187                         if (this._tiles[key].coords.z !== zoom) {
 
4190                         this._removeTile(key);
 
4194         _removeAllTiles: function () {
 
4195                 for (var key in this._tiles) {
 
4196                         this._removeTile(key);
 
4200         _invalidateAll: function () {
 
4201                 for (var z in this._levels) {
 
4202                         L.DomUtil.remove(this._levels[z].el);
 
4203                         delete this._levels[z];
 
4205                 this._removeAllTiles();
 
4207                 this._tileZoom = null;
 
4210         _retainParent: function (x, y, z, minZoom) {
 
4211                 var x2 = Math.floor(x / 2),
 
4212                     y2 = Math.floor(y / 2),
 
4214                     coords2 = new L.Point(+x2, +y2);
 
4217                 var key = this._tileCoordsToKey(coords2),
 
4218                     tile = this._tiles[key];
 
4220                 if (tile && tile.active) {
 
4224                 } else if (tile && tile.loaded) {
 
4229                         return this._retainParent(x2, y2, z2, minZoom);
 
4235         _retainChildren: function (x, y, z, maxZoom) {
 
4237                 for (var i = 2 * x; i < 2 * x + 2; i++) {
 
4238                         for (var j = 2 * y; j < 2 * y + 2; j++) {
 
4240                                 var coords = new L.Point(i, j);
 
4243                                 var key = this._tileCoordsToKey(coords),
 
4244                                     tile = this._tiles[key];
 
4246                                 if (tile && tile.active) {
 
4250                                 } else if (tile && tile.loaded) {
 
4254                                 if (z + 1 < maxZoom) {
 
4255                                         this._retainChildren(i, j, z + 1, maxZoom);
 
4261         _resetView: function (e) {
 
4262                 var animating = e && (e.pinch || e.flyTo);
 
4263                 this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
 
4266         _animateZoom: function (e) {
 
4267                 this._setView(e.center, e.zoom, true, e.noUpdate);
 
4270         _setView: function (center, zoom, noPrune, noUpdate) {
 
4271                 var tileZoom = Math.round(zoom);
 
4272                 if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
 
4273                     (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
 
4274                         tileZoom = undefined;
 
4277                 var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
 
4279                 if (!noUpdate || tileZoomChanged) {
 
4281                         this._tileZoom = tileZoom;
 
4283                         if (this._abortLoading) {
 
4284                                 this._abortLoading();
 
4287                         this._updateLevels();
 
4290                         if (tileZoom !== undefined) {
 
4291                                 this._update(center);
 
4298                         // Flag to prevent _updateOpacity from pruning tiles during
 
4299                         // a zoom anim or a pinch gesture
 
4300                         this._noPrune = !!noPrune;
 
4303                 this._setZoomTransforms(center, zoom);
 
4306         _setZoomTransforms: function (center, zoom) {
 
4307                 for (var i in this._levels) {
 
4308                         this._setZoomTransform(this._levels[i], center, zoom);
 
4312         _setZoomTransform: function (level, center, zoom) {
 
4313                 var scale = this._map.getZoomScale(zoom, level.zoom),
 
4314                     translate = level.origin.multiplyBy(scale)
 
4315                         .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
 
4317                 if (L.Browser.any3d) {
 
4318                         L.DomUtil.setTransform(level.el, translate, scale);
 
4320                         L.DomUtil.setPosition(level.el, translate);
 
4324         _resetGrid: function () {
 
4325                 var map = this._map,
 
4326                     crs = map.options.crs,
 
4327                     tileSize = this._tileSize = this.getTileSize(),
 
4328                     tileZoom = this._tileZoom;
 
4330                 var bounds = this._map.getPixelWorldBounds(this._tileZoom);
 
4332                         this._globalTileRange = this._pxBoundsToTileRange(bounds);
 
4335                 this._wrapX = crs.wrapLng && !this.options.noWrap && [
 
4336                         Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
 
4337                         Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
 
4339                 this._wrapY = crs.wrapLat && !this.options.noWrap && [
 
4340                         Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
 
4341                         Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
 
4345         _onMoveEnd: function () {
 
4346                 if (!this._map || this._map._animatingZoom) { return; }
 
4351         _getTiledPixelBounds: function (center) {
 
4352                 var map = this._map,
 
4353                     mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
 
4354                     scale = map.getZoomScale(mapZoom, this._tileZoom),
 
4355                     pixelCenter = map.project(center, this._tileZoom).floor(),
 
4356                     halfSize = map.getSize().divideBy(scale * 2);
 
4358                 return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
 
4361         // Private method to load tiles in the grid's active zoom level according to map bounds
 
4362         _update: function (center) {
 
4363                 var map = this._map;
 
4364                 if (!map) { return; }
 
4365                 var zoom = map.getZoom();
 
4367                 if (center === undefined) { center = map.getCenter(); }
 
4368                 if (this._tileZoom === undefined) { return; }   // if out of minzoom/maxzoom
 
4370                 var pixelBounds = this._getTiledPixelBounds(center),
 
4371                     tileRange = this._pxBoundsToTileRange(pixelBounds),
 
4372                     tileCenter = tileRange.getCenter(),
 
4374                     margin = this.options.keepBuffer,
 
4375                     noPruneRange = new L.Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
 
4376                                               tileRange.getTopRight().add([margin, -margin]));
 
4378                 for (var key in this._tiles) {
 
4379                         var c = this._tiles[key].coords;
 
4380                         if (c.z !== this._tileZoom || !noPruneRange.contains(L.point(c.x, c.y))) {
 
4381                                 this._tiles[key].current = false;
 
4385                 // _update just loads more tiles. If the tile zoom level differs too much
 
4386                 // from the map's, let _setView reset levels and prune old tiles.
 
4387                 if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
 
4389                 // create a queue of coordinates to load tiles from
 
4390                 for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
 
4391                         for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
 
4392                                 var coords = new L.Point(i, j);
 
4393                                 coords.z = this._tileZoom;
 
4395                                 if (!this._isValidTile(coords)) { continue; }
 
4397                                 var tile = this._tiles[this._tileCoordsToKey(coords)];
 
4399                                         tile.current = true;
 
4406                 // sort tile queue to load tiles in order of their distance to center
 
4407                 queue.sort(function (a, b) {
 
4408                         return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
 
4411                 if (queue.length !== 0) {
 
4412                         // if its the first batch of tiles to load
 
4413                         if (!this._loading) {
 
4414                                 this._loading = true;
 
4415                                 // @event loading: Event
 
4416                                 // Fired when the grid layer starts loading tiles.
 
4417                                 this.fire('loading');
 
4420                         // create DOM fragment to append tiles in one batch
 
4421                         var fragment = document.createDocumentFragment();
 
4423                         for (i = 0; i < queue.length; i++) {
 
4424                                 this._addTile(queue[i], fragment);
 
4427                         this._level.el.appendChild(fragment);
 
4431         _isValidTile: function (coords) {
 
4432                 var crs = this._map.options.crs;
 
4434                 if (!crs.infinite) {
 
4435                         // don't load tile if it's out of bounds and not wrapped
 
4436                         var bounds = this._globalTileRange;
 
4437                         if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
 
4438                             (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
 
4441                 if (!this.options.bounds) { return true; }
 
4443                 // don't load tile if it doesn't intersect the bounds in options
 
4444                 var tileBounds = this._tileCoordsToBounds(coords);
 
4445                 return L.latLngBounds(this.options.bounds).overlaps(tileBounds);
 
4448         _keyToBounds: function (key) {
 
4449                 return this._tileCoordsToBounds(this._keyToTileCoords(key));
 
4452         // converts tile coordinates to its geographical bounds
 
4453         _tileCoordsToBounds: function (coords) {
 
4455                 var map = this._map,
 
4456                     tileSize = this.getTileSize(),
 
4458                     nwPoint = coords.scaleBy(tileSize),
 
4459                     sePoint = nwPoint.add(tileSize),
 
4461                     nw = map.unproject(nwPoint, coords.z),
 
4462                     se = map.unproject(sePoint, coords.z);
 
4464                 if (!this.options.noWrap) {
 
4465                         nw = map.wrapLatLng(nw);
 
4466                         se = map.wrapLatLng(se);
 
4469                 return new L.LatLngBounds(nw, se);
 
4472         // converts tile coordinates to key for the tile cache
 
4473         _tileCoordsToKey: function (coords) {
 
4474                 return coords.x + ':' + coords.y + ':' + coords.z;
 
4477         // converts tile cache key to coordinates
 
4478         _keyToTileCoords: function (key) {
 
4479                 var k = key.split(':'),
 
4480                     coords = new L.Point(+k[0], +k[1]);
 
4485         _removeTile: function (key) {
 
4486                 var tile = this._tiles[key];
 
4487                 if (!tile) { return; }
 
4489                 L.DomUtil.remove(tile.el);
 
4491                 delete this._tiles[key];
 
4493                 // @event tileunload: TileEvent
 
4494                 // Fired when a tile is removed (e.g. when a tile goes off the screen).
 
4495                 this.fire('tileunload', {
 
4497                         coords: this._keyToTileCoords(key)
 
4501         _initTile: function (tile) {
 
4502                 L.DomUtil.addClass(tile, 'leaflet-tile');
 
4504                 var tileSize = this.getTileSize();
 
4505                 tile.style.width = tileSize.x + 'px';
 
4506                 tile.style.height = tileSize.y + 'px';
 
4508                 tile.onselectstart = L.Util.falseFn;
 
4509                 tile.onmousemove = L.Util.falseFn;
 
4511                 // update opacity on tiles in IE7-8 because of filter inheritance problems
 
4512                 if (L.Browser.ielt9 && this.options.opacity < 1) {
 
4513                         L.DomUtil.setOpacity(tile, this.options.opacity);
 
4516                 // without this hack, tiles disappear after zoom on Chrome for Android
 
4517                 // https://github.com/Leaflet/Leaflet/issues/2078
 
4518                 if (L.Browser.android && !L.Browser.android23) {
 
4519                         tile.style.WebkitBackfaceVisibility = 'hidden';
 
4523         _addTile: function (coords, container) {
 
4524                 var tilePos = this._getTilePos(coords),
 
4525                     key = this._tileCoordsToKey(coords);
 
4527                 var tile = this.createTile(this._wrapCoords(coords), L.bind(this._tileReady, this, coords));
 
4529                 this._initTile(tile);
 
4531                 // if createTile is defined with a second argument ("done" callback),
 
4532                 // we know that tile is async and will be ready later; otherwise
 
4533                 if (this.createTile.length < 2) {
 
4534                         // mark tile as ready, but delay one frame for opacity animation to happen
 
4535                         L.Util.requestAnimFrame(L.bind(this._tileReady, this, coords, null, tile));
 
4538                 L.DomUtil.setPosition(tile, tilePos);
 
4540                 // save tile in cache
 
4541                 this._tiles[key] = {
 
4547                 container.appendChild(tile);
 
4548                 // @event tileloadstart: TileEvent
 
4549                 // Fired when a tile is requested and starts loading.
 
4550                 this.fire('tileloadstart', {
 
4556         _tileReady: function (coords, err, tile) {
 
4557                 if (!this._map) { return; }
 
4560                         // @event tileerror: TileErrorEvent
 
4561                         // Fired when there is an error loading a tile.
 
4562                         this.fire('tileerror', {
 
4569                 var key = this._tileCoordsToKey(coords);
 
4571                 tile = this._tiles[key];
 
4572                 if (!tile) { return; }
 
4574                 tile.loaded = +new Date();
 
4575                 if (this._map._fadeAnimated) {
 
4576                         L.DomUtil.setOpacity(tile.el, 0);
 
4577                         L.Util.cancelAnimFrame(this._fadeFrame);
 
4578                         this._fadeFrame = L.Util.requestAnimFrame(this._updateOpacity, this);
 
4584                 L.DomUtil.addClass(tile.el, 'leaflet-tile-loaded');
 
4586                 // @event tileload: TileEvent
 
4587                 // Fired when a tile loads.
 
4588                 this.fire('tileload', {
 
4593                 if (this._noTilesToLoad()) {
 
4594                         this._loading = false;
 
4595                         // @event load: Event
 
4596                         // Fired when the grid layer loaded all visible tiles.
 
4599                         if (L.Browser.ielt9 || !this._map._fadeAnimated) {
 
4600                                 L.Util.requestAnimFrame(this._pruneTiles, this);
 
4602                                 // Wait a bit more than 0.2 secs (the duration of the tile fade-in)
 
4603                                 // to trigger a pruning.
 
4604                                 setTimeout(L.bind(this._pruneTiles, this), 250);
 
4609         _getTilePos: function (coords) {
 
4610                 return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
 
4613         _wrapCoords: function (coords) {
 
4614                 var newCoords = new L.Point(
 
4615                         this._wrapX ? L.Util.wrapNum(coords.x, this._wrapX) : coords.x,
 
4616                         this._wrapY ? L.Util.wrapNum(coords.y, this._wrapY) : coords.y);
 
4617                 newCoords.z = coords.z;
 
4621         _pxBoundsToTileRange: function (bounds) {
 
4622                 var tileSize = this.getTileSize();
 
4623                 return new L.Bounds(
 
4624                         bounds.min.unscaleBy(tileSize).floor(),
 
4625                         bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
 
4628         _noTilesToLoad: function () {
 
4629                 for (var key in this._tiles) {
 
4630                         if (!this._tiles[key].loaded) { return false; }
 
4636 // @factory L.gridLayer(options?: GridLayer options)
 
4637 // Creates a new instance of GridLayer with the supplied options.
 
4638 L.gridLayer = function (options) {
 
4639         return new L.GridLayer(options);
 
4646  * @inherits GridLayer
 
4648  * Used to load and display tile layers on the map. Extends `GridLayer`.
 
4653  * L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar'}).addTo(map);
 
4656  * @section URL template
 
4659  * A string of the following form:
 
4662  * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
 
4665  * `{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.
 
4667  * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
 
4670  * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
 
4675 L.TileLayer = L.GridLayer.extend({
 
4678         // @aka TileLayer options
 
4680                 // @option minZoom: Number = 0
 
4681                 // Minimum zoom number.
 
4684                 // @option maxZoom: Number = 18
 
4685                 // Maximum zoom number.
 
4688                 // @option maxNativeZoom: Number = null
 
4689                 // Maximum zoom number the tile source has available. If it is specified,
 
4690                 // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
 
4691                 // from `maxNativeZoom` level and auto-scaled.
 
4692                 maxNativeZoom: null,
 
4694                 // @option subdomains: String|String[] = 'abc'
 
4695                 // 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.
 
4698                 // @option errorTileUrl: String = ''
 
4699                 // URL to the tile image to show in place of the tile that failed to load.
 
4702                 // @option zoomOffset: Number = 0
 
4703                 // The zoom number used in tile URLs will be offset with this value.
 
4706                 // @option tms: Boolean = false
 
4707                 // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
 
4710                 // @option zoomReverse: Boolean = false
 
4711                 // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
 
4714                 // @option detectRetina: Boolean = false
 
4715                 // 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.
 
4716                 detectRetina: false,
 
4718                 // @option crossOrigin: Boolean = false
 
4719                 // If true, all tiles will have their crossOrigin attribute set to ''. This is needed if you want to access tile pixel data.
 
4723         initialize: function (url, options) {
 
4727                 options = L.setOptions(this, options);
 
4729                 // detecting retina displays, adjusting tileSize and zoom levels
 
4730                 if (options.detectRetina && L.Browser.retina && options.maxZoom > 0) {
 
4732                         options.tileSize = Math.floor(options.tileSize / 2);
 
4734                         if (!options.zoomReverse) {
 
4735                                 options.zoomOffset++;
 
4738                                 options.zoomOffset--;
 
4742                         options.minZoom = Math.max(0, options.minZoom);
 
4745                 if (typeof options.subdomains === 'string') {
 
4746                         options.subdomains = options.subdomains.split('');
 
4749                 // for https://github.com/Leaflet/Leaflet/issues/137
 
4750                 if (!L.Browser.android) {
 
4751                         this.on('tileunload', this._onTileRemove);
 
4755         // @method setUrl(url: String, noRedraw?: Boolean): this
 
4756         // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
 
4757         setUrl: function (url, noRedraw) {
 
4766         // @method createTile(coords: Object, done?: Function): HTMLElement
 
4767         // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
 
4768         // to return an `<img>` HTML element with the appropiate image URL given `coords`. The `done`
 
4769         // callback is called when the tile has been loaded.
 
4770         createTile: function (coords, done) {
 
4771                 var tile = document.createElement('img');
 
4773                 L.DomEvent.on(tile, 'load', L.bind(this._tileOnLoad, this, done, tile));
 
4774                 L.DomEvent.on(tile, 'error', L.bind(this._tileOnError, this, done, tile));
 
4776                 if (this.options.crossOrigin) {
 
4777                         tile.crossOrigin = '';
 
4781                  Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
 
4782                  http://www.w3.org/TR/WCAG20-TECHS/H67
 
4786                 tile.src = this.getTileUrl(coords);
 
4791         // @section Extension methods
 
4793         // Layers extending `TileLayer` might reimplement the following method.
 
4794         // @method getTileUrl(coords: Object): String
 
4795         // Called only internally, returns the URL for a tile given its coordinates.
 
4796         // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
 
4797         getTileUrl: function (coords) {
 
4799                         r: L.Browser.retina ? '@2x' : '',
 
4800                         s: this._getSubdomain(coords),
 
4803                         z: this._getZoomForUrl()
 
4805                 if (this._map && !this._map.options.crs.infinite) {
 
4806                         var invertedY = this._globalTileRange.max.y - coords.y;
 
4807                         if (this.options.tms) {
 
4808                                 data['y'] = invertedY;
 
4810                         data['-y'] = invertedY;
 
4813                 return L.Util.template(this._url, L.extend(data, this.options));
 
4816         _tileOnLoad: function (done, tile) {
 
4817                 // For https://github.com/Leaflet/Leaflet/issues/3332
 
4818                 if (L.Browser.ielt9) {
 
4819                         setTimeout(L.bind(done, this, null, tile), 0);
 
4825         _tileOnError: function (done, tile, e) {
 
4826                 var errorUrl = this.options.errorTileUrl;
 
4828                         tile.src = errorUrl;
 
4833         getTileSize: function () {
 
4834                 var map = this._map,
 
4835                     tileSize = L.GridLayer.prototype.getTileSize.call(this),
 
4836                     zoom = this._tileZoom + this.options.zoomOffset,
 
4837                     zoomN = this.options.maxNativeZoom;
 
4839                 // increase tile size when overscaling
 
4840                 return zoomN !== null && zoom > zoomN ?
 
4841                                 tileSize.divideBy(map.getZoomScale(zoomN, zoom)).round() :
 
4845         _onTileRemove: function (e) {
 
4846                 e.tile.onload = null;
 
4849         _getZoomForUrl: function () {
 
4851                 var options = this.options,
 
4852                     zoom = this._tileZoom;
 
4854                 if (options.zoomReverse) {
 
4855                         zoom = options.maxZoom - zoom;
 
4858                 zoom += options.zoomOffset;
 
4860                 return options.maxNativeZoom !== null ? Math.min(zoom, options.maxNativeZoom) : zoom;
 
4863         _getSubdomain: function (tilePoint) {
 
4864                 var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
 
4865                 return this.options.subdomains[index];
 
4868         // stops loading all tiles in the background layer
 
4869         _abortLoading: function () {
 
4871                 for (i in this._tiles) {
 
4872                         if (this._tiles[i].coords.z !== this._tileZoom) {
 
4873                                 tile = this._tiles[i].el;
 
4875                                 tile.onload = L.Util.falseFn;
 
4876                                 tile.onerror = L.Util.falseFn;
 
4878                                 if (!tile.complete) {
 
4879                                         tile.src = L.Util.emptyImageUrl;
 
4880                                         L.DomUtil.remove(tile);
 
4888 // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
 
4889 // Instantiates a tile layer object given a `URL template` and optionally an options object.
 
4891 L.tileLayer = function (url, options) {
 
4892         return new L.TileLayer(url, options);
 
4898  * @class TileLayer.WMS
 
4899  * @inherits TileLayer
 
4900  * @aka L.TileLayer.WMS
 
4901  * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
 
4906  * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
 
4907  *      layers: 'nexrad-n0r-900913',
 
4908  *      format: 'image/png',
 
4909  *      transparent: true,
 
4910  *      attribution: "Weather data © 2012 IEM Nexrad"
 
4915 L.TileLayer.WMS = L.TileLayer.extend({
 
4918         // @aka TileLayer.WMS options
 
4919         // If any custom options not documented here are used, they will be sent to the
 
4920         // WMS server as extra parameters in each request URL. This can be useful for
 
4921         // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
 
4926                 // @option layers: String = ''
 
4927                 // **(required)** Comma-separated list of WMS layers to show.
 
4930                 // @option styles: String = ''
 
4931                 // Comma-separated list of WMS styles.
 
4934                 // @option format: String = 'image/jpeg'
 
4935                 // WMS image format (use `'image/png'` for layers with transparency).
 
4936                 format: 'image/jpeg',
 
4938                 // @option transparent: Boolean = false
 
4939                 // If `true`, the WMS service will return images with transparency.
 
4942                 // @option version: String = '1.1.1'
 
4943                 // Version of the WMS service to use
 
4948                 // @option crs: CRS = null
 
4949                 // Coordinate Reference System to use for the WMS requests, defaults to
 
4950                 // map CRS. Don't change this if you're not sure what it means.
 
4953                 // @option uppercase: Boolean = false
 
4954                 // If `true`, WMS request parameter keys will be uppercase.
 
4958         initialize: function (url, options) {
 
4962                 var wmsParams = L.extend({}, this.defaultWmsParams);
 
4964                 // all keys that are not TileLayer options go to WMS params
 
4965                 for (var i in options) {
 
4966                         if (!(i in this.options)) {
 
4967                                 wmsParams[i] = options[i];
 
4971                 options = L.setOptions(this, options);
 
4973                 wmsParams.width = wmsParams.height = options.tileSize * (options.detectRetina && L.Browser.retina ? 2 : 1);
 
4975                 this.wmsParams = wmsParams;
 
4978         onAdd: function (map) {
 
4980                 this._crs = this.options.crs || map.options.crs;
 
4981                 this._wmsVersion = parseFloat(this.wmsParams.version);
 
4983                 var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
 
4984                 this.wmsParams[projectionKey] = this._crs.code;
 
4986                 L.TileLayer.prototype.onAdd.call(this, map);
 
4989         getTileUrl: function (coords) {
 
4991                 var tileBounds = this._tileCoordsToBounds(coords),
 
4992                     nw = this._crs.project(tileBounds.getNorthWest()),
 
4993                     se = this._crs.project(tileBounds.getSouthEast()),
 
4995                     bbox = (this._wmsVersion >= 1.3 && this._crs === L.CRS.EPSG4326 ?
 
4996                             [se.y, nw.x, nw.y, se.x] :
 
4997                             [nw.x, se.y, se.x, nw.y]).join(','),
 
4999                     url = L.TileLayer.prototype.getTileUrl.call(this, coords);
 
5002                         L.Util.getParamString(this.wmsParams, url, this.options.uppercase) +
 
5003                         (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
 
5006         // @method setParams(params: Object, noRedraw?: Boolean): this
 
5007         // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
 
5008         setParams: function (params, noRedraw) {
 
5010                 L.extend(this.wmsParams, params);
 
5021 // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
 
5022 // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
 
5023 L.tileLayer.wms = function (url, options) {
 
5024         return new L.TileLayer.WMS(url, options);
 
5030  * @class ImageOverlay
 
5031  * @aka L.ImageOverlay
 
5032  * @inherits Interactive layer
 
5034  * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
 
5039  * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
 
5040  *      imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
 
5041  * L.imageOverlay(imageUrl, imageBounds).addTo(map);
 
5045 L.ImageOverlay = L.Layer.extend({
 
5048         // @aka ImageOverlay options
 
5050                 // @option opacity: Number = 1.0
 
5051                 // The opacity of the image overlay.
 
5054                 // @option alt: String = ''
 
5055                 // Text for the `alt` attribute of the image (useful for accessibility).
 
5058                 // @option interactive: Boolean = false
 
5059                 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
 
5062                 // @option attribution: String = null
 
5063                 // An optional string containing HTML to be shown on the `Attribution control`
 
5066                 // @option crossOrigin: Boolean = false
 
5067                 // If true, the image will have its crossOrigin attribute set to ''. This is needed if you want to access image pixel data.
 
5071         initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
 
5073                 this._bounds = L.latLngBounds(bounds);
 
5075                 L.setOptions(this, options);
 
5078         onAdd: function () {
 
5082                         if (this.options.opacity < 1) {
 
5083                                 this._updateOpacity();
 
5087                 if (this.options.interactive) {
 
5088                         L.DomUtil.addClass(this._image, 'leaflet-interactive');
 
5089                         this.addInteractiveTarget(this._image);
 
5092                 this.getPane().appendChild(this._image);
 
5096         onRemove: function () {
 
5097                 L.DomUtil.remove(this._image);
 
5098                 if (this.options.interactive) {
 
5099                         this.removeInteractiveTarget(this._image);
 
5103         // @method setOpacity(opacity: Number): this
 
5104         // Sets the opacity of the overlay.
 
5105         setOpacity: function (opacity) {
 
5106                 this.options.opacity = opacity;
 
5109                         this._updateOpacity();
 
5114         setStyle: function (styleOpts) {
 
5115                 if (styleOpts.opacity) {
 
5116                         this.setOpacity(styleOpts.opacity);
 
5121         // @method bringToFront(): this
 
5122         // Brings the layer to the top of all overlays.
 
5123         bringToFront: function () {
 
5125                         L.DomUtil.toFront(this._image);
 
5130         // @method bringToBack(): this
 
5131         // Brings the layer to the bottom of all overlays.
 
5132         bringToBack: function () {
 
5134                         L.DomUtil.toBack(this._image);
 
5139         // @method setUrl(url: String): this
 
5140         // Changes the URL of the image.
 
5141         setUrl: function (url) {
 
5145                         this._image.src = url;
 
5150         setBounds: function (bounds) {
 
5151                 this._bounds = bounds;
 
5159         getAttribution: function () {
 
5160                 return this.options.attribution;
 
5163         getEvents: function () {
 
5166                         viewreset: this._reset
 
5169                 if (this._zoomAnimated) {
 
5170                         events.zoomanim = this._animateZoom;
 
5176         getBounds: function () {
 
5177                 return this._bounds;
 
5180         getElement: function () {
 
5184         _initImage: function () {
 
5185                 var img = this._image = L.DomUtil.create('img',
 
5186                                 'leaflet-image-layer ' + (this._zoomAnimated ? 'leaflet-zoom-animated' : ''));
 
5188                 img.onselectstart = L.Util.falseFn;
 
5189                 img.onmousemove = L.Util.falseFn;
 
5191                 img.onload = L.bind(this.fire, this, 'load');
 
5193                 if (this.options.crossOrigin) {
 
5194                         img.crossOrigin = '';
 
5197                 img.src = this._url;
 
5198                 img.alt = this.options.alt;
 
5201         _animateZoom: function (e) {
 
5202                 var scale = this._map.getZoomScale(e.zoom),
 
5203                     offset = this._map._latLngToNewLayerPoint(this._bounds.getNorthWest(), e.zoom, e.center);
 
5205                 L.DomUtil.setTransform(this._image, offset, scale);
 
5208         _reset: function () {
 
5209                 var image = this._image,
 
5210                     bounds = new L.Bounds(
 
5211                         this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
 
5212                         this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
 
5213                     size = bounds.getSize();
 
5215                 L.DomUtil.setPosition(image, bounds.min);
 
5217                 image.style.width  = size.x + 'px';
 
5218                 image.style.height = size.y + 'px';
 
5221         _updateOpacity: function () {
 
5222                 L.DomUtil.setOpacity(this._image, this.options.opacity);
 
5226 // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
 
5227 // Instantiates an image overlay object given the URL of the image and the
 
5228 // geographical bounds it is tied to.
 
5229 L.imageOverlay = function (url, bounds, options) {
 
5230         return new L.ImageOverlay(url, bounds, options);
 
5240  * Represents an icon to provide when creating a marker.
 
5245  * var myIcon = L.icon({
 
5246  *     iconUrl: 'my-icon.png',
 
5247  *     iconRetinaUrl: 'my-icon@2x.png',
 
5248  *     iconSize: [38, 95],
 
5249  *     iconAnchor: [22, 94],
 
5250  *     popupAnchor: [-3, -76],
 
5251  *     shadowUrl: 'my-icon-shadow.png',
 
5252  *     shadowRetinaUrl: 'my-icon-shadow@2x.png',
 
5253  *     shadowSize: [68, 95],
 
5254  *     shadowAnchor: [22, 94]
 
5257  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
 
5260  * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
 
5264 L.Icon = L.Class.extend({
 
5269          * @option iconUrl: String = null
 
5270          * **(required)** The URL to the icon image (absolute or relative to your script path).
 
5272          * @option iconRetinaUrl: String = null
 
5273          * The URL to a retina sized version of the icon image (absolute or relative to your
 
5274          * script path). Used for Retina screen devices.
 
5276          * @option iconSize: Point = null
 
5277          * Size of the icon image in pixels.
 
5279          * @option iconAnchor: Point = null
 
5280          * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
 
5281          * will be aligned so that this point is at the marker's geographical location. Centered
 
5282          * by default if size is specified, also can be set in CSS with negative margins.
 
5284          * @option popupAnchor: Point = null
 
5285          * The coordinates of the point from which popups will "open", relative to the icon anchor.
 
5287          * @option shadowUrl: String = null
 
5288          * The URL to the icon shadow image. If not specified, no shadow image will be created.
 
5290          * @option shadowRetinaUrl: String = null
 
5292          * @option shadowSize: Point = null
 
5293          * Size of the shadow image in pixels.
 
5295          * @option shadowAnchor: Point = null
 
5296          * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
 
5297          * as iconAnchor if not specified).
 
5299          * @option className: String = ''
 
5300          * A custom class name to assign to both icon and shadow images. Empty by default.
 
5303         initialize: function (options) {
 
5304                 L.setOptions(this, options);
 
5307         // @method createIcon(oldIcon?: HTMLElement): HTMLElement
 
5308         // Called internally when the icon has to be shown, returns a `<img>` HTML element
 
5309         // styled according to the options.
 
5310         createIcon: function (oldIcon) {
 
5311                 return this._createIcon('icon', oldIcon);
 
5314         // @method createShadow(oldIcon?: HTMLElement): HTMLElement
 
5315         // As `createIcon`, but for the shadow beneath it.
 
5316         createShadow: function (oldIcon) {
 
5317                 return this._createIcon('shadow', oldIcon);
 
5320         _createIcon: function (name, oldIcon) {
 
5321                 var src = this._getIconUrl(name);
 
5324                         if (name === 'icon') {
 
5325                                 throw new Error('iconUrl not set in Icon options (see the docs).');
 
5330                 var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
 
5331                 this._setIconStyles(img, name);
 
5336         _setIconStyles: function (img, name) {
 
5337                 var options = this.options;
 
5338                 var sizeOption = options[name + 'Size'];
 
5340                 if (typeof sizeOption === 'number') {
 
5341                         sizeOption = [sizeOption, sizeOption];
 
5344                 var size = L.point(sizeOption),
 
5345                     anchor = L.point(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
 
5346                             size && size.divideBy(2, true));
 
5348                 img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
 
5351                         img.style.marginLeft = (-anchor.x) + 'px';
 
5352                         img.style.marginTop  = (-anchor.y) + 'px';
 
5356                         img.style.width  = size.x + 'px';
 
5357                         img.style.height = size.y + 'px';
 
5361         _createImg: function (src, el) {
 
5362                 el = el || document.createElement('img');
 
5367         _getIconUrl: function (name) {
 
5368                 return L.Browser.retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
 
5373 // @factory L.icon(options: Icon options)
 
5374 // Creates an icon instance with the given options.
 
5375 L.icon = function (options) {
 
5376         return new L.Icon(options);
 
5382  * @miniclass Icon.Default (Icon)
 
5383  * @aka L.Icon.Default
 
5386  * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
 
5387  * no icon is specified. Points to the blue marker image distributed with Leaflet
 
5390  * In order to change the default icon, just change the properties of `L.Icon.Default.prototype.options`
 
5391  * (which is a set of `Icon options`).
 
5394 L.Icon.Default = L.Icon.extend({
 
5397                 iconUrl:       'marker-icon.png',
 
5398                 iconRetinaUrl: 'marker-icon-2x.png',
 
5399                 shadowUrl:     'marker-shadow.png',
 
5401                 iconAnchor:  [12, 41],
 
5402                 popupAnchor: [1, -34],
 
5403                 tooltipAnchor: [16, -28],
 
5404                 shadowSize:  [41, 41]
 
5407         _getIconUrl: function (name) {
 
5408                 if (!L.Icon.Default.imagePath) {        // Deprecated, backwards-compatibility only
 
5409                         L.Icon.Default.imagePath = this._detectIconPath();
 
5412                 // @option imagePath: String
 
5413                 // `L.Icon.Default` will try to auto-detect the absolute location of the
 
5414                 // blue icon images. If you are placing these images in a non-standard
 
5415                 // way, set this option to point to the right absolute path.
 
5416                 return (this.options.imagePath || L.Icon.Default.imagePath) + L.Icon.prototype._getIconUrl.call(this, name);
 
5419         _detectIconPath: function () {
 
5420                 var el = L.DomUtil.create('div',  'leaflet-default-icon-path', document.body);
 
5421                 var path = L.DomUtil.getStyle(el, 'background-image') ||
 
5422                            L.DomUtil.getStyle(el, 'backgroundImage');   // IE8
 
5424                 document.body.removeChild(el);
 
5426                 return path.indexOf('url') === 0 ?
 
5427                         path.replace(/^url\([\"\']?/, '').replace(/[\"\']?\)$/, '') : '';
 
5435  * @inherits Interactive layer
 
5437  * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
 
5442  * L.marker([50.5, 30.5]).addTo(map);
 
5446 L.Marker = L.Layer.extend({
 
5449         // @aka Marker options
 
5451                 // @option icon: Icon = *
 
5452                 // 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.
 
5453                 icon: new L.Icon.Default(),
 
5455                 // Option inherited from "Interactive layer" abstract class
 
5458                 // @option draggable: Boolean = false
 
5459                 // Whether the marker is draggable with mouse/touch or not.
 
5462                 // @option keyboard: Boolean = true
 
5463                 // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
 
5466                 // @option title: String = ''
 
5467                 // Text for the browser tooltip that appear on marker hover (no tooltip by default).
 
5470                 // @option alt: String = ''
 
5471                 // Text for the `alt` attribute of the icon image (useful for accessibility).
 
5474                 // @option zIndexOffset: Number = 0
 
5475                 // 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).
 
5478                 // @option opacity: Number = 1.0
 
5479                 // The opacity of the marker.
 
5482                 // @option riseOnHover: Boolean = false
 
5483                 // If `true`, the marker will get on top of others when you hover the mouse over it.
 
5486                 // @option riseOffset: Number = 250
 
5487                 // The z-index offset used for the `riseOnHover` feature.
 
5490                 // @option pane: String = 'markerPane'
 
5491                 // `Map pane` where the markers icon will be added.
 
5494                 // FIXME: shadowPane is no longer a valid option
 
5495                 nonBubblingEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu']
 
5500          * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
 
5503         initialize: function (latlng, options) {
 
5504                 L.setOptions(this, options);
 
5505                 this._latlng = L.latLng(latlng);
 
5508         onAdd: function (map) {
 
5509                 this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
 
5511                 if (this._zoomAnimated) {
 
5512                         map.on('zoomanim', this._animateZoom, this);
 
5519         onRemove: function (map) {
 
5520                 if (this.dragging && this.dragging.enabled()) {
 
5521                         this.options.draggable = true;
 
5522                         this.dragging.removeHooks();
 
5525                 if (this._zoomAnimated) {
 
5526                         map.off('zoomanim', this._animateZoom, this);
 
5530                 this._removeShadow();
 
5533         getEvents: function () {
 
5536                         viewreset: this.update
 
5540         // @method getLatLng: LatLng
 
5541         // Returns the current geographical position of the marker.
 
5542         getLatLng: function () {
 
5543                 return this._latlng;
 
5546         // @method setLatLng(latlng: LatLng): this
 
5547         // Changes the marker position to the given point.
 
5548         setLatLng: function (latlng) {
 
5549                 var oldLatLng = this._latlng;
 
5550                 this._latlng = L.latLng(latlng);
 
5553                 // @event move: Event
 
5554                 // 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`.
 
5555                 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
 
5558         // @method setZIndexOffset(offset: Number): this
 
5559         // Changes the [zIndex offset](#marker-zindexoffset) of the marker.
 
5560         setZIndexOffset: function (offset) {
 
5561                 this.options.zIndexOffset = offset;
 
5562                 return this.update();
 
5565         // @method setIcon(icon: Icon): this
 
5566         // Changes the marker icon.
 
5567         setIcon: function (icon) {
 
5569                 this.options.icon = icon;
 
5577                         this.bindPopup(this._popup, this._popup.options);
 
5583         getElement: function () {
 
5587         update: function () {
 
5590                         var pos = this._map.latLngToLayerPoint(this._latlng).round();
 
5597         _initIcon: function () {
 
5598                 var options = this.options,
 
5599                     classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
 
5601                 var icon = options.icon.createIcon(this._icon),
 
5604                 // if we're not reusing the icon, remove the old one and init new one
 
5605                 if (icon !== this._icon) {
 
5611                         if (options.title) {
 
5612                                 icon.title = options.title;
 
5615                                 icon.alt = options.alt;
 
5619                 L.DomUtil.addClass(icon, classToAdd);
 
5621                 if (options.keyboard) {
 
5622                         icon.tabIndex = '0';
 
5627                 if (options.riseOnHover) {
 
5629                                 mouseover: this._bringToFront,
 
5630                                 mouseout: this._resetZIndex
 
5634                 var newShadow = options.icon.createShadow(this._shadow),
 
5637                 if (newShadow !== this._shadow) {
 
5638                         this._removeShadow();
 
5643                         L.DomUtil.addClass(newShadow, classToAdd);
 
5645                 this._shadow = newShadow;
 
5648                 if (options.opacity < 1) {
 
5649                         this._updateOpacity();
 
5654                         this.getPane().appendChild(this._icon);
 
5656                 this._initInteraction();
 
5657                 if (newShadow && addShadow) {
 
5658                         this.getPane('shadowPane').appendChild(this._shadow);
 
5662         _removeIcon: function () {
 
5663                 if (this.options.riseOnHover) {
 
5665                                 mouseover: this._bringToFront,
 
5666                                 mouseout: this._resetZIndex
 
5670                 L.DomUtil.remove(this._icon);
 
5671                 this.removeInteractiveTarget(this._icon);
 
5676         _removeShadow: function () {
 
5678                         L.DomUtil.remove(this._shadow);
 
5680                 this._shadow = null;
 
5683         _setPos: function (pos) {
 
5684                 L.DomUtil.setPosition(this._icon, pos);
 
5687                         L.DomUtil.setPosition(this._shadow, pos);
 
5690                 this._zIndex = pos.y + this.options.zIndexOffset;
 
5692                 this._resetZIndex();
 
5695         _updateZIndex: function (offset) {
 
5696                 this._icon.style.zIndex = this._zIndex + offset;
 
5699         _animateZoom: function (opt) {
 
5700                 var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
 
5705         _initInteraction: function () {
 
5707                 if (!this.options.interactive) { return; }
 
5709                 L.DomUtil.addClass(this._icon, 'leaflet-interactive');
 
5711                 this.addInteractiveTarget(this._icon);
 
5713                 if (L.Handler.MarkerDrag) {
 
5714                         var draggable = this.options.draggable;
 
5715                         if (this.dragging) {
 
5716                                 draggable = this.dragging.enabled();
 
5717                                 this.dragging.disable();
 
5720                         this.dragging = new L.Handler.MarkerDrag(this);
 
5723                                 this.dragging.enable();
 
5728         // @method setOpacity(opacity: Number): this
 
5729         // Changes the opacity of the marker.
 
5730         setOpacity: function (opacity) {
 
5731                 this.options.opacity = opacity;
 
5733                         this._updateOpacity();
 
5739         _updateOpacity: function () {
 
5740                 var opacity = this.options.opacity;
 
5742                 L.DomUtil.setOpacity(this._icon, opacity);
 
5745                         L.DomUtil.setOpacity(this._shadow, opacity);
 
5749         _bringToFront: function () {
 
5750                 this._updateZIndex(this.options.riseOffset);
 
5753         _resetZIndex: function () {
 
5754                 this._updateZIndex(0);
 
5759 // factory L.marker(latlng: LatLng, options? : Marker options)
 
5761 // @factory L.marker(latlng: LatLng, options? : Marker options)
 
5762 // Instantiates a Marker object given a geographical point and optionally an options object.
 
5763 L.marker = function (latlng, options) {
 
5764         return new L.Marker(latlng, options);
 
5774  * Represents a lightweight icon for markers that uses a simple `<div>`
 
5775  * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
 
5779  * var myIcon = L.divIcon({className: 'my-div-icon'});
 
5780  * // you can set .my-div-icon styles in CSS
 
5782  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
 
5785  * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
 
5788 L.DivIcon = L.Icon.extend({
 
5791                 // @aka DivIcon options
 
5792                 iconSize: [12, 12], // also can be set through CSS
 
5794                 // iconAnchor: (Point),
 
5795                 // popupAnchor: (Point),
 
5797                 // @option html: String = ''
 
5798                 // Custom HTML code to put inside the div element, empty by default.
 
5801                 // @option bgPos: Point = [0, 0]
 
5802                 // Optional relative position of the background, in pixels
 
5805                 className: 'leaflet-div-icon'
 
5808         createIcon: function (oldIcon) {
 
5809                 var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
 
5810                     options = this.options;
 
5812                 div.innerHTML = options.html !== false ? options.html : '';
 
5814                 if (options.bgPos) {
 
5815                         var bgPos = L.point(options.bgPos);
 
5816                         div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
 
5818                 this._setIconStyles(div, 'icon');
 
5823         createShadow: function () {
 
5828 // @factory L.divIcon(options: DivIcon options)
 
5829 // Creates a `DivIcon` instance with the given options.
 
5830 L.divIcon = function (options) {
 
5831         return new L.DivIcon(options);
 
5840  * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
 
5843 // @namespace DivOverlay
 
5844 L.DivOverlay = L.Layer.extend({
 
5847         // @aka DivOverlay options
 
5849                 // @option offset: Point = Point(0, 7)
 
5850                 // The offset of the popup position. Useful to control the anchor
 
5851                 // of the popup when opening it on some overlays.
 
5854                 // @option className: String = ''
 
5855                 // A custom CSS class name to assign to the popup.
 
5858                 // @option pane: String = 'popupPane'
 
5859                 // `Map pane` where the popup will be added.
 
5863         initialize: function (options, source) {
 
5864                 L.setOptions(this, options);
 
5866                 this._source = source;
 
5869         onAdd: function (map) {
 
5870                 this._zoomAnimated = map._zoomAnimated;
 
5872                 if (!this._container) {
 
5876                 if (map._fadeAnimated) {
 
5877                         L.DomUtil.setOpacity(this._container, 0);
 
5880                 clearTimeout(this._removeTimeout);
 
5881                 this.getPane().appendChild(this._container);
 
5884                 if (map._fadeAnimated) {
 
5885                         L.DomUtil.setOpacity(this._container, 1);
 
5888                 this.bringToFront();
 
5891         onRemove: function (map) {
 
5892                 if (map._fadeAnimated) {
 
5893                         L.DomUtil.setOpacity(this._container, 0);
 
5894                         this._removeTimeout = setTimeout(L.bind(L.DomUtil.remove, L.DomUtil, this._container), 200);
 
5896                         L.DomUtil.remove(this._container);
 
5901         // @method getLatLng: LatLng
 
5902         // Returns the geographical point of popup.
 
5903         getLatLng: function () {
 
5904                 return this._latlng;
 
5907         // @method setLatLng(latlng: LatLng): this
 
5908         // Sets the geographical point where the popup will open.
 
5909         setLatLng: function (latlng) {
 
5910                 this._latlng = L.latLng(latlng);
 
5912                         this._updatePosition();
 
5918         // @method getContent: String|HTMLElement
 
5919         // Returns the content of the popup.
 
5920         getContent: function () {
 
5921                 return this._content;
 
5924         // @method setContent(htmlContent: String|HTMLElement|Function): this
 
5925         // 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.
 
5926         setContent: function (content) {
 
5927                 this._content = content;
 
5932         // @method getElement: String|HTMLElement
 
5933         // Alias for [getContent()](#popup-getcontent)
 
5934         getElement: function () {
 
5935                 return this._container;
 
5938         // @method update: null
 
5939         // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
 
5940         update: function () {
 
5941                 if (!this._map) { return; }
 
5943                 this._container.style.visibility = 'hidden';
 
5945                 this._updateContent();
 
5946                 this._updateLayout();
 
5947                 this._updatePosition();
 
5949                 this._container.style.visibility = '';
 
5954         getEvents: function () {
 
5956                         zoom: this._updatePosition,
 
5957                         viewreset: this._updatePosition
 
5960                 if (this._zoomAnimated) {
 
5961                         events.zoomanim = this._animateZoom;
 
5966         // @method isOpen: Boolean
 
5967         // Returns `true` when the popup is visible on the map.
 
5968         isOpen: function () {
 
5969                 return !!this._map && this._map.hasLayer(this);
 
5972         // @method bringToFront: this
 
5973         // Brings this popup in front of other popups (in the same map pane).
 
5974         bringToFront: function () {
 
5976                         L.DomUtil.toFront(this._container);
 
5981         // @method bringToBack: this
 
5982         // Brings this popup to the back of other popups (in the same map pane).
 
5983         bringToBack: function () {
 
5985                         L.DomUtil.toBack(this._container);
 
5990         _updateContent: function () {
 
5991                 if (!this._content) { return; }
 
5993                 var node = this._contentNode;
 
5994                 var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
 
5996                 if (typeof content === 'string') {
 
5997                         node.innerHTML = content;
 
5999                         while (node.hasChildNodes()) {
 
6000                                 node.removeChild(node.firstChild);
 
6002                         node.appendChild(content);
 
6004                 this.fire('contentupdate');
 
6007         _updatePosition: function () {
 
6008                 if (!this._map) { return; }
 
6010                 var pos = this._map.latLngToLayerPoint(this._latlng),
 
6011                     offset = L.point(this.options.offset),
 
6012                     anchor = this._getAnchor();
 
6014                 if (this._zoomAnimated) {
 
6015                         L.DomUtil.setPosition(this._container, pos.add(anchor));
 
6017                         offset = offset.add(pos).add(anchor);
 
6020                 var bottom = this._containerBottom = -offset.y,
 
6021                     left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
 
6023                 // bottom position the popup in case the height of the popup changes (images loading etc)
 
6024                 this._container.style.bottom = bottom + 'px';
 
6025                 this._container.style.left = left + 'px';
 
6028         _getAnchor: function () {
 
6038  * @inherits DivOverlay
 
6040  * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
 
6041  * open popups while making sure that only one popup is open at one time
 
6042  * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
 
6046  * If you want to just bind a popup to marker click and then open it, it's really easy:
 
6049  * marker.bindPopup(popupContent).openPopup();
 
6051  * Path overlays like polylines also have a `bindPopup` method.
 
6052  * Here's a more complicated way to open a popup on a map:
 
6055  * var popup = L.popup()
 
6056  *      .setLatLng(latlng)
 
6057  *      .setContent('<p>Hello world!<br />This is a nice popup.</p>')
 
6064 L.Popup = L.DivOverlay.extend({
 
6067         // @aka Popup options
 
6069                 // @option maxWidth: Number = 300
 
6070                 // Max width of the popup, in pixels.
 
6073                 // @option minWidth: Number = 50
 
6074                 // Min width of the popup, in pixels.
 
6077                 // @option maxHeight: Number = null
 
6078                 // If set, creates a scrollable container of the given height
 
6079                 // inside a popup if its content exceeds it.
 
6082                 // @option autoPan: Boolean = true
 
6083                 // Set it to `false` if you don't want the map to do panning animation
 
6084                 // to fit the opened popup.
 
6087                 // @option autoPanPaddingTopLeft: Point = null
 
6088                 // The margin between the popup and the top left corner of the map
 
6089                 // view after autopanning was performed.
 
6090                 autoPanPaddingTopLeft: null,
 
6092                 // @option autoPanPaddingBottomRight: Point = null
 
6093                 // The margin between the popup and the bottom right corner of the map
 
6094                 // view after autopanning was performed.
 
6095                 autoPanPaddingBottomRight: null,
 
6097                 // @option autoPanPadding: Point = Point(5, 5)
 
6098                 // Equivalent of setting both top left and bottom right autopan padding to the same value.
 
6099                 autoPanPadding: [5, 5],
 
6101                 // @option keepInView: Boolean = false
 
6102                 // Set it to `true` if you want to prevent users from panning the popup
 
6103                 // off of the screen while it is open.
 
6106                 // @option closeButton: Boolean = true
 
6107                 // Controls the presence of a close button in the popup.
 
6110                 // @option autoClose: Boolean = true
 
6111                 // Set it to `false` if you want to override the default behavior of
 
6112                 // the popup closing when user clicks the map (set globally by
 
6113                 // the Map's [closePopupOnClick](#map-closepopuponclick) option).
 
6116                 // @option className: String = ''
 
6117                 // A custom CSS class name to assign to the popup.
 
6122         // @method openOn(map: Map): this
 
6123         // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
 
6124         openOn: function (map) {
 
6125                 map.openPopup(this);
 
6129         onAdd: function (map) {
 
6130                 L.DivOverlay.prototype.onAdd.call(this, map);
 
6133                 // @section Popup events
 
6134                 // @event popupopen: PopupEvent
 
6135                 // Fired when a popup is opened in the map
 
6136                 map.fire('popupopen', {popup: this});
 
6140                         // @section Popup events
 
6141                         // @event popupopen: PopupEvent
 
6142                         // Fired when a popup bound to this layer is opened
 
6143                         this._source.fire('popupopen', {popup: this}, true);
 
6144                         // For non-path layers, we toggle the popup when clicking
 
6145                         // again the layer, so prevent the map to reopen it.
 
6146                         if (!(this._source instanceof L.Path)) {
 
6147                                 this._source.on('preclick', L.DomEvent.stopPropagation);
 
6152         onRemove: function (map) {
 
6153                 L.DivOverlay.prototype.onRemove.call(this, map);
 
6156                 // @section Popup events
 
6157                 // @event popupclose: PopupEvent
 
6158                 // Fired when a popup in the map is closed
 
6159                 map.fire('popupclose', {popup: this});
 
6163                         // @section Popup events
 
6164                         // @event popupclose: PopupEvent
 
6165                         // Fired when a popup bound to this layer is closed
 
6166                         this._source.fire('popupclose', {popup: this}, true);
 
6167                         if (!(this._source instanceof L.Path)) {
 
6168                                 this._source.off('preclick', L.DomEvent.stopPropagation);
 
6173         getEvents: function () {
 
6174                 var events = L.DivOverlay.prototype.getEvents.call(this);
 
6176                 if ('closeOnClick' in this.options ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
 
6177                         events.preclick = this._close;
 
6180                 if (this.options.keepInView) {
 
6181                         events.moveend = this._adjustPan;
 
6187         _close: function () {
 
6189                         this._map.closePopup(this);
 
6193         _initLayout: function () {
 
6194                 var prefix = 'leaflet-popup',
 
6195                     container = this._container = L.DomUtil.create('div',
 
6196                         prefix + ' ' + (this.options.className || '') +
 
6197                         ' leaflet-zoom-animated');
 
6199                 if (this.options.closeButton) {
 
6200                         var closeButton = this._closeButton = L.DomUtil.create('a', prefix + '-close-button', container);
 
6201                         closeButton.href = '#close';
 
6202                         closeButton.innerHTML = '×';
 
6204                         L.DomEvent.on(closeButton, 'click', this._onCloseButtonClick, this);
 
6207                 var wrapper = this._wrapper = L.DomUtil.create('div', prefix + '-content-wrapper', container);
 
6208                 this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper);
 
6211                         .disableClickPropagation(wrapper)
 
6212                         .disableScrollPropagation(this._contentNode)
 
6213                         .on(wrapper, 'contextmenu', L.DomEvent.stopPropagation);
 
6215                 this._tipContainer = L.DomUtil.create('div', prefix + '-tip-container', container);
 
6216                 this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer);
 
6219         _updateLayout: function () {
 
6220                 var container = this._contentNode,
 
6221                     style = container.style;
 
6224                 style.whiteSpace = 'nowrap';
 
6226                 var width = container.offsetWidth;
 
6227                 width = Math.min(width, this.options.maxWidth);
 
6228                 width = Math.max(width, this.options.minWidth);
 
6230                 style.width = (width + 1) + 'px';
 
6231                 style.whiteSpace = '';
 
6235                 var height = container.offsetHeight,
 
6236                     maxHeight = this.options.maxHeight,
 
6237                     scrolledClass = 'leaflet-popup-scrolled';
 
6239                 if (maxHeight && height > maxHeight) {
 
6240                         style.height = maxHeight + 'px';
 
6241                         L.DomUtil.addClass(container, scrolledClass);
 
6243                         L.DomUtil.removeClass(container, scrolledClass);
 
6246                 this._containerWidth = this._container.offsetWidth;
 
6249         _animateZoom: function (e) {
 
6250                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
 
6251                     anchor = this._getAnchor();
 
6252                 L.DomUtil.setPosition(this._container, pos.add(anchor));
 
6255         _adjustPan: function () {
 
6256                 if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; }
 
6258                 var map = this._map,
 
6259                     marginBottom = parseInt(L.DomUtil.getStyle(this._container, 'marginBottom'), 10) || 0,
 
6260                     containerHeight = this._container.offsetHeight + marginBottom,
 
6261                     containerWidth = this._containerWidth,
 
6262                     layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom);
 
6264                 layerPos._add(L.DomUtil.getPosition(this._container));
 
6266                 var containerPos = map.layerPointToContainerPoint(layerPos),
 
6267                     padding = L.point(this.options.autoPanPadding),
 
6268                     paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding),
 
6269                     paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding),
 
6270                     size = map.getSize(),
 
6274                 if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
 
6275                         dx = containerPos.x + containerWidth - size.x + paddingBR.x;
 
6277                 if (containerPos.x - dx - paddingTL.x < 0) { // left
 
6278                         dx = containerPos.x - paddingTL.x;
 
6280                 if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
 
6281                         dy = containerPos.y + containerHeight - size.y + paddingBR.y;
 
6283                 if (containerPos.y - dy - paddingTL.y < 0) { // top
 
6284                         dy = containerPos.y - paddingTL.y;
 
6288                 // @section Popup events
 
6289                 // @event autopanstart: Event
 
6290                 // Fired when the map starts autopanning when opening a popup.
 
6293                             .fire('autopanstart')
 
6298         _onCloseButtonClick: function (e) {
 
6303         _getAnchor: function () {
 
6304                 // Where should we anchor the popup on the source layer?
 
6305                 return L.point(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
 
6311 // @factory L.popup(options?: Popup options, source?: Layer)
 
6312 // 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.
 
6313 L.popup = function (options, source) {
 
6314         return new L.Popup(options, source);
 
6319  * @section Interaction Options
 
6320  * @option closePopupOnClick: Boolean = true
 
6321  * Set it to `false` if you don't want popups to close when user clicks the map.
 
6323 L.Map.mergeOptions({
 
6324         closePopupOnClick: true
 
6329 // @section Methods for Layers and Controls
 
6331         // @method openPopup(popup: Popup): this
 
6332         // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
 
6334         // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
 
6335         // Creates a popup with the specified content and options and opens it in the given point on a map.
 
6336         openPopup: function (popup, latlng, options) {
 
6337                 if (!(popup instanceof L.Popup)) {
 
6338                         popup = new L.Popup(options).setContent(popup);
 
6342                         popup.setLatLng(latlng);
 
6345                 if (this.hasLayer(popup)) {
 
6349                 if (this._popup && this._popup.options.autoClose) {
 
6353                 this._popup = popup;
 
6354                 return this.addLayer(popup);
 
6357         // @method closePopup(popup?: Popup): this
 
6358         // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
 
6359         closePopup: function (popup) {
 
6360                 if (!popup || popup === this._popup) {
 
6361                         popup = this._popup;
 
6365                         this.removeLayer(popup);
 
6375  * @section Popup methods example
 
6377  * All layers share a set of methods convenient for binding popups to it.
 
6380  * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
 
6381  * layer.openPopup();
 
6382  * layer.closePopup();
 
6385  * 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.
 
6388 // @section Popup methods
 
6391         // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
 
6392         // Binds a popup to the layer with the passed `content` and sets up the
 
6393         // neccessary event listeners. If a `Function` is passed it will receive
 
6394         // the layer as the first argument and should return a `String` or `HTMLElement`.
 
6395         bindPopup: function (content, options) {
 
6397                 if (content instanceof L.Popup) {
 
6398                         L.setOptions(content, options);
 
6399                         this._popup = content;
 
6400                         content._source = this;
 
6402                         if (!this._popup || options) {
 
6403                                 this._popup = new L.Popup(options, this);
 
6405                         this._popup.setContent(content);
 
6408                 if (!this._popupHandlersAdded) {
 
6410                                 click: this._openPopup,
 
6411                                 remove: this.closePopup,
 
6412                                 move: this._movePopup
 
6414                         this._popupHandlersAdded = true;
 
6420         // @method unbindPopup(): this
 
6421         // Removes the popup previously bound with `bindPopup`.
 
6422         unbindPopup: function () {
 
6425                                 click: this._openPopup,
 
6426                                 remove: this.closePopup,
 
6427                                 move: this._movePopup
 
6429                         this._popupHandlersAdded = false;
 
6435         // @method openPopup(latlng?: LatLng): this
 
6436         // Opens the bound popup at the specificed `latlng` or at the default popup anchor if no `latlng` is passed.
 
6437         openPopup: function (layer, latlng) {
 
6438                 if (!(layer instanceof L.Layer)) {
 
6443                 if (layer instanceof L.FeatureGroup) {
 
6444                         for (var id in this._layers) {
 
6445                                 layer = this._layers[id];
 
6451                         latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
 
6454                 if (this._popup && this._map) {
 
6455                         // set popup source to this layer
 
6456                         this._popup._source = layer;
 
6458                         // update the popup (content, layout, ect...)
 
6459                         this._popup.update();
 
6461                         // open the popup on the map
 
6462                         this._map.openPopup(this._popup, latlng);
 
6468         // @method closePopup(): this
 
6469         // Closes the popup bound to this layer if it is open.
 
6470         closePopup: function () {
 
6472                         this._popup._close();
 
6477         // @method togglePopup(): this
 
6478         // Opens or closes the popup bound to this layer depending on its current state.
 
6479         togglePopup: function (target) {
 
6481                         if (this._popup._map) {
 
6484                                 this.openPopup(target);
 
6490         // @method isPopupOpen(): boolean
 
6491         // Returns `true` if the popup bound to this layer is currently open.
 
6492         isPopupOpen: function () {
 
6493                 return this._popup.isOpen();
 
6496         // @method setPopupContent(content: String|HTMLElement|Popup): this
 
6497         // Sets the content of the popup bound to this layer.
 
6498         setPopupContent: function (content) {
 
6500                         this._popup.setContent(content);
 
6505         // @method getPopup(): Popup
 
6506         // Returns the popup bound to this layer.
 
6507         getPopup: function () {
 
6511         _openPopup: function (e) {
 
6512                 var layer = e.layer || e.target;
 
6522                 // prevent map click
 
6525                 // if this inherits from Path its a vector and we can just
 
6526                 // open the popup at the new location
 
6527                 if (layer instanceof L.Path) {
 
6528                         this.openPopup(e.layer || e.target, e.latlng);
 
6532                 // otherwise treat it like a marker and figure out
 
6533                 // if we should toggle it open/closed
 
6534                 if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
 
6537                         this.openPopup(layer, e.latlng);
 
6541         _movePopup: function (e) {
 
6542                 this._popup.setLatLng(e.latlng);
 
6549  * Popup extension to L.Marker, adding popup-related methods.
 
6553         _getPopupAnchor: function () {
 
6554                 return this.options.icon.options.popupAnchor || [0, 0];
 
6562  * @inherits DivOverlay
 
6564  * Used to display small texts on top of map layers.
 
6569  * marker.bindTooltip("my tooltip text").openTooltip();
 
6571  * Note about tooltip offset. Leaflet takes two options in consideration
 
6572  * for computing tooltip offseting:
 
6573  * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
 
6574  *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
 
6575  *   move it to the bottom. Negatives will move to the left and top.
 
6576  * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
 
6577  *   should adapt this value if you use a custom icon.
 
6581 // @namespace Tooltip
 
6582 L.Tooltip = L.DivOverlay.extend({
 
6585         // @aka Tooltip options
 
6587                 // @option pane: String = 'tooltipPane'
 
6588                 // `Map pane` where the tooltip will be added.
 
6589                 pane: 'tooltipPane',
 
6591                 // @option offset: Point = Point(0, 0)
 
6592                 // Optional offset of the tooltip position.
 
6595                 // @option direction: String = 'auto'
 
6596                 // Direction where to open the tooltip. Possible values are: `right`, `left`,
 
6597                 // `top`, `bottom`, `center`, `auto`.
 
6598                 // `auto` will dynamicaly switch between `right` and `left` according to the tooltip
 
6599                 // position on the map.
 
6602                 // @option permanent: Boolean = false
 
6603                 // Whether to open the tooltip permanently or only on mouseover.
 
6606                 // @option sticky: Boolean = false
 
6607                 // If true, the tooltip will follow the mouse instead of being fixed at the feature center.
 
6610                 // @option interactive: Boolean = false
 
6611                 // If true, the tooltip will listen to the feature events.
 
6614                 // @option opacity: Number = 0.9
 
6615                 // Tooltip container opacity.
 
6619         onAdd: function (map) {
 
6620                 L.DivOverlay.prototype.onAdd.call(this, map);
 
6621                 this.setOpacity(this.options.opacity);
 
6624                 // @section Tooltip events
 
6625                 // @event tooltipopen: TooltipEvent
 
6626                 // Fired when a tooltip is opened in the map.
 
6627                 map.fire('tooltipopen', {tooltip: this});
 
6631                         // @section Tooltip events
 
6632                         // @event tooltipopen: TooltipEvent
 
6633                         // Fired when a tooltip bound to this layer is opened.
 
6634                         this._source.fire('tooltipopen', {tooltip: this}, true);
 
6638         onRemove: function (map) {
 
6639                 L.DivOverlay.prototype.onRemove.call(this, map);
 
6642                 // @section Tooltip events
 
6643                 // @event tooltipclose: TooltipEvent
 
6644                 // Fired when a tooltip in the map is closed.
 
6645                 map.fire('tooltipclose', {tooltip: this});
 
6649                         // @section Tooltip events
 
6650                         // @event tooltipclose: TooltipEvent
 
6651                         // Fired when a tooltip bound to this layer is closed.
 
6652                         this._source.fire('tooltipclose', {tooltip: this}, true);
 
6656         getEvents: function () {
 
6657                 var events = L.DivOverlay.prototype.getEvents.call(this);
 
6659                 if (L.Browser.touch && !this.options.permanent) {
 
6660                         events.preclick = this._close;
 
6666         _close: function () {
 
6668                         this._map.closeTooltip(this);
 
6672         _initLayout: function () {
 
6673                 var prefix = 'leaflet-tooltip',
 
6674                     className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
 
6676                 this._contentNode = this._container = L.DomUtil.create('div', className);
 
6679         _updateLayout: function () {},
 
6681         _adjustPan: function () {},
 
6683         _setPosition: function (pos) {
 
6684                 var map = this._map,
 
6685                     container = this._container,
 
6686                     centerPoint = map.latLngToContainerPoint(map.getCenter()),
 
6687                     tooltipPoint = map.layerPointToContainerPoint(pos),
 
6688                     direction = this.options.direction,
 
6689                     tooltipWidth = container.offsetWidth,
 
6690                     tooltipHeight = container.offsetHeight,
 
6691                     offset = L.point(this.options.offset),
 
6692                     anchor = this._getAnchor();
 
6694                 if (direction === 'top') {
 
6695                         pos = pos.add(L.point(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y));
 
6696                 } else if (direction === 'bottom') {
 
6697                         pos = pos.subtract(L.point(tooltipWidth / 2 - offset.x, -offset.y));
 
6698                 } else if (direction === 'center') {
 
6699                         pos = pos.subtract(L.point(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y));
 
6700                 } else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
 
6701                         direction = 'right';
 
6702                         pos = pos.add([offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y]);
 
6705                         pos = pos.subtract(L.point(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y));
 
6708                 L.DomUtil.removeClass(container, 'leaflet-tooltip-right');
 
6709                 L.DomUtil.removeClass(container, 'leaflet-tooltip-left');
 
6710                 L.DomUtil.removeClass(container, 'leaflet-tooltip-top');
 
6711                 L.DomUtil.removeClass(container, 'leaflet-tooltip-bottom');
 
6712                 L.DomUtil.addClass(container, 'leaflet-tooltip-' + direction);
 
6713                 L.DomUtil.setPosition(container, pos);
 
6716         _updatePosition: function () {
 
6717                 var pos = this._map.latLngToLayerPoint(this._latlng);
 
6718                 this._setPosition(pos);
 
6721         setOpacity: function (opacity) {
 
6722                 this.options.opacity = opacity;
 
6724                 if (this._container) {
 
6725                         L.DomUtil.setOpacity(this._container, opacity);
 
6729         _animateZoom: function (e) {
 
6730                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
 
6731                 this._setPosition(pos);
 
6734         _getAnchor: function () {
 
6735                 // Where should we anchor the tooltip on the source layer?
 
6736                 return L.point(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
 
6741 // @namespace Tooltip
 
6742 // @factory L.tooltip(options?: Tooltip options, source?: Layer)
 
6743 // 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.
 
6744 L.tooltip = function (options, source) {
 
6745         return new L.Tooltip(options, source);
 
6749 // @section Methods for Layers and Controls
 
6752         // @method openTooltip(tooltip: Tooltip): this
 
6753         // Opens the specified tooltip.
 
6755         // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
 
6756         // Creates a tooltip with the specified content and options and open it.
 
6757         openTooltip: function (tooltip, latlng, options) {
 
6758                 if (!(tooltip instanceof L.Tooltip)) {
 
6759                         tooltip = new L.Tooltip(options).setContent(tooltip);
 
6763                         tooltip.setLatLng(latlng);
 
6766                 if (this.hasLayer(tooltip)) {
 
6770                 return this.addLayer(tooltip);
 
6773         // @method closeTooltip(tooltip?: Tooltip): this
 
6774         // Closes the tooltip given as parameter.
 
6775         closeTooltip: function (tooltip) {
 
6777                         this.removeLayer(tooltip);
 
6788  * @section Tooltip methods example
 
6790  * All layers share a set of methods convenient for binding tooltips to it.
 
6793  * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
 
6794  * layer.openTooltip();
 
6795  * layer.closeTooltip();
 
6799 // @section Tooltip methods
 
6802         // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
 
6803         // Binds a tooltip to the layer with the passed `content` and sets up the
 
6804         // neccessary event listeners. If a `Function` is passed it will receive
 
6805         // the layer as the first argument and should return a `String` or `HTMLElement`.
 
6806         bindTooltip: function (content, options) {
 
6808                 if (content instanceof L.Tooltip) {
 
6809                         L.setOptions(content, options);
 
6810                         this._tooltip = content;
 
6811                         content._source = this;
 
6813                         if (!this._tooltip || options) {
 
6814                                 this._tooltip = L.tooltip(options, this);
 
6816                         this._tooltip.setContent(content);
 
6820                 this._initTooltipInteractions();
 
6822                 if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
 
6829         // @method unbindTooltip(): this
 
6830         // Removes the tooltip previously bound with `bindTooltip`.
 
6831         unbindTooltip: function () {
 
6832                 if (this._tooltip) {
 
6833                         this._initTooltipInteractions(true);
 
6834                         this.closeTooltip();
 
6835                         this._tooltip = null;
 
6840         _initTooltipInteractions: function (remove) {
 
6841                 if (!remove && this._tooltipHandlersAdded) { return; }
 
6842                 var onOff = remove ? 'off' : 'on',
 
6844                         remove: this.closeTooltip,
 
6845                         move: this._moveTooltip
 
6847                 if (!this._tooltip.options.permanent) {
 
6848                         events.mouseover = this._openTooltip;
 
6849                         events.mouseout = this.closeTooltip;
 
6850                         if (this._tooltip.options.sticky) {
 
6851                                 events.mousemove = this._moveTooltip;
 
6853                         if (L.Browser.touch) {
 
6854                                 events.click = this._openTooltip;
 
6857                         events.add = this._openTooltip;
 
6859                 this[onOff](events);
 
6860                 this._tooltipHandlersAdded = !remove;
 
6863         // @method openTooltip(latlng?: LatLng): this
 
6864         // Opens the bound tooltip at the specificed `latlng` or at the default tooltip anchor if no `latlng` is passed.
 
6865         openTooltip: function (layer, latlng) {
 
6866                 if (!(layer instanceof L.Layer)) {
 
6871                 if (layer instanceof L.FeatureGroup) {
 
6872                         for (var id in this._layers) {
 
6873                                 layer = this._layers[id];
 
6879                         latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
 
6882                 if (this._tooltip && this._map) {
 
6884                         // set tooltip source to this layer
 
6885                         this._tooltip._source = layer;
 
6887                         // update the tooltip (content, layout, ect...)
 
6888                         this._tooltip.update();
 
6890                         // open the tooltip on the map
 
6891                         this._map.openTooltip(this._tooltip, latlng);
 
6893                         // Tooltip container may not be defined if not permanent and never
 
6895                         if (this._tooltip.options.interactive && this._tooltip._container) {
 
6896                                 L.DomUtil.addClass(this._tooltip._container, 'leaflet-clickable');
 
6897                                 this.addInteractiveTarget(this._tooltip._container);
 
6904         // @method closeTooltip(): this
 
6905         // Closes the tooltip bound to this layer if it is open.
 
6906         closeTooltip: function () {
 
6907                 if (this._tooltip) {
 
6908                         this._tooltip._close();
 
6909                         if (this._tooltip.options.interactive && this._tooltip._container) {
 
6910                                 L.DomUtil.removeClass(this._tooltip._container, 'leaflet-clickable');
 
6911                                 this.removeInteractiveTarget(this._tooltip._container);
 
6917         // @method toggleTooltip(): this
 
6918         // Opens or closes the tooltip bound to this layer depending on its current state.
 
6919         toggleTooltip: function (target) {
 
6920                 if (this._tooltip) {
 
6921                         if (this._tooltip._map) {
 
6922                                 this.closeTooltip();
 
6924                                 this.openTooltip(target);
 
6930         // @method isTooltipOpen(): boolean
 
6931         // Returns `true` if the tooltip bound to this layer is currently open.
 
6932         isTooltipOpen: function () {
 
6933                 return this._tooltip.isOpen();
 
6936         // @method setTooltipContent(content: String|HTMLElement|Tooltip): this
 
6937         // Sets the content of the tooltip bound to this layer.
 
6938         setTooltipContent: function (content) {
 
6939                 if (this._tooltip) {
 
6940                         this._tooltip.setContent(content);
 
6945         // @method getTooltip(): Tooltip
 
6946         // Returns the tooltip bound to this layer.
 
6947         getTooltip: function () {
 
6948                 return this._tooltip;
 
6951         _openTooltip: function (e) {
 
6952                 var layer = e.layer || e.target;
 
6954                 if (!this._tooltip || !this._map) {
 
6957                 this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
 
6960         _moveTooltip: function (e) {
 
6961                 var latlng = e.latlng, containerPoint, layerPoint;
 
6962                 if (this._tooltip.options.sticky && e.originalEvent) {
 
6963                         containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
 
6964                         layerPoint = this._map.containerPointToLayerPoint(containerPoint);
 
6965                         latlng = this._map.layerPointToLatLng(layerPoint);
 
6967                 this._tooltip.setLatLng(latlng);
 
6974  * Tooltip extension to L.Marker, adding tooltip-related methods.
 
6978         _getTooltipAnchor: function () {
 
6979                 return this.options.icon.options.tooltipAnchor || [0, 0];
 
6990  * Used to group several layers and handle them as one. If you add it to the map,
 
6991  * any layers added or removed from the group will be added/removed on the map as
 
6992  * well. Extends `Layer`.
 
6997  * L.layerGroup([marker1, marker2])
 
6998  *      .addLayer(polyline)
 
7003 L.LayerGroup = L.Layer.extend({
 
7005         initialize: function (layers) {
 
7011                         for (i = 0, len = layers.length; i < len; i++) {
 
7012                                 this.addLayer(layers[i]);
 
7017         // @method addLayer(layer: Layer): this
 
7018         // Adds the given layer to the group.
 
7019         addLayer: function (layer) {
 
7020                 var id = this.getLayerId(layer);
 
7022                 this._layers[id] = layer;
 
7025                         this._map.addLayer(layer);
 
7031         // @method removeLayer(layer: Layer): this
 
7032         // Removes the given layer from the group.
 
7034         // @method removeLayer(id: Number): this
 
7035         // Removes the layer with the given internal ID from the group.
 
7036         removeLayer: function (layer) {
 
7037                 var id = layer in this._layers ? layer : this.getLayerId(layer);
 
7039                 if (this._map && this._layers[id]) {
 
7040                         this._map.removeLayer(this._layers[id]);
 
7043                 delete this._layers[id];
 
7048         // @method hasLayer(layer: Layer): Boolean
 
7049         // Returns `true` if the given layer is currently added to the group.
 
7050         hasLayer: function (layer) {
 
7051                 return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
 
7054         // @method clearLayers(): this
 
7055         // Removes all the layers from the group.
 
7056         clearLayers: function () {
 
7057                 for (var i in this._layers) {
 
7058                         this.removeLayer(this._layers[i]);
 
7063         // @method invoke(methodName: String, …): this
 
7064         // Calls `methodName` on every layer contained in this group, passing any
 
7065         // additional parameters. Has no effect if the layers contained do not
 
7066         // implement `methodName`.
 
7067         invoke: function (methodName) {
 
7068                 var args = Array.prototype.slice.call(arguments, 1),
 
7071                 for (i in this._layers) {
 
7072                         layer = this._layers[i];
 
7074                         if (layer[methodName]) {
 
7075                                 layer[methodName].apply(layer, args);
 
7082         onAdd: function (map) {
 
7083                 for (var i in this._layers) {
 
7084                         map.addLayer(this._layers[i]);
 
7088         onRemove: function (map) {
 
7089                 for (var i in this._layers) {
 
7090                         map.removeLayer(this._layers[i]);
 
7094         // @method eachLayer(fn: Function, context?: Object): this
 
7095         // Iterates over the layers of the group, optionally specifying context of the iterator function.
 
7097         // group.eachLayer(function (layer) {
 
7098         //      layer.bindPopup('Hello');
 
7101         eachLayer: function (method, context) {
 
7102                 for (var i in this._layers) {
 
7103                         method.call(context, this._layers[i]);
 
7108         // @method getLayer(id: Number): Layer
 
7109         // Returns the layer with the given internal ID.
 
7110         getLayer: function (id) {
 
7111                 return this._layers[id];
 
7114         // @method getLayers(): Layer[]
 
7115         // Returns an array of all the layers added to the group.
 
7116         getLayers: function () {
 
7119                 for (var i in this._layers) {
 
7120                         layers.push(this._layers[i]);
 
7125         // @method setZIndex(zIndex: Number): this
 
7126         // Calls `setZIndex` on every layer contained in this group, passing the z-index.
 
7127         setZIndex: function (zIndex) {
 
7128                 return this.invoke('setZIndex', zIndex);
 
7131         // @method getLayerId(layer: Layer): Number
 
7132         // Returns the internal ID for a layer
 
7133         getLayerId: function (layer) {
 
7134                 return L.stamp(layer);
 
7139 // @factory L.layerGroup(layers: Layer[])
 
7140 // Create a layer group, optionally given an initial set of layers.
 
7141 L.layerGroup = function (layers) {
 
7142         return new L.LayerGroup(layers);
 
7148  * @class FeatureGroup
 
7149  * @aka L.FeatureGroup
 
7150  * @inherits LayerGroup
 
7152  * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
 
7153  *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
 
7154  *  * Events are propagated to the `FeatureGroup`, so if the group has an event
 
7155  * handler, it will handle events from any of the layers. This includes mouse events
 
7156  * and custom events.
 
7157  *  * Has `layeradd` and `layerremove` events
 
7162  * L.featureGroup([marker1, marker2, polyline])
 
7163  *      .bindPopup('Hello world!')
 
7164  *      .on('click', function() { alert('Clicked on a member of the group!'); })
 
7169 L.FeatureGroup = L.LayerGroup.extend({
 
7171         addLayer: function (layer) {
 
7172                 if (this.hasLayer(layer)) {
 
7176                 layer.addEventParent(this);
 
7178                 L.LayerGroup.prototype.addLayer.call(this, layer);
 
7180                 // @event layeradd: LayerEvent
 
7181                 // Fired when a layer is added to this `FeatureGroup`
 
7182                 return this.fire('layeradd', {layer: layer});
 
7185         removeLayer: function (layer) {
 
7186                 if (!this.hasLayer(layer)) {
 
7189                 if (layer in this._layers) {
 
7190                         layer = this._layers[layer];
 
7193                 layer.removeEventParent(this);
 
7195                 L.LayerGroup.prototype.removeLayer.call(this, layer);
 
7197                 // @event layerremove: LayerEvent
 
7198                 // Fired when a layer is removed from this `FeatureGroup`
 
7199                 return this.fire('layerremove', {layer: layer});
 
7202         // @method setStyle(style: Path options): this
 
7203         // Sets the given path options to each layer of the group that has a `setStyle` method.
 
7204         setStyle: function (style) {
 
7205                 return this.invoke('setStyle', style);
 
7208         // @method bringToFront(): this
 
7209         // Brings the layer group to the top of all other layers
 
7210         bringToFront: function () {
 
7211                 return this.invoke('bringToFront');
 
7214         // @method bringToBack(): this
 
7215         // Brings the layer group to the top of all other layers
 
7216         bringToBack: function () {
 
7217                 return this.invoke('bringToBack');
 
7220         // @method getBounds(): LatLngBounds
 
7221         // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
 
7222         getBounds: function () {
 
7223                 var bounds = new L.LatLngBounds();
 
7225                 for (var id in this._layers) {
 
7226                         var layer = this._layers[id];
 
7227                         bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
 
7233 // @factory L.featureGroup(layers: Layer[])
 
7234 // Create a feature group, optionally given an initial set of layers.
 
7235 L.featureGroup = function (layers) {
 
7236         return new L.FeatureGroup(layers);
 
7246  * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
 
7247  * DOM container of the renderer, its bounds, and its zoom animation.
 
7249  * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
 
7250  * itself can be added or removed to the map. All paths use a renderer, which can
 
7251  * be implicit (the map will decide the type of renderer and use it automatically)
 
7252  * or explicit (using the [`renderer`](#path-renderer) option of the path).
 
7254  * Do not use this class directly, use `SVG` and `Canvas` instead.
 
7256  * @event update: Event
 
7257  * Fired when the renderer updates its bounds, center and zoom, for example when
 
7261 L.Renderer = L.Layer.extend({
 
7264         // @aka Renderer options
 
7266                 // @option padding: Number = 0.1
 
7267                 // How much to extend the clip area around the map view (relative to its size)
 
7268                 // e.g. 0.1 would be 10% of map view in each direction
 
7272         initialize: function (options) {
 
7273                 L.setOptions(this, options);
 
7277         onAdd: function () {
 
7278                 if (!this._container) {
 
7279                         this._initContainer(); // defined by renderer implementations
 
7281                         if (this._zoomAnimated) {
 
7282                                 L.DomUtil.addClass(this._container, 'leaflet-zoom-animated');
 
7286                 this.getPane().appendChild(this._container);
 
7290         onRemove: function () {
 
7291                 L.DomUtil.remove(this._container);
 
7294         getEvents: function () {
 
7296                         viewreset: this._reset,
 
7298                         moveend: this._update
 
7300                 if (this._zoomAnimated) {
 
7301                         events.zoomanim = this._onAnimZoom;
 
7306         _onAnimZoom: function (ev) {
 
7307                 this._updateTransform(ev.center, ev.zoom);
 
7310         _onZoom: function () {
 
7311                 this._updateTransform(this._map.getCenter(), this._map.getZoom());
 
7314         _updateTransform: function (center, zoom) {
 
7315                 var scale = this._map.getZoomScale(zoom, this._zoom),
 
7316                     position = L.DomUtil.getPosition(this._container),
 
7317                     viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
 
7318                     currentCenterPoint = this._map.project(this._center, zoom),
 
7319                     destCenterPoint = this._map.project(center, zoom),
 
7320                     centerOffset = destCenterPoint.subtract(currentCenterPoint),
 
7322                     topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
 
7324                 if (L.Browser.any3d) {
 
7325                         L.DomUtil.setTransform(this._container, topLeftOffset, scale);
 
7327                         L.DomUtil.setPosition(this._container, topLeftOffset);
 
7331         _reset: function () {
 
7333                 this._updateTransform(this._center, this._zoom);
 
7336         _update: function () {
 
7337                 // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
 
7338                 // Subclasses are responsible of firing the 'update' event.
 
7339                 var p = this.options.padding,
 
7340                     size = this._map.getSize(),
 
7341                     min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
 
7343                 this._bounds = new L.Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
 
7345                 this._center = this._map.getCenter();
 
7346                 this._zoom = this._map.getZoom();
 
7352         // @namespace Map; @method getRenderer(layer: Path): Renderer
 
7353         // Returns the instance of `Renderer` that should be used to render the given
 
7354         // `Path`. It will ensure that the `renderer` options of the map and paths
 
7355         // are respected, and that the renderers do exist on the map.
 
7356         getRenderer: function (layer) {
 
7357                 // @namespace Path; @option renderer: Renderer
 
7358                 // Use this specific instance of `Renderer` for this path. Takes
 
7359                 // precedence over the map's [default renderer](#map-renderer).
 
7360                 var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
 
7363                         // @namespace Map; @option preferCanvas: Boolean = false
 
7364                         // Whether `Path`s should be rendered on a `Canvas` renderer.
 
7365                         // By default, all `Path`s are rendered in a `SVG` renderer.
 
7366                         renderer = this._renderer = (this.options.preferCanvas && L.canvas()) || L.svg();
 
7369                 if (!this.hasLayer(renderer)) {
 
7370                         this.addLayer(renderer);
 
7375         _getPaneRenderer: function (name) {
 
7376                 if (name === 'overlayPane' || name === undefined) {
 
7380                 var renderer = this._paneRenderers[name];
 
7381                 if (renderer === undefined) {
 
7382                         renderer = (L.SVG && L.svg({pane: name})) || (L.Canvas && L.canvas({pane: name}));
 
7383                         this._paneRenderers[name] = renderer;
 
7394  * @inherits Interactive layer
 
7396  * An abstract class that contains options and constants shared between vector
 
7397  * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
 
7400 L.Path = L.Layer.extend({
 
7403         // @aka Path options
 
7405                 // @option stroke: Boolean = true
 
7406                 // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
 
7409                 // @option color: String = '#3388ff'
 
7413                 // @option weight: Number = 3
 
7414                 // Stroke width in pixels
 
7417                 // @option opacity: Number = 1.0
 
7421                 // @option lineCap: String= 'round'
 
7422                 // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
 
7425                 // @option lineJoin: String = 'round'
 
7426                 // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
 
7429                 // @option dashArray: String = null
 
7430                 // 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).
 
7433                 // @option dashOffset: String = null
 
7434                 // 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).
 
7437                 // @option fill: Boolean = depends
 
7438                 // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
 
7441                 // @option fillColor: String = *
 
7442                 // Fill color. Defaults to the value of the [`color`](#path-color) option
 
7445                 // @option fillOpacity: Number = 0.2
 
7449                 // @option fillRule: String = 'evenodd'
 
7450                 // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
 
7451                 fillRule: 'evenodd',
 
7455                 // Option inherited from "Interactive layer" abstract class
 
7459         beforeAdd: function (map) {
 
7460                 // Renderer is set here because we need to call renderer.getEvents
 
7461                 // before this.getEvents.
 
7462                 this._renderer = map.getRenderer(this);
 
7465         onAdd: function () {
 
7466                 this._renderer._initPath(this);
 
7468                 this._renderer._addPath(this);
 
7469                 this._renderer.on('update', this._update, this);
 
7472         onRemove: function () {
 
7473                 this._renderer._removePath(this);
 
7474                 this._renderer.off('update', this._update, this);
 
7477         getEvents: function () {
 
7479                         zoomend: this._project,
 
7480                         viewreset: this._reset
 
7484         // @method redraw(): this
 
7485         // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
 
7486         redraw: function () {
 
7488                         this._renderer._updatePath(this);
 
7493         // @method setStyle(style: Path options): this
 
7494         // Changes the appearance of a Path based on the options in the `Path options` object.
 
7495         setStyle: function (style) {
 
7496                 L.setOptions(this, style);
 
7497                 if (this._renderer) {
 
7498                         this._renderer._updateStyle(this);
 
7503         // @method bringToFront(): this
 
7504         // Brings the layer to the top of all path layers.
 
7505         bringToFront: function () {
 
7506                 if (this._renderer) {
 
7507                         this._renderer._bringToFront(this);
 
7512         // @method bringToBack(): this
 
7513         // Brings the layer to the bottom of all path layers.
 
7514         bringToBack: function () {
 
7515                 if (this._renderer) {
 
7516                         this._renderer._bringToBack(this);
 
7521         getElement: function () {
 
7525         _reset: function () {
 
7526                 // defined in children classes
 
7531         _clickTolerance: function () {
 
7532                 // used when doing hit detection for Canvas layers
 
7533                 return (this.options.stroke ? this.options.weight / 2 : 0) + (L.Browser.touch ? 10 : 0);
 
7540  * @namespace LineUtil
 
7542  * Various utility functions for polyine points processing, used by Leaflet internally to make polylines lightning-fast.
 
7547         // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
 
7548         // Improves rendering performance dramatically by lessening the number of points to draw.
 
7550         // @function simplify(points: Point[], tolerance: Number): Point[]
 
7551         // Dramatically reduces the number of points in a polyline while retaining
 
7552         // its shape and returns a new array of simplified points, using the
 
7553         // [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
 
7554         // Used for a huge performance boost when processing/displaying Leaflet polylines for
 
7555         // each zoom level and also reducing visual noise. tolerance affects the amount of
 
7556         // simplification (lesser value means higher quality but slower and with more points).
 
7557         // Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
 
7558         simplify: function (points, tolerance) {
 
7559                 if (!tolerance || !points.length) {
 
7560                         return points.slice();
 
7563                 var sqTolerance = tolerance * tolerance;
 
7565                 // stage 1: vertex reduction
 
7566                 points = this._reducePoints(points, sqTolerance);
 
7568                 // stage 2: Douglas-Peucker simplification
 
7569                 points = this._simplifyDP(points, sqTolerance);
 
7574         // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
 
7575         // Returns the distance between point `p` and segment `p1` to `p2`.
 
7576         pointToSegmentDistance:  function (p, p1, p2) {
 
7577                 return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true));
 
7580         // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
 
7581         // Returns the closest point from a point `p` on a segment `p1` to `p2`.
 
7582         closestPointOnSegment: function (p, p1, p2) {
 
7583                 return this._sqClosestPointOnSegment(p, p1, p2);
 
7586         // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
 
7587         _simplifyDP: function (points, sqTolerance) {
 
7589                 var len = points.length,
 
7590                     ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
 
7591                     markers = new ArrayConstructor(len);
 
7593                 markers[0] = markers[len - 1] = 1;
 
7595                 this._simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
 
7600                 for (i = 0; i < len; i++) {
 
7602                                 newPoints.push(points[i]);
 
7609         _simplifyDPStep: function (points, markers, sqTolerance, first, last) {
 
7614                 for (i = first + 1; i <= last - 1; i++) {
 
7615                         sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true);
 
7617                         if (sqDist > maxSqDist) {
 
7623                 if (maxSqDist > sqTolerance) {
 
7626                         this._simplifyDPStep(points, markers, sqTolerance, first, index);
 
7627                         this._simplifyDPStep(points, markers, sqTolerance, index, last);
 
7631         // reduce points that are too close to each other to a single point
 
7632         _reducePoints: function (points, sqTolerance) {
 
7633                 var reducedPoints = [points[0]];
 
7635                 for (var i = 1, prev = 0, len = points.length; i < len; i++) {
 
7636                         if (this._sqDist(points[i], points[prev]) > sqTolerance) {
 
7637                                 reducedPoints.push(points[i]);
 
7641                 if (prev < len - 1) {
 
7642                         reducedPoints.push(points[len - 1]);
 
7644                 return reducedPoints;
 
7648         // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
 
7649         // Clips the segment a to b by rectangular bounds with the
 
7650         // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
 
7651         // (modifying the segment points directly!). Used by Leaflet to only show polyline
 
7652         // points that are on the screen or near, increasing performance.
 
7653         clipSegment: function (a, b, bounds, useLastCode, round) {
 
7654                 var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds),
 
7655                     codeB = this._getBitCode(b, bounds),
 
7657                     codeOut, p, newCode;
 
7659                 // save 2nd code to avoid calculating it on the next segment
 
7660                 this._lastCode = codeB;
 
7663                         // if a,b is inside the clip window (trivial accept)
 
7664                         if (!(codeA | codeB)) {
 
7668                         // if a,b is outside the clip window (trivial reject)
 
7669                         if (codeA & codeB) {
 
7674                         codeOut = codeA || codeB;
 
7675                         p = this._getEdgeIntersection(a, b, codeOut, bounds, round);
 
7676                         newCode = this._getBitCode(p, bounds);
 
7678                         if (codeOut === codeA) {
 
7688         _getEdgeIntersection: function (a, b, code, bounds, round) {
 
7695                 if (code & 8) { // top
 
7696                         x = a.x + dx * (max.y - a.y) / dy;
 
7699                 } else if (code & 4) { // bottom
 
7700                         x = a.x + dx * (min.y - a.y) / dy;
 
7703                 } else if (code & 2) { // right
 
7705                         y = a.y + dy * (max.x - a.x) / dx;
 
7707                 } else if (code & 1) { // left
 
7709                         y = a.y + dy * (min.x - a.x) / dx;
 
7712                 return new L.Point(x, y, round);
 
7715         _getBitCode: function (p, bounds) {
 
7718                 if (p.x < bounds.min.x) { // left
 
7720                 } else if (p.x > bounds.max.x) { // right
 
7724                 if (p.y < bounds.min.y) { // bottom
 
7726                 } else if (p.y > bounds.max.y) { // top
 
7733         // square distance (to avoid unnecessary Math.sqrt calls)
 
7734         _sqDist: function (p1, p2) {
 
7735                 var dx = p2.x - p1.x,
 
7737                 return dx * dx + dy * dy;
 
7740         // return closest point on segment or distance to that point
 
7741         _sqClosestPointOnSegment: function (p, p1, p2, sqDist) {
 
7746                     dot = dx * dx + dy * dy,
 
7750                         t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
 
7764                 return sqDist ? dx * dx + dy * dy : new L.Point(x, y);
 
7775  * A class for drawing polyline overlays on a map. Extends `Path`.
 
7780  * // create a red polyline from an array of LatLng points
 
7787  * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
 
7789  * // zoom the map to the polyline
 
7790  * map.fitBounds(polyline.getBounds());
 
7793  * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
 
7796  * // create a red polyline from an array of arrays of LatLng points
 
7798  *      [[-122.68, 45.51],
 
7808 L.Polyline = L.Path.extend({
 
7811         // @aka Polyline options
 
7813                 // @option smoothFactor: Number = 1.0
 
7814                 // How much to simplify the polyline on each zoom level. More means
 
7815                 // better performance and smoother look, and less means more accurate representation.
 
7818                 // @option noClip: Boolean = false
 
7819                 // Disable polyline clipping.
 
7823         initialize: function (latlngs, options) {
 
7824                 L.setOptions(this, options);
 
7825                 this._setLatLngs(latlngs);
 
7828         // @method getLatLngs(): LatLng[]
 
7829         // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
 
7830         getLatLngs: function () {
 
7831                 return this._latlngs;
 
7834         // @method setLatLngs(latlngs: LatLng[]): this
 
7835         // Replaces all the points in the polyline with the given array of geographical points.
 
7836         setLatLngs: function (latlngs) {
 
7837                 this._setLatLngs(latlngs);
 
7838                 return this.redraw();
 
7841         // @method isEmpty(): Boolean
 
7842         // Returns `true` if the Polyline has no LatLngs.
 
7843         isEmpty: function () {
 
7844                 return !this._latlngs.length;
 
7847         closestLayerPoint: function (p) {
 
7848                 var minDistance = Infinity,
 
7850                     closest = L.LineUtil._sqClosestPointOnSegment,
 
7853                 for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
 
7854                         var points = this._parts[j];
 
7856                         for (var i = 1, len = points.length; i < len; i++) {
 
7860                                 var sqDist = closest(p, p1, p2, true);
 
7862                                 if (sqDist < minDistance) {
 
7863                                         minDistance = sqDist;
 
7864                                         minPoint = closest(p, p1, p2);
 
7869                         minPoint.distance = Math.sqrt(minDistance);
 
7874         // @method getCenter(): LatLng
 
7875         // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
 
7876         getCenter: function () {
 
7877                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
7879                         throw new Error('Must add layer to map before using getCenter()');
 
7882                 var i, halfDist, segDist, dist, p1, p2, ratio,
 
7883                     points = this._rings[0],
 
7884                     len = points.length;
 
7886                 if (!len) { return null; }
 
7888                 // polyline centroid algorithm; only uses the first ring if there are multiple
 
7890                 for (i = 0, halfDist = 0; i < len - 1; i++) {
 
7891                         halfDist += points[i].distanceTo(points[i + 1]) / 2;
 
7894                 // The line is so small in the current view that all points are on the same pixel.
 
7895                 if (halfDist === 0) {
 
7896                         return this._map.layerPointToLatLng(points[0]);
 
7899                 for (i = 0, dist = 0; i < len - 1; i++) {
 
7902                         segDist = p1.distanceTo(p2);
 
7905                         if (dist > halfDist) {
 
7906                                 ratio = (dist - halfDist) / segDist;
 
7907                                 return this._map.layerPointToLatLng([
 
7908                                         p2.x - ratio * (p2.x - p1.x),
 
7909                                         p2.y - ratio * (p2.y - p1.y)
 
7915         // @method getBounds(): LatLngBounds
 
7916         // Returns the `LatLngBounds` of the path.
 
7917         getBounds: function () {
 
7918                 return this._bounds;
 
7921         // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
 
7922         // Adds a given point to the polyline. By default, adds to the first ring of
 
7923         // the polyline in case of a multi-polyline, but can be overridden by passing
 
7924         // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
 
7925         addLatLng: function (latlng, latlngs) {
 
7926                 latlngs = latlngs || this._defaultShape();
 
7927                 latlng = L.latLng(latlng);
 
7928                 latlngs.push(latlng);
 
7929                 this._bounds.extend(latlng);
 
7930                 return this.redraw();
 
7933         _setLatLngs: function (latlngs) {
 
7934                 this._bounds = new L.LatLngBounds();
 
7935                 this._latlngs = this._convertLatLngs(latlngs);
 
7938         _defaultShape: function () {
 
7939                 return L.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0];
 
7942         // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
 
7943         _convertLatLngs: function (latlngs) {
 
7945                     flat = L.Polyline._flat(latlngs);
 
7947                 for (var i = 0, len = latlngs.length; i < len; i++) {
 
7949                                 result[i] = L.latLng(latlngs[i]);
 
7950                                 this._bounds.extend(result[i]);
 
7952                                 result[i] = this._convertLatLngs(latlngs[i]);
 
7959         _project: function () {
 
7960                 var pxBounds = new L.Bounds();
 
7962                 this._projectLatlngs(this._latlngs, this._rings, pxBounds);
 
7964                 var w = this._clickTolerance(),
 
7965                     p = new L.Point(w, w);
 
7967                 if (this._bounds.isValid() && pxBounds.isValid()) {
 
7968                         pxBounds.min._subtract(p);
 
7969                         pxBounds.max._add(p);
 
7970                         this._pxBounds = pxBounds;
 
7974         // recursively turns latlngs into a set of rings with projected coordinates
 
7975         _projectLatlngs: function (latlngs, result, projectedBounds) {
 
7976                 var flat = latlngs[0] instanceof L.LatLng,
 
7977                     len = latlngs.length,
 
7982                         for (i = 0; i < len; i++) {
 
7983                                 ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
 
7984                                 projectedBounds.extend(ring[i]);
 
7988                         for (i = 0; i < len; i++) {
 
7989                                 this._projectLatlngs(latlngs[i], result, projectedBounds);
 
7994         // clip polyline by renderer bounds so that we have less to render for performance
 
7995         _clipPoints: function () {
 
7996                 var bounds = this._renderer._bounds;
 
7999                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
8003                 if (this.options.noClip) {
 
8004                         this._parts = this._rings;
 
8008                 var parts = this._parts,
 
8009                     i, j, k, len, len2, segment, points;
 
8011                 for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
 
8012                         points = this._rings[i];
 
8014                         for (j = 0, len2 = points.length; j < len2 - 1; j++) {
 
8015                                 segment = L.LineUtil.clipSegment(points[j], points[j + 1], bounds, j, true);
 
8017                                 if (!segment) { continue; }
 
8019                                 parts[k] = parts[k] || [];
 
8020                                 parts[k].push(segment[0]);
 
8022                                 // if segment goes out of screen, or it's the last one, it's the end of the line part
 
8023                                 if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
 
8024                                         parts[k].push(segment[1]);
 
8031         // simplify each clipped part of the polyline for performance
 
8032         _simplifyPoints: function () {
 
8033                 var parts = this._parts,
 
8034                     tolerance = this.options.smoothFactor;
 
8036                 for (var i = 0, len = parts.length; i < len; i++) {
 
8037                         parts[i] = L.LineUtil.simplify(parts[i], tolerance);
 
8041         _update: function () {
 
8042                 if (!this._map) { return; }
 
8045                 this._simplifyPoints();
 
8049         _updatePath: function () {
 
8050                 this._renderer._updatePoly(this);
 
8054 // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
 
8055 // Instantiates a polyline object given an array of geographical points and
 
8056 // optionally an options object. You can create a `Polyline` object with
 
8057 // multiple separate lines (`MultiPolyline`) by passing an array of arrays
 
8058 // of geographic points.
 
8059 L.polyline = function (latlngs, options) {
 
8060         return new L.Polyline(latlngs, options);
 
8063 L.Polyline._flat = function (latlngs) {
 
8064         // true if it's a flat array of latlngs; false if nested
 
8065         return !L.Util.isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
 
8071  * @namespace PolyUtil
 
8072  * Various utility functions for polygon geometries.
 
8077 /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
 
8078  * 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)).
 
8079  * Used by Leaflet to only show polygon points that are on the screen or near, increasing
 
8080  * performance. Note that polygon points needs different algorithm for clipping
 
8081  * than polyline, so there's a seperate method for it.
 
8083 L.PolyUtil.clipPolygon = function (points, bounds, round) {
 
8085             edges = [1, 4, 2, 8],
 
8091         for (i = 0, len = points.length; i < len; i++) {
 
8092                 points[i]._code = lu._getBitCode(points[i], bounds);
 
8095         // for each edge (left, bottom, right, top)
 
8096         for (k = 0; k < 4; k++) {
 
8100                 for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
 
8104                         // if a is inside the clip window
 
8105                         if (!(a._code & edge)) {
 
8106                                 // if b is outside the clip window (a->b goes out of screen)
 
8107                                 if (b._code & edge) {
 
8108                                         p = lu._getEdgeIntersection(b, a, edge, bounds, round);
 
8109                                         p._code = lu._getBitCode(p, bounds);
 
8110                                         clippedPoints.push(p);
 
8112                                 clippedPoints.push(a);
 
8114                         // else if b is inside the clip window (a->b enters the screen)
 
8115                         } else if (!(b._code & edge)) {
 
8116                                 p = lu._getEdgeIntersection(b, a, edge, bounds, round);
 
8117                                 p._code = lu._getBitCode(p, bounds);
 
8118                                 clippedPoints.push(p);
 
8121                 points = clippedPoints;
 
8132  * @inherits Polyline
 
8134  * A class for drawing polygon overlays on a map. Extends `Polyline`.
 
8136  * 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.
 
8142  * // create a red polygon from an array of LatLng points
 
8143  * var latlngs = [[-111.03, 41],[-111.04, 45],[-104.05, 45],[-104.05, 41]];
 
8145  * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
 
8147  * // zoom the map to the polygon
 
8148  * map.fitBounds(polygon.getBounds());
 
8151  * 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:
 
8155  *   [[-111.03, 41],[-111.04, 45],[-104.05, 45],[-104.05, 41]], // outer ring
 
8156  *   [[-108.58,37.29],[-108.58,40.71],[-102.50,40.71],[-102.50,37.29]] // hole
 
8160  * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
 
8164  *   [ // first polygon
 
8165  *     [[-111.03, 41],[-111.04, 45],[-104.05, 45],[-104.05, 41]], // outer ring
 
8166  *     [[-108.58,37.29],[-108.58,40.71],[-102.50,40.71],[-102.50,37.29]] // hole
 
8168  *   [ // second polygon
 
8169  *     [[-109.05, 37],[-109.03, 41],[-102.05, 41],[-102.04, 37],[-109.05, 38]]
 
8175 L.Polygon = L.Polyline.extend({
 
8181         isEmpty: function () {
 
8182                 return !this._latlngs.length || !this._latlngs[0].length;
 
8185         getCenter: function () {
 
8186                 // throws error when not yet added to map as this center calculation requires projected coordinates
 
8188                         throw new Error('Must add layer to map before using getCenter()');
 
8191                 var i, j, p1, p2, f, area, x, y, center,
 
8192                     points = this._rings[0],
 
8193                     len = points.length;
 
8195                 if (!len) { return null; }
 
8197                 // polygon centroid algorithm; only uses the first ring if there are multiple
 
8201                 for (i = 0, j = len - 1; i < len; j = i++) {
 
8205                         f = p1.y * p2.x - p2.y * p1.x;
 
8206                         x += (p1.x + p2.x) * f;
 
8207                         y += (p1.y + p2.y) * f;
 
8212                         // Polygon is so small that all points are on same pixel.
 
8215                         center = [x / area, y / area];
 
8217                 return this._map.layerPointToLatLng(center);
 
8220         _convertLatLngs: function (latlngs) {
 
8221                 var result = L.Polyline.prototype._convertLatLngs.call(this, latlngs),
 
8222                     len = result.length;
 
8224                 // remove last point if it equals first one
 
8225                 if (len >= 2 && result[0] instanceof L.LatLng && result[0].equals(result[len - 1])) {
 
8231         _setLatLngs: function (latlngs) {
 
8232                 L.Polyline.prototype._setLatLngs.call(this, latlngs);
 
8233                 if (L.Polyline._flat(this._latlngs)) {
 
8234                         this._latlngs = [this._latlngs];
 
8238         _defaultShape: function () {
 
8239                 return L.Polyline._flat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
 
8242         _clipPoints: function () {
 
8243                 // polygons need a different clipping algorithm so we redefine that
 
8245                 var bounds = this._renderer._bounds,
 
8246                     w = this.options.weight,
 
8247                     p = new L.Point(w, w);
 
8249                 // increase clip padding by stroke width to avoid stroke on clip edges
 
8250                 bounds = new L.Bounds(bounds.min.subtract(p), bounds.max.add(p));
 
8253                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
 
8257                 if (this.options.noClip) {
 
8258                         this._parts = this._rings;
 
8262                 for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
 
8263                         clipped = L.PolyUtil.clipPolygon(this._rings[i], bounds, true);
 
8264                         if (clipped.length) {
 
8265                                 this._parts.push(clipped);
 
8270         _updatePath: function () {
 
8271                 this._renderer._updatePoly(this, true);
 
8276 // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
 
8277 L.polygon = function (latlngs, options) {
 
8278         return new L.Polygon(latlngs, options);
 
8284  * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
 
8292  * A class for drawing rectangle overlays on a map. Extends `Polygon`.
 
8297  * // define rectangle geographical bounds
 
8298  * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
 
8300  * // create an orange rectangle
 
8301  * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
 
8303  * // zoom the map to the rectangle bounds
 
8304  * map.fitBounds(bounds);
 
8310 L.Rectangle = L.Polygon.extend({
 
8311         initialize: function (latLngBounds, options) {
 
8312                 L.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
 
8315         // @method setBounds(latLngBounds: LatLngBounds): this
 
8316         // Redraws the rectangle with the passed bounds.
 
8317         setBounds: function (latLngBounds) {
 
8318                 return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
 
8321         _boundsToLatLngs: function (latLngBounds) {
 
8322                 latLngBounds = L.latLngBounds(latLngBounds);
 
8324                         latLngBounds.getSouthWest(),
 
8325                         latLngBounds.getNorthWest(),
 
8326                         latLngBounds.getNorthEast(),
 
8327                         latLngBounds.getSouthEast()
 
8333 // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
 
8334 L.rectangle = function (latLngBounds, options) {
 
8335         return new L.Rectangle(latLngBounds, options);
 
8341  * @class CircleMarker
 
8342  * @aka L.CircleMarker
 
8345  * A circle of a fixed size with radius specified in pixels. Extends `Path`.
 
8348 L.CircleMarker = L.Path.extend({
 
8351         // @aka CircleMarker options
 
8355                 // @option radius: Number = 10
 
8356                 // Radius of the circle marker, in pixels
 
8360         initialize: function (latlng, options) {
 
8361                 L.setOptions(this, options);
 
8362                 this._latlng = L.latLng(latlng);
 
8363                 this._radius = this.options.radius;
 
8366         // @method setLatLng(latLng: LatLng): this
 
8367         // Sets the position of a circle marker to a new location.
 
8368         setLatLng: function (latlng) {
 
8369                 this._latlng = L.latLng(latlng);
 
8371                 return this.fire('move', {latlng: this._latlng});
 
8374         // @method getLatLng(): LatLng
 
8375         // Returns the current geographical position of the circle marker
 
8376         getLatLng: function () {
 
8377                 return this._latlng;
 
8380         // @method setRadius(radius: Number): this
 
8381         // Sets the radius of a circle marker. Units are in pixels.
 
8382         setRadius: function (radius) {
 
8383                 this.options.radius = this._radius = radius;
 
8384                 return this.redraw();
 
8387         // @method getRadius(): Number
 
8388         // Returns the current radius of the circle
 
8389         getRadius: function () {
 
8390                 return this._radius;
 
8393         setStyle : function (options) {
 
8394                 var radius = options && options.radius || this._radius;
 
8395                 L.Path.prototype.setStyle.call(this, options);
 
8396                 this.setRadius(radius);
 
8400         _project: function () {
 
8401                 this._point = this._map.latLngToLayerPoint(this._latlng);
 
8402                 this._updateBounds();
 
8405         _updateBounds: function () {
 
8406                 var r = this._radius,
 
8407                     r2 = this._radiusY || r,
 
8408                     w = this._clickTolerance(),
 
8409                     p = [r + w, r2 + w];
 
8410                 this._pxBounds = new L.Bounds(this._point.subtract(p), this._point.add(p));
 
8413         _update: function () {
 
8419         _updatePath: function () {
 
8420                 this._renderer._updateCircle(this);
 
8423         _empty: function () {
 
8424                 return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
 
8429 // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
 
8430 // Instantiates a circle marker object given a geographical point, and an optional options object.
 
8431 L.circleMarker = function (latlng, options) {
 
8432         return new L.CircleMarker(latlng, options);
 
8440  * @inherits CircleMarker
 
8442  * A class for drawing circle overlays on a map. Extends `CircleMarker`.
 
8444  * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
 
8449  * L.circle([50.5, 30.5], 200).addTo(map);
 
8453 L.Circle = L.CircleMarker.extend({
 
8455         initialize: function (latlng, options, legacyOptions) {
 
8456                 if (typeof options === 'number') {
 
8457                         // Backwards compatibility with 0.7.x factory (latlng, radius, options?)
 
8458                         options = L.extend({}, legacyOptions, {radius: options});
 
8460                 L.setOptions(this, options);
 
8461                 this._latlng = L.latLng(latlng);
 
8463                 if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
 
8466                 // @aka Circle options
 
8467                 // @option radius: Number; Radius of the circle, in meters.
 
8468                 this._mRadius = this.options.radius;
 
8471         // @method setRadius(radius: Number): this
 
8472         // Sets the radius of a circle. Units are in meters.
 
8473         setRadius: function (radius) {
 
8474                 this._mRadius = radius;
 
8475                 return this.redraw();
 
8478         // @method getRadius(): Number
 
8479         // Returns the current radius of a circle. Units are in meters.
 
8480         getRadius: function () {
 
8481                 return this._mRadius;
 
8484         // @method getBounds(): LatLngBounds
 
8485         // Returns the `LatLngBounds` of the path.
 
8486         getBounds: function () {
 
8487                 var half = [this._radius, this._radiusY || this._radius];
 
8489                 return new L.LatLngBounds(
 
8490                         this._map.layerPointToLatLng(this._point.subtract(half)),
 
8491                         this._map.layerPointToLatLng(this._point.add(half)));
 
8494         setStyle: L.Path.prototype.setStyle,
 
8496         _project: function () {
 
8498                 var lng = this._latlng.lng,
 
8499                     lat = this._latlng.lat,
 
8501                     crs = map.options.crs;
 
8503                 if (crs.distance === L.CRS.Earth.distance) {
 
8504                         var d = Math.PI / 180,
 
8505                             latR = (this._mRadius / L.CRS.Earth.R) / d,
 
8506                             top = map.project([lat + latR, lng]),
 
8507                             bottom = map.project([lat - latR, lng]),
 
8508                             p = top.add(bottom).divideBy(2),
 
8509                             lat2 = map.unproject(p).lat,
 
8510                             lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
 
8511                                     (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
 
8513                         if (isNaN(lngR) || lngR === 0) {
 
8514                                 lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
 
8517                         this._point = p.subtract(map.getPixelOrigin());
 
8518                         this._radius = isNaN(lngR) ? 0 : Math.max(Math.round(p.x - map.project([lat2, lng - lngR]).x), 1);
 
8519                         this._radiusY = Math.max(Math.round(p.y - top.y), 1);
 
8522                         var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
 
8524                         this._point = map.latLngToLayerPoint(this._latlng);
 
8525                         this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
 
8528                 this._updateBounds();
 
8532 // @factory L.circle(latlng: LatLng, options?: Circle options)
 
8533 // Instantiates a circle object given a geographical point, and an options object
 
8534 // which contains the circle radius.
 
8536 // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
 
8537 // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
 
8538 // Do not use in new applications or plugins.
 
8539 L.circle = function (latlng, options, legacyOptions) {
 
8540         return new L.Circle(latlng, options, legacyOptions);
 
8547  * @inherits Renderer
 
8550  * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
 
8551  * Inherits `Renderer`.
 
8553  * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
 
8554  * available in all web browsers, notably Android 2.x and 3.x.
 
8556  * Although SVG is not available on IE7 and IE8, these browsers support
 
8557  * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
 
8558  * (a now deprecated technology), and the SVG renderer will fall back to VML in
 
8563  * Use SVG by default for all paths in the map:
 
8566  * var map = L.map('map', {
 
8571  * Use a SVG renderer with extra padding for specific vector geometries:
 
8574  * var map = L.map('map');
 
8575  * var myRenderer = L.svg({ padding: 0.5 });
 
8576  * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
8577  * var circle = L.circle( center, { renderer: myRenderer } );
 
8581 L.SVG = L.Renderer.extend({
 
8583         getEvents: function () {
 
8584                 var events = L.Renderer.prototype.getEvents.call(this);
 
8585                 events.zoomstart = this._onZoomStart;
 
8589         _initContainer: function () {
 
8590                 this._container = L.SVG.create('svg');
 
8592                 // makes it possible to click through svg root; we'll reset it back in individual paths
 
8593                 this._container.setAttribute('pointer-events', 'none');
 
8595                 this._rootGroup = L.SVG.create('g');
 
8596                 this._container.appendChild(this._rootGroup);
 
8599         _onZoomStart: function () {
 
8600                 // Drag-then-pinch interactions might mess up the center and zoom.
 
8601                 // In this case, the easiest way to prevent this is re-do the renderer
 
8602                 //   bounds and padding when the zooming starts.
 
8606         _update: function () {
 
8607                 if (this._map._animatingZoom && this._bounds) { return; }
 
8609                 L.Renderer.prototype._update.call(this);
 
8611                 var b = this._bounds,
 
8613                     container = this._container;
 
8615                 // set size of svg-container if changed
 
8616                 if (!this._svgSize || !this._svgSize.equals(size)) {
 
8617                         this._svgSize = size;
 
8618                         container.setAttribute('width', size.x);
 
8619                         container.setAttribute('height', size.y);
 
8622                 // movement: update container viewBox so that we don't have to change coordinates of individual layers
 
8623                 L.DomUtil.setPosition(container, b.min);
 
8624                 container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
 
8626                 this.fire('update');
 
8629         // methods below are called by vector layers implementations
 
8631         _initPath: function (layer) {
 
8632                 var path = layer._path = L.SVG.create('path');
 
8635                 // @option className: String = null
 
8636                 // Custom class name set on an element. Only for SVG renderer.
 
8637                 if (layer.options.className) {
 
8638                         L.DomUtil.addClass(path, layer.options.className);
 
8641                 if (layer.options.interactive) {
 
8642                         L.DomUtil.addClass(path, 'leaflet-interactive');
 
8645                 this._updateStyle(layer);
 
8648         _addPath: function (layer) {
 
8649                 this._rootGroup.appendChild(layer._path);
 
8650                 layer.addInteractiveTarget(layer._path);
 
8653         _removePath: function (layer) {
 
8654                 L.DomUtil.remove(layer._path);
 
8655                 layer.removeInteractiveTarget(layer._path);
 
8658         _updatePath: function (layer) {
 
8663         _updateStyle: function (layer) {
 
8664                 var path = layer._path,
 
8665                     options = layer.options;
 
8667                 if (!path) { return; }
 
8669                 if (options.stroke) {
 
8670                         path.setAttribute('stroke', options.color);
 
8671                         path.setAttribute('stroke-opacity', options.opacity);
 
8672                         path.setAttribute('stroke-width', options.weight);
 
8673                         path.setAttribute('stroke-linecap', options.lineCap);
 
8674                         path.setAttribute('stroke-linejoin', options.lineJoin);
 
8676                         if (options.dashArray) {
 
8677                                 path.setAttribute('stroke-dasharray', options.dashArray);
 
8679                                 path.removeAttribute('stroke-dasharray');
 
8682                         if (options.dashOffset) {
 
8683                                 path.setAttribute('stroke-dashoffset', options.dashOffset);
 
8685                                 path.removeAttribute('stroke-dashoffset');
 
8688                         path.setAttribute('stroke', 'none');
 
8692                         path.setAttribute('fill', options.fillColor || options.color);
 
8693                         path.setAttribute('fill-opacity', options.fillOpacity);
 
8694                         path.setAttribute('fill-rule', options.fillRule || 'evenodd');
 
8696                         path.setAttribute('fill', 'none');
 
8700         _updatePoly: function (layer, closed) {
 
8701                 this._setPath(layer, L.SVG.pointsToPath(layer._parts, closed));
 
8704         _updateCircle: function (layer) {
 
8705                 var p = layer._point,
 
8707                     r2 = layer._radiusY || r,
 
8708                     arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
 
8710                 // drawing a circle with two half-arcs
 
8711                 var d = layer._empty() ? 'M0 0' :
 
8712                                 'M' + (p.x - r) + ',' + p.y +
 
8713                                 arc + (r * 2) + ',0 ' +
 
8714                                 arc + (-r * 2) + ',0 ';
 
8716                 this._setPath(layer, d);
 
8719         _setPath: function (layer, path) {
 
8720                 layer._path.setAttribute('d', path);
 
8723         // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
 
8724         _bringToFront: function (layer) {
 
8725                 L.DomUtil.toFront(layer._path);
 
8728         _bringToBack: function (layer) {
 
8729                 L.DomUtil.toBack(layer._path);
 
8734 // @namespace SVG; @section
 
8735 // There are several static functions which can be called without instantiating L.SVG:
 
8737         // @function create(name: String): SVGElement
 
8738         // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
 
8739         // corresponding to the class name passed. For example, using 'line' will return
 
8740         // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
 
8741         create: function (name) {
 
8742                 return document.createElementNS('http://www.w3.org/2000/svg', name);
 
8745         // @function pointsToPath(rings: Point[], closed: Boolean): String
 
8746         // Generates a SVG path string for multiple rings, with each ring turning
 
8747         // into "M..L..L.." instructions
 
8748         pointsToPath: function (rings, closed) {
 
8750                     i, j, len, len2, points, p;
 
8752                 for (i = 0, len = rings.length; i < len; i++) {
 
8755                         for (j = 0, len2 = points.length; j < len2; j++) {
 
8757                                 str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
 
8760                         // closes the ring for polygons; "x" is VML syntax
 
8761                         str += closed ? (L.Browser.svg ? 'z' : 'x') : '';
 
8764                 // SVG complains about empty path strings
 
8765                 return str || 'M0 0';
 
8769 // @namespace Browser; @property svg: Boolean
 
8770 // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
 
8771 L.Browser.svg = !!(document.createElementNS && L.SVG.create('svg').createSVGRect);
 
8775 // @factory L.svg(options?: Renderer options)
 
8776 // Creates a SVG renderer with the given options.
 
8777 L.svg = function (options) {
 
8778         return L.Browser.svg || L.Browser.vml ? new L.SVG(options) : null;
 
8784  * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
 
8790  * 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.
 
8792  * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
 
8793  * with old versions of Internet Explorer.
 
8796 // @namespace Browser; @property vml: Boolean
 
8797 // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
 
8798 L.Browser.vml = !L.Browser.svg && (function () {
 
8800                 var div = document.createElement('div');
 
8801                 div.innerHTML = '<v:shape adj="1"/>';
 
8803                 var shape = div.firstChild;
 
8804                 shape.style.behavior = 'url(#default#VML)';
 
8806                 return shape && (typeof shape.adj === 'object');
 
8813 // redefine some SVG methods to handle VML syntax which is similar but with some differences
 
8814 L.SVG.include(!L.Browser.vml ? {} : {
 
8816         _initContainer: function () {
 
8817                 this._container = L.DomUtil.create('div', 'leaflet-vml-container');
 
8820         _update: function () {
 
8821                 if (this._map._animatingZoom) { return; }
 
8822                 L.Renderer.prototype._update.call(this);
 
8825         _initPath: function (layer) {
 
8826                 var container = layer._container = L.SVG.create('shape');
 
8828                 L.DomUtil.addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
 
8830                 container.coordsize = '1 1';
 
8832                 layer._path = L.SVG.create('path');
 
8833                 container.appendChild(layer._path);
 
8835                 this._updateStyle(layer);
 
8838         _addPath: function (layer) {
 
8839                 var container = layer._container;
 
8840                 this._container.appendChild(container);
 
8842                 if (layer.options.interactive) {
 
8843                         layer.addInteractiveTarget(container);
 
8847         _removePath: function (layer) {
 
8848                 var container = layer._container;
 
8849                 L.DomUtil.remove(container);
 
8850                 layer.removeInteractiveTarget(container);
 
8853         _updateStyle: function (layer) {
 
8854                 var stroke = layer._stroke,
 
8856                     options = layer.options,
 
8857                     container = layer._container;
 
8859                 container.stroked = !!options.stroke;
 
8860                 container.filled = !!options.fill;
 
8862                 if (options.stroke) {
 
8864                                 stroke = layer._stroke = L.SVG.create('stroke');
 
8866                         container.appendChild(stroke);
 
8867                         stroke.weight = options.weight + 'px';
 
8868                         stroke.color = options.color;
 
8869                         stroke.opacity = options.opacity;
 
8871                         if (options.dashArray) {
 
8872                                 stroke.dashStyle = L.Util.isArray(options.dashArray) ?
 
8873                                     options.dashArray.join(' ') :
 
8874                                     options.dashArray.replace(/( *, *)/g, ' ');
 
8876                                 stroke.dashStyle = '';
 
8878                         stroke.endcap = options.lineCap.replace('butt', 'flat');
 
8879                         stroke.joinstyle = options.lineJoin;
 
8881                 } else if (stroke) {
 
8882                         container.removeChild(stroke);
 
8883                         layer._stroke = null;
 
8888                                 fill = layer._fill = L.SVG.create('fill');
 
8890                         container.appendChild(fill);
 
8891                         fill.color = options.fillColor || options.color;
 
8892                         fill.opacity = options.fillOpacity;
 
8895                         container.removeChild(fill);
 
8900         _updateCircle: function (layer) {
 
8901                 var p = layer._point.round(),
 
8902                     r = Math.round(layer._radius),
 
8903                     r2 = Math.round(layer._radiusY || r);
 
8905                 this._setPath(layer, layer._empty() ? 'M0 0' :
 
8906                                 'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
 
8909         _setPath: function (layer, path) {
 
8910                 layer._path.v = path;
 
8913         _bringToFront: function (layer) {
 
8914                 L.DomUtil.toFront(layer._container);
 
8917         _bringToBack: function (layer) {
 
8918                 L.DomUtil.toBack(layer._container);
 
8922 if (L.Browser.vml) {
 
8923         L.SVG.create = (function () {
 
8925                         document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
 
8926                         return function (name) {
 
8927                                 return document.createElement('<lvml:' + name + ' class="lvml">');
 
8930                         return function (name) {
 
8931                                 return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
 
8941  * @inherits Renderer
 
8944  * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
 
8945  * Inherits `Renderer`.
 
8947  * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
 
8948  * available in all web browsers, notably IE8, and overlapping geometries might
 
8949  * not display properly in some edge cases.
 
8953  * Use Canvas by default for all paths in the map:
 
8956  * var map = L.map('map', {
 
8957  *      renderer: L.canvas()
 
8961  * Use a Canvas renderer with extra padding for specific vector geometries:
 
8964  * var map = L.map('map');
 
8965  * var myRenderer = L.canvas({ padding: 0.5 });
 
8966  * var line = L.polyline( coordinates, { renderer: myRenderer } );
 
8967  * var circle = L.circle( center, { renderer: myRenderer } );
 
8971 L.Canvas = L.Renderer.extend({
 
8973         onAdd: function () {
 
8974                 L.Renderer.prototype.onAdd.call(this);
 
8976                 this._layers = this._layers || {};
 
8978                 // Redraw vectors since canvas is cleared upon removal,
 
8979                 // in case of removing the renderer itself from the map.
 
8983         _initContainer: function () {
 
8984                 var container = this._container = document.createElement('canvas');
 
8987                         .on(container, 'mousemove', L.Util.throttle(this._onMouseMove, 32, this), this)
 
8988                         .on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this)
 
8989                         .on(container, 'mouseout', this._handleMouseOut, this);
 
8991                 this._ctx = container.getContext('2d');
 
8994         _update: function () {
 
8995                 if (this._map._animatingZoom && this._bounds) { return; }
 
8997                 this._drawnLayers = {};
 
8999                 L.Renderer.prototype._update.call(this);
 
9001                 var b = this._bounds,
 
9002                     container = this._container,
 
9004                     m = L.Browser.retina ? 2 : 1;
 
9006                 L.DomUtil.setPosition(container, b.min);
 
9008                 // set canvas size (also clearing it); use double size on retina
 
9009                 container.width = m * size.x;
 
9010                 container.height = m * size.y;
 
9011                 container.style.width = size.x + 'px';
 
9012                 container.style.height = size.y + 'px';
 
9014                 if (L.Browser.retina) {
 
9015                         this._ctx.scale(2, 2);
 
9018                 // translate so we use the same path coordinates after canvas element moves
 
9019                 this._ctx.translate(-b.min.x, -b.min.y);
 
9021                 // Tell paths to redraw themselves
 
9022                 this.fire('update');
 
9025         _initPath: function (layer) {
 
9026                 this._updateDashArray(layer);
 
9027                 this._layers[L.stamp(layer)] = layer;
 
9030         _addPath: L.Util.falseFn,
 
9032         _removePath: function (layer) {
 
9033                 layer._removed = true;
 
9034                 this._requestRedraw(layer);
 
9037         _updatePath: function (layer) {
 
9038                 this._redrawBounds = layer._pxBounds;
 
9043                 this._redrawBounds = null;
 
9046         _updateStyle: function (layer) {
 
9047                 this._updateDashArray(layer);
 
9048                 this._requestRedraw(layer);
 
9051         _updateDashArray: function (layer) {
 
9052                 if (layer.options.dashArray) {
 
9053                         var parts = layer.options.dashArray.split(','),
 
9056                         for (i = 0; i < parts.length; i++) {
 
9057                                 dashArray.push(Number(parts[i]));
 
9059                         layer.options._dashArray = dashArray;
 
9063         _requestRedraw: function (layer) {
 
9064                 if (!this._map) { return; }
 
9066                 var padding = (layer.options.weight || 0) + 1;
 
9067                 this._redrawBounds = this._redrawBounds || new L.Bounds();
 
9068                 this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
 
9069                 this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
 
9071                 this._redrawRequest = this._redrawRequest || L.Util.requestAnimFrame(this._redraw, this);
 
9074         _redraw: function () {
 
9075                 this._redrawRequest = null;
 
9077                 this._draw(true); // clear layers in redraw bounds
 
9078                 this._draw(); // draw layers
 
9080                 this._redrawBounds = null;
 
9083         _draw: function (clear) {
 
9084                 this._clear = clear;
 
9085                 var layer, bounds = this._redrawBounds;
 
9088                         this._ctx.beginPath();
 
9089                         this._ctx.rect(bounds.min.x, bounds.min.y, bounds.max.x - bounds.min.x, bounds.max.y - bounds.min.y);
 
9093                 for (var id in this._layers) {
 
9094                         layer = this._layers[id];
 
9095                         if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
 
9096                                 layer._updatePath();
 
9098                         if (clear && layer._removed) {
 
9099                                 delete layer._removed;
 
9100                                 delete this._layers[id];
 
9103                 this._ctx.restore();  // Restore state before clipping.
 
9106         _updatePoly: function (layer, closed) {
 
9109                     parts = layer._parts,
 
9113                 if (!len) { return; }
 
9115                 this._drawnLayers[layer._leaflet_id] = layer;
 
9119                 if (ctx.setLineDash) {
 
9120                         ctx.setLineDash(layer.options && layer.options._dashArray || []);
 
9123                 for (i = 0; i < len; i++) {
 
9124                         for (j = 0, len2 = parts[i].length; j < len2; j++) {
 
9126                                 ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
 
9133                 this._fillStroke(ctx, layer);
 
9135                 // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
 
9138         _updateCircle: function (layer) {
 
9140                 if (layer._empty()) { return; }
 
9142                 var p = layer._point,
 
9145                     s = (layer._radiusY || r) / r;
 
9147                 this._drawnLayers[layer._leaflet_id] = layer;
 
9155                 ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
 
9161                 this._fillStroke(ctx, layer);
 
9164         _fillStroke: function (ctx, layer) {
 
9165                 var clear = this._clear,
 
9166                     options = layer.options;
 
9168                 ctx.globalCompositeOperation = clear ? 'destination-out' : 'source-over';
 
9171                         ctx.globalAlpha = clear ? 1 : options.fillOpacity;
 
9172                         ctx.fillStyle = options.fillColor || options.color;
 
9173                         ctx.fill(options.fillRule || 'evenodd');
 
9176                 if (options.stroke && options.weight !== 0) {
 
9177                         ctx.globalAlpha = clear ? 1 : options.opacity;
 
9179                         // if clearing shape, do it with the previously drawn line width
 
9180                         layer._prevWeight = ctx.lineWidth = clear ? layer._prevWeight + 1 : options.weight;
 
9182                         ctx.strokeStyle = options.color;
 
9183                         ctx.lineCap = options.lineCap;
 
9184                         ctx.lineJoin = options.lineJoin;
 
9189         // Canvas obviously doesn't have mouse events for individual drawn objects,
 
9190         // so we emulate that by calculating what's under the mouse on mousemove/click manually
 
9192         _onClick: function (e) {
 
9193                 var point = this._map.mouseEventToLayerPoint(e), layers = [], layer;
 
9195                 for (var id in this._layers) {
 
9196                         layer = this._layers[id];
 
9197                         if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
 
9198                                 L.DomEvent._fakeStop(e);
 
9202                 if (layers.length)  {
 
9203                         this._fireEvent(layers, e);
 
9207         _onMouseMove: function (e) {
 
9208                 if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
 
9210                 var point = this._map.mouseEventToLayerPoint(e);
 
9211                 this._handleMouseOut(e, point);
 
9212                 this._handleMouseHover(e, point);
 
9216         _handleMouseOut: function (e, point) {
 
9217                 var layer = this._hoveredLayer;
 
9218                 if (layer && (e.type === 'mouseout' || !layer._containsPoint(point))) {
 
9219                         // if we're leaving the layer, fire mouseout
 
9220                         L.DomUtil.removeClass(this._container, 'leaflet-interactive');
 
9221                         this._fireEvent([layer], e, 'mouseout');
 
9222                         this._hoveredLayer = null;
 
9226         _handleMouseHover: function (e, point) {
 
9229                 for (id in this._drawnLayers) {
 
9230                         layer = this._drawnLayers[id];
 
9231                         if (layer.options.interactive && layer._containsPoint(point)) {
 
9232                                 L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor
 
9233                                 this._fireEvent([layer], e, 'mouseover');
 
9234                                 this._hoveredLayer = layer;
 
9238                 if (this._hoveredLayer) {
 
9239                         this._fireEvent([this._hoveredLayer], e);
 
9243         _fireEvent: function (layers, e, type) {
 
9244                 this._map._fireDOMEvent(e, type || e.type, layers);
 
9247         // TODO _bringToFront & _bringToBack, pretty tricky
 
9249         _bringToFront: L.Util.falseFn,
 
9250         _bringToBack: L.Util.falseFn
 
9253 // @namespace Browser; @property canvas: Boolean
 
9254 // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
 
9255 L.Browser.canvas = (function () {
 
9256         return !!document.createElement('canvas').getContext;
 
9259 // @namespace Canvas
 
9260 // @factory L.canvas(options?: Renderer options)
 
9261 // Creates a Canvas renderer with the given options.
 
9262 L.canvas = function (options) {
 
9263         return L.Browser.canvas ? new L.Canvas(options) : null;
 
9266 L.Polyline.prototype._containsPoint = function (p, closed) {
 
9267         var i, j, k, len, len2, part,
 
9268             w = this._clickTolerance();
 
9270         if (!this._pxBounds.contains(p)) { return false; }
 
9272         // hit detection for polylines
 
9273         for (i = 0, len = this._parts.length; i < len; i++) {
 
9274                 part = this._parts[i];
 
9276                 for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
9277                         if (!closed && (j === 0)) { continue; }
 
9279                         if (L.LineUtil.pointToSegmentDistance(p, part[k], part[j]) <= w) {
 
9287 L.Polygon.prototype._containsPoint = function (p) {
 
9289             part, p1, p2, i, j, k, len, len2;
 
9291         if (!this._pxBounds.contains(p)) { return false; }
 
9293         // ray casting algorithm for detecting if point is in polygon
 
9294         for (i = 0, len = this._parts.length; i < len; i++) {
 
9295                 part = this._parts[i];
 
9297                 for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
 
9301                         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)) {
 
9307         // also check if it's on polygon stroke
 
9308         return inside || L.Polyline.prototype._containsPoint.call(this, p, true);
 
9311 L.CircleMarker.prototype._containsPoint = function (p) {
 
9312         return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
 
9320  * @inherits FeatureGroup
 
9322  * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
 
9323  * GeoJSON data and display it on the map. Extends `FeatureGroup`.
 
9329  *      style: function (feature) {
 
9330  *              return {color: feature.properties.color};
 
9332  * }).bindPopup(function (layer) {
 
9333  *      return layer.feature.properties.description;
 
9338 L.GeoJSON = L.FeatureGroup.extend({
 
9341          * @aka GeoJSON options
 
9343          * @option pointToLayer: Function = *
 
9344          * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
 
9345          * called when data is added, passing the GeoJSON point feature and its `LatLng`.
 
9346          * The default is to spawn a default `Marker`:
 
9348          * function(geoJsonPoint, latlng) {
 
9349          *      return L.marker(latlng);
 
9353          * @option style: Function = *
 
9354          * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
 
9355          * called internally when data is added.
 
9356          * The default value is to not override any defaults:
 
9358          * function (geoJsonFeature) {
 
9363          * @option onEachFeature: Function = *
 
9364          * A `Function` that will be called once for each created `Feature`, after it has
 
9365          * been created and styled. Useful for attaching events and popups to features.
 
9366          * The default is to do nothing with the newly created layers:
 
9368          * function (feature, layer) {}
 
9371          * @option filter: Function = *
 
9372          * A `Function` that will be used to decide whether to show a feature or not.
 
9373          * The default is to show all features:
 
9375          * function (geoJsonFeature) {
 
9380          * @option coordsToLatLng: Function = *
 
9381          * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
 
9382          * The default is the `coordsToLatLng` static method.
 
9385         initialize: function (geojson, options) {
 
9386                 L.setOptions(this, options);
 
9391                         this.addData(geojson);
 
9395         // @function addData( <GeoJSON> data ): Layer
 
9396         // Adds a GeoJSON object to the layer.
 
9397         addData: function (geojson) {
 
9398                 var features = L.Util.isArray(geojson) ? geojson : geojson.features,
 
9402                         for (i = 0, len = features.length; i < len; i++) {
 
9403                                 // only add this if geometry or geometries are set and not null
 
9404                                 feature = features[i];
 
9405                                 if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
 
9406                                         this.addData(feature);
 
9412                 var options = this.options;
 
9414                 if (options.filter && !options.filter(geojson)) { return this; }
 
9416                 var layer = L.GeoJSON.geometryToLayer(geojson, options);
 
9420                 layer.feature = L.GeoJSON.asFeature(geojson);
 
9422                 layer.defaultOptions = layer.options;
 
9423                 this.resetStyle(layer);
 
9425                 if (options.onEachFeature) {
 
9426                         options.onEachFeature(geojson, layer);
 
9429                 return this.addLayer(layer);
 
9432         // @function resetStyle( <Path> layer ): Layer
 
9433         // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
 
9434         resetStyle: function (layer) {
 
9435                 // reset any custom styles
 
9436                 layer.options = L.Util.extend({}, layer.defaultOptions);
 
9437                 this._setLayerStyle(layer, this.options.style);
 
9441         // @function setStyle( <Function> style ): Layer
 
9442         // Changes styles of GeoJSON vector layers with the given style function.
 
9443         setStyle: function (style) {
 
9444                 return this.eachLayer(function (layer) {
 
9445                         this._setLayerStyle(layer, style);
 
9449         _setLayerStyle: function (layer, style) {
 
9450                 if (typeof style === 'function') {
 
9451                         style = style(layer.feature);
 
9453                 if (layer.setStyle) {
 
9454                         layer.setStyle(style);
 
9460 // There are several static functions which can be called without instantiating L.GeoJSON:
 
9461 L.extend(L.GeoJSON, {
 
9462         // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
 
9463         // Creates a `Layer` from a given GeoJSON feature. Can use a custom
 
9464         // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
 
9465         // functions if provided as options.
 
9466         geometryToLayer: function (geojson, options) {
 
9468                 var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
 
9469                     coords = geometry ? geometry.coordinates : null,
 
9471                     pointToLayer = options && options.pointToLayer,
 
9472                     coordsToLatLng = options && options.coordsToLatLng || this.coordsToLatLng,
 
9473                     latlng, latlngs, i, len;
 
9475                 if (!coords && !geometry) {
 
9479                 switch (geometry.type) {
 
9481                         latlng = coordsToLatLng(coords);
 
9482                         return pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng);
 
9485                         for (i = 0, len = coords.length; i < len; i++) {
 
9486                                 latlng = coordsToLatLng(coords[i]);
 
9487                                 layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng));
 
9489                         return new L.FeatureGroup(layers);
 
9492                 case 'MultiLineString':
 
9493                         latlngs = this.coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, coordsToLatLng);
 
9494                         return new L.Polyline(latlngs, options);
 
9497                 case 'MultiPolygon':
 
9498                         latlngs = this.coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, coordsToLatLng);
 
9499                         return new L.Polygon(latlngs, options);
 
9501                 case 'GeometryCollection':
 
9502                         for (i = 0, len = geometry.geometries.length; i < len; i++) {
 
9503                                 var layer = this.geometryToLayer({
 
9504                                         geometry: geometry.geometries[i],
 
9506                                         properties: geojson.properties
 
9513                         return new L.FeatureGroup(layers);
 
9516                         throw new Error('Invalid GeoJSON object.');
 
9520         // @function coordsToLatLng(coords: Array): LatLng
 
9521         // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
 
9522         // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
 
9523         coordsToLatLng: function (coords) {
 
9524                 return new L.LatLng(coords[1], coords[0], coords[2]);
 
9527         // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
 
9528         // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
 
9529         // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
 
9530         // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
 
9531         coordsToLatLngs: function (coords, levelsDeep, coordsToLatLng) {
 
9534                 for (var i = 0, len = coords.length, latlng; i < len; i++) {
 
9535                         latlng = levelsDeep ?
 
9536                                 this.coordsToLatLngs(coords[i], levelsDeep - 1, coordsToLatLng) :
 
9537                                 (coordsToLatLng || this.coordsToLatLng)(coords[i]);
 
9539                         latlngs.push(latlng);
 
9545         // @function latLngToCoords(latlng: LatLng): Array
 
9546         // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
 
9547         latLngToCoords: function (latlng) {
 
9548                 return latlng.alt !== undefined ?
 
9549                                 [latlng.lng, latlng.lat, latlng.alt] :
 
9550                                 [latlng.lng, latlng.lat];
 
9553         // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
 
9554         // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
 
9555         // `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.
 
9556         latLngsToCoords: function (latlngs, levelsDeep, closed) {
 
9559                 for (var i = 0, len = latlngs.length; i < len; i++) {
 
9560                         coords.push(levelsDeep ?
 
9561                                 L.GeoJSON.latLngsToCoords(latlngs[i], levelsDeep - 1, closed) :
 
9562                                 L.GeoJSON.latLngToCoords(latlngs[i]));
 
9565                 if (!levelsDeep && closed) {
 
9566                         coords.push(coords[0]);
 
9572         getFeature: function (layer, newGeometry) {
 
9573                 return layer.feature ?
 
9574                                 L.extend({}, layer.feature, {geometry: newGeometry}) :
 
9575                                 L.GeoJSON.asFeature(newGeometry);
 
9578         // @function asFeature(geojson: Object): Object
 
9579         // Normalize GeoJSON geometries/features into GeoJSON features.
 
9580         asFeature: function (geojson) {
 
9581                 if (geojson.type === 'Feature') {
 
9593 var PointToGeoJSON = {
 
9594         toGeoJSON: function () {
 
9595                 return L.GeoJSON.getFeature(this, {
 
9597                         coordinates: L.GeoJSON.latLngToCoords(this.getLatLng())
 
9602 L.Marker.include(PointToGeoJSON);
 
9604 // @namespace CircleMarker
 
9605 // @method toGeoJSON(): Object
 
9606 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
 
9607 L.Circle.include(PointToGeoJSON);
 
9608 L.CircleMarker.include(PointToGeoJSON);
 
9611 // @namespace Polyline
 
9612 // @method toGeoJSON(): Object
 
9613 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
 
9614 L.Polyline.prototype.toGeoJSON = function () {
 
9615         var multi = !L.Polyline._flat(this._latlngs);
 
9617         var coords = L.GeoJSON.latLngsToCoords(this._latlngs, multi ? 1 : 0);
 
9619         return L.GeoJSON.getFeature(this, {
 
9620                 type: (multi ? 'Multi' : '') + 'LineString',
 
9625 // @namespace Polygon
 
9626 // @method toGeoJSON(): Object
 
9627 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
 
9628 L.Polygon.prototype.toGeoJSON = function () {
 
9629         var holes = !L.Polyline._flat(this._latlngs),
 
9630             multi = holes && !L.Polyline._flat(this._latlngs[0]);
 
9632         var coords = L.GeoJSON.latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true);
 
9638         return L.GeoJSON.getFeature(this, {
 
9639                 type: (multi ? 'Multi' : '') + 'Polygon',
 
9645 // @namespace LayerGroup
 
9646 L.LayerGroup.include({
 
9647         toMultiPoint: function () {
 
9650                 this.eachLayer(function (layer) {
 
9651                         coords.push(layer.toGeoJSON().geometry.coordinates);
 
9654                 return L.GeoJSON.getFeature(this, {
 
9660         // @method toGeoJSON(): Object
 
9661         // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `GeometryCollection`).
 
9662         toGeoJSON: function () {
 
9664                 var type = this.feature && this.feature.geometry && this.feature.geometry.type;
 
9666                 if (type === 'MultiPoint') {
 
9667                         return this.toMultiPoint();
 
9670                 var isGeometryCollection = type === 'GeometryCollection',
 
9673                 this.eachLayer(function (layer) {
 
9674                         if (layer.toGeoJSON) {
 
9675                                 var json = layer.toGeoJSON();
 
9676                                 jsons.push(isGeometryCollection ? json.geometry : L.GeoJSON.asFeature(json));
 
9680                 if (isGeometryCollection) {
 
9681                         return L.GeoJSON.getFeature(this, {
 
9683                                 type: 'GeometryCollection'
 
9688                         type: 'FeatureCollection',
 
9694 // @namespace GeoJSON
 
9695 // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
 
9696 // Creates a GeoJSON layer. Optionally accepts an object in
 
9697 // [GeoJSON format](http://geojson.org/geojson-spec.html) to display on the map
 
9698 // (you can alternatively add it later with `addData` method) and an `options` object.
 
9699 L.geoJSON = function (geojson, options) {
 
9700         return new L.GeoJSON(geojson, options);
 
9702 // Backward compatibility.
 
9703 L.geoJson = L.geoJSON;
 
9708  * @namespace DomEvent
 
9709  * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
 
9712 // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
 
9716 var eventsKey = '_leaflet_events';
 
9720         // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
 
9721         // Adds a listener function (`fn`) to a particular DOM event type of the
 
9722         // element `el`. You can optionally specify the context of the listener
 
9723         // (object the `this` keyword will point to). You can also pass several
 
9724         // space-separated types (e.g. `'click dblclick'`).
 
9727         // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
 
9728         // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
9729         on: function (obj, types, fn, context) {
 
9731                 if (typeof types === 'object') {
 
9732                         for (var type in types) {
 
9733                                 this._on(obj, type, types[type], fn);
 
9736                         types = L.Util.splitWords(types);
 
9738                         for (var i = 0, len = types.length; i < len; i++) {
 
9739                                 this._on(obj, types[i], fn, context);
 
9746         // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
 
9747         // Removes a previously added listener function. If no function is specified,
 
9748         // it will remove all the listeners of that particular DOM event from the element.
 
9749         // Note that if you passed a custom context to on, you must pass the same
 
9750         // context to `off` in order to remove the listener.
 
9753         // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
 
9754         // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
 
9755         off: function (obj, types, fn, context) {
 
9757                 if (typeof types === 'object') {
 
9758                         for (var type in types) {
 
9759                                 this._off(obj, type, types[type], fn);
 
9762                         types = L.Util.splitWords(types);
 
9764                         for (var i = 0, len = types.length; i < len; i++) {
 
9765                                 this._off(obj, types[i], fn, context);
 
9772         _on: function (obj, type, fn, context) {
 
9773                 var id = type + L.stamp(fn) + (context ? '_' + L.stamp(context) : '');
 
9775                 if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
 
9777                 var handler = function (e) {
 
9778                         return fn.call(context || obj, e || window.event);
 
9781                 var originalHandler = handler;
 
9783                 if (L.Browser.pointer && type.indexOf('touch') === 0) {
 
9784                         this.addPointerListener(obj, type, handler, id);
 
9786                 } else if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) {
 
9787                         this.addDoubleTapListener(obj, handler, id);
 
9789                 } else if ('addEventListener' in obj) {
 
9791                         if (type === 'mousewheel') {
 
9792                                 obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
 
9794                         } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
 
9795                                 handler = function (e) {
 
9796                                         e = e || window.event;
 
9797                                         if (L.DomEvent._isExternalTarget(obj, e)) {
 
9801                                 obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
 
9804                                 if (type === 'click' && L.Browser.android) {
 
9805                                         handler = function (e) {
 
9806                                                 return L.DomEvent._filterClick(e, originalHandler);
 
9809                                 obj.addEventListener(type, handler, false);
 
9812                 } else if ('attachEvent' in obj) {
 
9813                         obj.attachEvent('on' + type, handler);
 
9816                 obj[eventsKey] = obj[eventsKey] || {};
 
9817                 obj[eventsKey][id] = handler;
 
9822         _off: function (obj, type, fn, context) {
 
9824                 var id = type + L.stamp(fn) + (context ? '_' + L.stamp(context) : ''),
 
9825                     handler = obj[eventsKey] && obj[eventsKey][id];
 
9827                 if (!handler) { return this; }
 
9829                 if (L.Browser.pointer && type.indexOf('touch') === 0) {
 
9830                         this.removePointerListener(obj, type, id);
 
9832                 } else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) {
 
9833                         this.removeDoubleTapListener(obj, id);
 
9835                 } else if ('removeEventListener' in obj) {
 
9837                         if (type === 'mousewheel') {
 
9838                                 obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
 
9841                                 obj.removeEventListener(
 
9842                                         type === 'mouseenter' ? 'mouseover' :
 
9843                                         type === 'mouseleave' ? 'mouseout' : type, handler, false);
 
9846                 } else if ('detachEvent' in obj) {
 
9847                         obj.detachEvent('on' + type, handler);
 
9850                 obj[eventsKey][id] = null;
 
9855         // @function stopPropagation(ev: DOMEvent): this
 
9856         // Stop the given event from propagation to parent elements. Used inside the listener functions:
 
9858         // L.DomEvent.on(div, 'click', function (ev) {
 
9859         //      L.DomEvent.stopPropagation(ev);
 
9862         stopPropagation: function (e) {
 
9864                 if (e.stopPropagation) {
 
9865                         e.stopPropagation();
 
9866                 } else if (e.originalEvent) {  // In case of Leaflet event.
 
9867                         e.originalEvent._stopped = true;
 
9869                         e.cancelBubble = true;
 
9871                 L.DomEvent._skipped(e);
 
9876         // @function disableScrollPropagation(el: HTMLElement): this
 
9877         // Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
 
9878         disableScrollPropagation: function (el) {
 
9879                 return L.DomEvent.on(el, 'mousewheel', L.DomEvent.stopPropagation);
 
9882         // @function disableClickPropagation(el: HTMLElement): this
 
9883         // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
 
9884         // `'mousedown'` and `'touchstart'` events (plus browser variants).
 
9885         disableClickPropagation: function (el) {
 
9886                 var stop = L.DomEvent.stopPropagation;
 
9888                 L.DomEvent.on(el, L.Draggable.START.join(' '), stop);
 
9890                 return L.DomEvent.on(el, {
 
9891                         click: L.DomEvent._fakeStop,
 
9896         // @function preventDefault(ev: DOMEvent): this
 
9897         // Prevents the default action of the DOM Event `ev` from happening (such as
 
9898         // following a link in the href of the a element, or doing a POST request
 
9899         // with page reload when a `<form>` is submitted).
 
9900         // Use it inside listener functions.
 
9901         preventDefault: function (e) {
 
9903                 if (e.preventDefault) {
 
9906                         e.returnValue = false;
 
9911         // @function stop(ev): this
 
9912         // Does `stopPropagation` and `preventDefault` at the same time.
 
9913         stop: function (e) {
 
9916                         .stopPropagation(e);
 
9919         // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
 
9920         // Gets normalized mouse position from a DOM event relative to the
 
9921         // `container` or to the whole page if not specified.
 
9922         getMousePosition: function (e, container) {
 
9924                         return new L.Point(e.clientX, e.clientY);
 
9927                 var rect = container.getBoundingClientRect();
 
9930                         e.clientX - rect.left - container.clientLeft,
 
9931                         e.clientY - rect.top - container.clientTop);
 
9934         // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
 
9935         // and Firefox scrolls device pixels, not CSS pixels
 
9936         _wheelPxFactor: (L.Browser.win && L.Browser.chrome) ? 2 :
 
9937                         L.Browser.gecko ? window.devicePixelRatio :
 
9940         // @function getWheelDelta(ev: DOMEvent): Number
 
9941         // Gets normalized wheel delta from a mousewheel DOM event, in vertical
 
9942         // pixels scrolled (negative if scrolling down).
 
9943         // Events from pointing devices without precise scrolling are mapped to
 
9944         // a best guess of 60 pixels.
 
9945         getWheelDelta: function (e) {
 
9946                 return (L.Browser.edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
 
9947                        (e.deltaY && e.deltaMode === 0) ? -e.deltaY / L.DomEvent._wheelPxFactor : // Pixels
 
9948                        (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
 
9949                        (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
 
9950                        (e.deltaX || e.deltaZ) ? 0 :     // Skip horizontal/depth wheel events
 
9951                        e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
 
9952                        (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
 
9953                        e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
 
9959         _fakeStop: function (e) {
 
9960                 // fakes stopPropagation by setting a special event flag, checked/reset with L.DomEvent._skipped(e)
 
9961                 L.DomEvent._skipEvents[e.type] = true;
 
9964         _skipped: function (e) {
 
9965                 var skipped = this._skipEvents[e.type];
 
9966                 // reset when checking, as it's only used in map container and propagates outside of the map
 
9967                 this._skipEvents[e.type] = false;
 
9971         // check if element really left/entered the event target (for mouseenter/mouseleave)
 
9972         _isExternalTarget: function (el, e) {
 
9974                 var related = e.relatedTarget;
 
9976                 if (!related) { return true; }
 
9979                         while (related && (related !== el)) {
 
9980                                 related = related.parentNode;
 
9985                 return (related !== el);
 
9988         // this is a horrible workaround for a bug in Android where a single touch triggers two click events
 
9989         _filterClick: function (e, handler) {
 
9990                 var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
 
9991                     elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick);
 
9993                 // are they closer together than 500ms yet more than 100ms?
 
9994                 // Android typically triggers them ~300ms apart while multiple listeners
 
9995                 // on the same event should be triggered far faster;
 
9996                 // or check if click is simulated on the element, and if it is, reject any non-simulated events
 
9998                 if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
 
10002                 L.DomEvent._lastClick = timeStamp;
 
10008 // @function addListener(…): this
 
10009 // Alias to [`L.DomEvent.on`](#domevent-on)
 
10010 L.DomEvent.addListener = L.DomEvent.on;
 
10012 // @function removeListener(…): this
 
10013 // Alias to [`L.DomEvent.off`](#domevent-off)
 
10014 L.DomEvent.removeListener = L.DomEvent.off;
 
10021  * @inherits Evented
 
10023  * A class for making DOM elements draggable (including touch support).
 
10024  * Used internally for map and marker dragging. Only works for elements
 
10025  * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
 
10029  * var draggable = new L.Draggable(elementToDrag);
 
10030  * draggable.enable();
 
10034 L.Draggable = L.Evented.extend({
 
10037                 // @option clickTolerance: Number = 3
 
10038                 // The max number of pixels a user can shift the mouse pointer during a click
 
10039                 // for it to be considered a valid click (as opposed to a mouse drag).
 
10044                 START: L.Browser.touch ? ['touchstart', 'mousedown'] : ['mousedown'],
 
10046                         mousedown: 'mouseup',
 
10047                         touchstart: 'touchend',
 
10048                         pointerdown: 'touchend',
 
10049                         MSPointerDown: 'touchend'
 
10052                         mousedown: 'mousemove',
 
10053                         touchstart: 'touchmove',
 
10054                         pointerdown: 'touchmove',
 
10055                         MSPointerDown: 'touchmove'
 
10059         // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline: Boolean)
 
10060         // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
 
10061         initialize: function (element, dragStartTarget, preventOutline) {
 
10062                 this._element = element;
 
10063                 this._dragStartTarget = dragStartTarget || element;
 
10064                 this._preventOutline = preventOutline;
 
10067         // @method enable()
 
10068         // Enables the dragging ability
 
10069         enable: function () {
 
10070                 if (this._enabled) { return; }
 
10072                 L.DomEvent.on(this._dragStartTarget, L.Draggable.START.join(' '), this._onDown, this);
 
10074                 this._enabled = true;
 
10077         // @method disable()
 
10078         // Disables the dragging ability
 
10079         disable: function () {
 
10080                 if (!this._enabled) { return; }
 
10082                 L.DomEvent.off(this._dragStartTarget, L.Draggable.START.join(' '), this._onDown, this);
 
10084                 this._enabled = false;
 
10085                 this._moved = false;
 
10088         _onDown: function (e) {
 
10089                 // Ignore simulated events, since we handle both touch and
 
10090                 // mouse explicitly; otherwise we risk getting duplicates of
 
10091                 // touch events, see #4315.
 
10092                 // Also ignore the event if disabled; this happens in IE11
 
10093                 // under some circumstances, see #3666.
 
10094                 if (e._simulated || !this._enabled) { return; }
 
10096                 this._moved = false;
 
10098                 if (L.DomUtil.hasClass(this._element, 'leaflet-zoom-anim')) { return; }
 
10100                 if (L.Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches) || !this._enabled) { return; }
 
10101                 L.Draggable._dragging = true;  // Prevent dragging multiple objects at once.
 
10103                 if (this._preventOutline) {
 
10104                         L.DomUtil.preventOutline(this._element);
 
10107                 L.DomUtil.disableImageDrag();
 
10108                 L.DomUtil.disableTextSelection();
 
10110                 if (this._moving) { return; }
 
10112                 // @event down: Event
 
10113                 // Fired when a drag is about to start.
 
10116                 var first = e.touches ? e.touches[0] : e;
 
10118                 this._startPoint = new L.Point(first.clientX, first.clientY);
 
10121                         .on(document, L.Draggable.MOVE[e.type], this._onMove, this)
 
10122                         .on(document, L.Draggable.END[e.type], this._onUp, this);
 
10125         _onMove: function (e) {
 
10126                 // Ignore simulated events, since we handle both touch and
 
10127                 // mouse explicitly; otherwise we risk getting duplicates of
 
10128                 // touch events, see #4315.
 
10129                 // Also ignore the event if disabled; this happens in IE11
 
10130                 // under some circumstances, see #3666.
 
10131                 if (e._simulated || !this._enabled) { return; }
 
10133                 if (e.touches && e.touches.length > 1) {
 
10134                         this._moved = true;
 
10138                 var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
 
10139                     newPoint = new L.Point(first.clientX, first.clientY),
 
10140                     offset = newPoint.subtract(this._startPoint);
 
10142                 if (!offset.x && !offset.y) { return; }
 
10143                 if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
 
10145                 L.DomEvent.preventDefault(e);
 
10147                 if (!this._moved) {
 
10148                         // @event dragstart: Event
 
10149                         // Fired when a drag starts
 
10150                         this.fire('dragstart');
 
10152                         this._moved = true;
 
10153                         this._startPos = L.DomUtil.getPosition(this._element).subtract(offset);
 
10155                         L.DomUtil.addClass(document.body, 'leaflet-dragging');
 
10157                         this._lastTarget = e.target || e.srcElement;
 
10158                         // IE and Edge do not give the <use> element, so fetch it
 
10160                         if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
 
10161                                 this._lastTarget = this._lastTarget.correspondingUseElement;
 
10163                         L.DomUtil.addClass(this._lastTarget, 'leaflet-drag-target');
 
10166                 this._newPos = this._startPos.add(offset);
 
10167                 this._moving = true;
 
10169                 L.Util.cancelAnimFrame(this._animRequest);
 
10170                 this._lastEvent = e;
 
10171                 this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true);
 
10174         _updatePosition: function () {
 
10175                 var e = {originalEvent: this._lastEvent};
 
10177                 // @event predrag: Event
 
10178                 // Fired continuously during dragging *before* each corresponding
 
10179                 // update of the element's position.
 
10180                 this.fire('predrag', e);
 
10181                 L.DomUtil.setPosition(this._element, this._newPos);
 
10183                 // @event drag: Event
 
10184                 // Fired continuously during dragging.
 
10185                 this.fire('drag', e);
 
10188         _onUp: function (e) {
 
10189                 // Ignore simulated events, since we handle both touch and
 
10190                 // mouse explicitly; otherwise we risk getting duplicates of
 
10191                 // touch events, see #4315.
 
10192                 // Also ignore the event if disabled; this happens in IE11
 
10193                 // under some circumstances, see #3666.
 
10194                 if (e._simulated || !this._enabled) { return; }
 
10196                 L.DomUtil.removeClass(document.body, 'leaflet-dragging');
 
10198                 if (this._lastTarget) {
 
10199                         L.DomUtil.removeClass(this._lastTarget, 'leaflet-drag-target');
 
10200                         this._lastTarget = null;
 
10203                 for (var i in L.Draggable.MOVE) {
 
10205                                 .off(document, L.Draggable.MOVE[i], this._onMove, this)
 
10206                                 .off(document, L.Draggable.END[i], this._onUp, this);
 
10209                 L.DomUtil.enableImageDrag();
 
10210                 L.DomUtil.enableTextSelection();
 
10212                 if (this._moved && this._moving) {
 
10213                         // ensure drag is not fired after dragend
 
10214                         L.Util.cancelAnimFrame(this._animRequest);
 
10216                         // @event dragend: DragEndEvent
 
10217                         // Fired when the drag ends.
 
10218                         this.fire('dragend', {
 
10219                                 distance: this._newPos.distanceTo(this._startPos)
 
10223                 this._moving = false;
 
10224                 L.Draggable._dragging = false;
 
10231         L.Handler is a base class for handler classes that are used internally to inject
 
10232         interaction features like dragging to classes like Map and Marker.
 
10237 // Abstract class for map interaction handlers
 
10239 L.Handler = L.Class.extend({
 
10240         initialize: function (map) {
 
10244         // @method enable(): this
 
10245         // Enables the handler
 
10246         enable: function () {
 
10247                 if (this._enabled) { return this; }
 
10249                 this._enabled = true;
 
10254         // @method disable(): this
 
10255         // Disables the handler
 
10256         disable: function () {
 
10257                 if (!this._enabled) { return this; }
 
10259                 this._enabled = false;
 
10260                 this.removeHooks();
 
10264         // @method enabled(): Boolean
 
10265         // Returns `true` if the handler is enabled
 
10266         enabled: function () {
 
10267                 return !!this._enabled;
 
10270         // @section Extension methods
 
10271         // Classes inheriting from `Handler` must implement the two following methods:
 
10272         // @method addHooks()
 
10273         // Called when the handler is enabled, should add event hooks.
 
10274         // @method removeHooks()
 
10275         // Called when the handler is disabled, should remove the event hooks added previously.
 
10281  * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
 
10285 // @section Interaction Options
 
10286 L.Map.mergeOptions({
 
10287         // @option dragging: Boolean = true
 
10288         // Whether the map be draggable with mouse/touch or not.
 
10291         // @section Panning Inertia Options
 
10292         // @option inertia: Boolean = *
 
10293         // If enabled, panning of the map will have an inertia effect where
 
10294         // the map builds momentum while dragging and continues moving in
 
10295         // the same direction for some time. Feels especially nice on touch
 
10296         // devices. Enabled by default unless running on old Android devices.
 
10297         inertia: !L.Browser.android23,
 
10299         // @option inertiaDeceleration: Number = 3000
 
10300         // The rate with which the inertial movement slows down, in pixels/second².
 
10301         inertiaDeceleration: 3400, // px/s^2
 
10303         // @option inertiaMaxSpeed: Number = Infinity
 
10304         // Max speed of the inertial movement, in pixels/second.
 
10305         inertiaMaxSpeed: Infinity, // px/s
 
10307         // @option easeLinearity: Number = 0.2
 
10308         easeLinearity: 0.2,
 
10310         // TODO refactor, move to CRS
 
10311         // @option worldCopyJump: Boolean = false
 
10312         // With this option enabled, the map tracks when you pan to another "copy"
 
10313         // of the world and seamlessly jumps to the original one so that all overlays
 
10314         // like markers and vector layers are still visible.
 
10315         worldCopyJump: false,
 
10317         // @option maxBoundsViscosity: Number = 0.0
 
10318         // If `maxBounds` is set, this option will control how solid the bounds
 
10319         // are when dragging the map around. The default value of `0.0` allows the
 
10320         // user to drag outside the bounds at normal speed, higher values will
 
10321         // slow down map dragging outside bounds, and `1.0` makes the bounds fully
 
10322         // solid, preventing the user from dragging outside the bounds.
 
10323         maxBoundsViscosity: 0.0
 
10326 L.Map.Drag = L.Handler.extend({
 
10327         addHooks: function () {
 
10328                 if (!this._draggable) {
 
10329                         var map = this._map;
 
10331                         this._draggable = new L.Draggable(map._mapPane, map._container);
 
10333                         this._draggable.on({
 
10334                                 down: this._onDown,
 
10335                                 dragstart: this._onDragStart,
 
10336                                 drag: this._onDrag,
 
10337                                 dragend: this._onDragEnd
 
10340                         this._draggable.on('predrag', this._onPreDragLimit, this);
 
10341                         if (map.options.worldCopyJump) {
 
10342                                 this._draggable.on('predrag', this._onPreDragWrap, this);
 
10343                                 map.on('zoomend', this._onZoomEnd, this);
 
10345                                 map.whenReady(this._onZoomEnd, this);
 
10348                 L.DomUtil.addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
 
10349                 this._draggable.enable();
 
10350                 this._positions = [];
 
10354         removeHooks: function () {
 
10355                 L.DomUtil.removeClass(this._map._container, 'leaflet-grab');
 
10356                 L.DomUtil.removeClass(this._map._container, 'leaflet-touch-drag');
 
10357                 this._draggable.disable();
 
10360         moved: function () {
 
10361                 return this._draggable && this._draggable._moved;
 
10364         moving: function () {
 
10365                 return this._draggable && this._draggable._moving;
 
10368         _onDown: function () {
 
10372         _onDragStart: function () {
 
10373                 var map = this._map;
 
10375                 if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
 
10376                         var bounds = L.latLngBounds(this._map.options.maxBounds);
 
10378                         this._offsetLimit = L.bounds(
 
10379                                 this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
 
10380                                 this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
 
10381                                         .add(this._map.getSize()));
 
10383                         this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
 
10385                         this._offsetLimit = null;
 
10390                     .fire('dragstart');
 
10392                 if (map.options.inertia) {
 
10393                         this._positions = [];
 
10398         _onDrag: function (e) {
 
10399                 if (this._map.options.inertia) {
 
10400                         var time = this._lastTime = +new Date(),
 
10401                             pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
 
10403                         this._positions.push(pos);
 
10404                         this._times.push(time);
 
10406                         if (time - this._times[0] > 50) {
 
10407                                 this._positions.shift();
 
10408                                 this._times.shift();
 
10417         _onZoomEnd: function () {
 
10418                 var pxCenter = this._map.getSize().divideBy(2),
 
10419                     pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
 
10421                 this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
 
10422                 this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
 
10425         _viscousLimit: function (value, threshold) {
 
10426                 return value - (value - threshold) * this._viscosity;
 
10429         _onPreDragLimit: function () {
 
10430                 if (!this._viscosity || !this._offsetLimit) { return; }
 
10432                 var offset = this._draggable._newPos.subtract(this._draggable._startPos);
 
10434                 var limit = this._offsetLimit;
 
10435                 if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
 
10436                 if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
 
10437                 if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
 
10438                 if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
 
10440                 this._draggable._newPos = this._draggable._startPos.add(offset);
 
10443         _onPreDragWrap: function () {
 
10444                 // TODO refactor to be able to adjust map pane position after zoom
 
10445                 var worldWidth = this._worldWidth,
 
10446                     halfWidth = Math.round(worldWidth / 2),
 
10447                     dx = this._initialWorldOffset,
 
10448                     x = this._draggable._newPos.x,
 
10449                     newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
 
10450                     newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
 
10451                     newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
 
10453                 this._draggable._absPos = this._draggable._newPos.clone();
 
10454                 this._draggable._newPos.x = newX;
 
10457         _onDragEnd: function (e) {
 
10458                 var map = this._map,
 
10459                     options = map.options,
 
10461                     noInertia = !options.inertia || this._times.length < 2;
 
10463                 map.fire('dragend', e);
 
10466                         map.fire('moveend');
 
10470                         var direction = this._lastPos.subtract(this._positions[0]),
 
10471                             duration = (this._lastTime - this._times[0]) / 1000,
 
10472                             ease = options.easeLinearity,
 
10474                             speedVector = direction.multiplyBy(ease / duration),
 
10475                             speed = speedVector.distanceTo([0, 0]),
 
10477                             limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
 
10478                             limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
 
10480                             decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
 
10481                             offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
 
10483                         if (!offset.x && !offset.y) {
 
10484                                 map.fire('moveend');
 
10487                                 offset = map._limitOffset(offset, map.options.maxBounds);
 
10489                                 L.Util.requestAnimFrame(function () {
 
10490                                         map.panBy(offset, {
 
10491                                                 duration: decelerationDuration,
 
10492                                                 easeLinearity: ease,
 
10502 // @section Handlers
 
10503 // @property dragging: Handler
 
10504 // Map dragging handler (by both mouse and touch).
 
10505 L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag);
 
10510  * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
 
10514 // @section Interaction Options
 
10516 L.Map.mergeOptions({
 
10517         // @option doubleClickZoom: Boolean|String = true
 
10518         // Whether the map can be zoomed in by double clicking on it and
 
10519         // zoomed out by double clicking while holding shift. If passed
 
10520         // `'center'`, double-click zoom will zoom to the center of the
 
10521         //  view regardless of where the mouse was.
 
10522         doubleClickZoom: true
 
10525 L.Map.DoubleClickZoom = L.Handler.extend({
 
10526         addHooks: function () {
 
10527                 this._map.on('dblclick', this._onDoubleClick, this);
 
10530         removeHooks: function () {
 
10531                 this._map.off('dblclick', this._onDoubleClick, this);
 
10534         _onDoubleClick: function (e) {
 
10535                 var map = this._map,
 
10536                     oldZoom = map.getZoom(),
 
10537                     delta = map.options.zoomDelta,
 
10538                     zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
 
10540                 if (map.options.doubleClickZoom === 'center') {
 
10543                         map.setZoomAround(e.containerPoint, zoom);
 
10548 // @section Handlers
 
10550 // Map properties include interaction handlers that allow you to control
 
10551 // interaction behavior in runtime, enabling or disabling certain features such
 
10552 // as dragging or touch zoom (see `Handler` methods). For example:
 
10555 // map.doubleClickZoom.disable();
 
10558 // @property doubleClickZoom: Handler
 
10559 // Double click zoom handler.
 
10560 L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom);
 
10565  * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
 
10569 // @section Interaction Options
 
10570 L.Map.mergeOptions({
 
10571         // @section Mousewheel options
 
10572         // @option scrollWheelZoom: Boolean|String = true
 
10573         // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
 
10574         // it will zoom to the center of the view regardless of where the mouse was.
 
10575         scrollWheelZoom: true,
 
10577         // @option wheelDebounceTime: Number = 40
 
10578         // Limits the rate at which a wheel can fire (in milliseconds). By default
 
10579         // user can't zoom via wheel more often than once per 40 ms.
 
10580         wheelDebounceTime: 40,
 
10582         // @option wheelPxPerZoomLevel: Number = 60
 
10583         // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
 
10584         // mean a change of one full zoom level. Smaller values will make wheel-zooming
 
10585         // faster (and vice versa).
 
10586         wheelPxPerZoomLevel: 60
 
10589 L.Map.ScrollWheelZoom = L.Handler.extend({
 
10590         addHooks: function () {
 
10591                 L.DomEvent.on(this._map._container, 'mousewheel', this._onWheelScroll, this);
 
10596         removeHooks: function () {
 
10597                 L.DomEvent.off(this._map._container, 'mousewheel', this._onWheelScroll, this);
 
10600         _onWheelScroll: function (e) {
 
10601                 var delta = L.DomEvent.getWheelDelta(e);
 
10603                 var debounce = this._map.options.wheelDebounceTime;
 
10605                 this._delta += delta;
 
10606                 this._lastMousePos = this._map.mouseEventToContainerPoint(e);
 
10608                 if (!this._startTime) {
 
10609                         this._startTime = +new Date();
 
10612                 var left = Math.max(debounce - (+new Date() - this._startTime), 0);
 
10614                 clearTimeout(this._timer);
 
10615                 this._timer = setTimeout(L.bind(this._performZoom, this), left);
 
10617                 L.DomEvent.stop(e);
 
10620         _performZoom: function () {
 
10621                 var map = this._map,
 
10622                     zoom = map.getZoom(),
 
10623                     snap = this._map.options.zoomSnap || 0;
 
10625                 map._stop(); // stop panning and fly animations if any
 
10627                 // map the delta with a sigmoid function to -4..4 range leaning on -1..1
 
10628                 var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
 
10629                     d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
 
10630                     d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
 
10631                     delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
 
10634                 this._startTime = null;
 
10636                 if (!delta) { return; }
 
10638                 if (map.options.scrollWheelZoom === 'center') {
 
10639                         map.setZoom(zoom + delta);
 
10641                         map.setZoomAround(this._lastMousePos, zoom + delta);
 
10646 // @section Handlers
 
10647 // @property scrollWheelZoom: Handler
 
10648 // Scroll wheel zoom handler.
 
10649 L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom);
 
10654  * Extends the event handling code with double tap support for mobile browsers.
 
10657 L.extend(L.DomEvent, {
 
10659         _touchstart: L.Browser.msPointer ? 'MSPointerDown' : L.Browser.pointer ? 'pointerdown' : 'touchstart',
 
10660         _touchend: L.Browser.msPointer ? 'MSPointerUp' : L.Browser.pointer ? 'pointerup' : 'touchend',
 
10662         // inspired by Zepto touch code by Thomas Fuchs
 
10663         addDoubleTapListener: function (obj, handler, id) {
 
10668                 function onTouchStart(e) {
 
10671                         if (L.Browser.pointer) {
 
10672                                 count = L.DomEvent._pointersCount;
 
10674                                 count = e.touches.length;
 
10677                         if (count > 1) { return; }
 
10679                         var now = Date.now(),
 
10680                             delta = now - (last || now);
 
10682                         touch = e.touches ? e.touches[0] : e;
 
10683                         doubleTap = (delta > 0 && delta <= delay);
 
10687                 function onTouchEnd() {
 
10688                         if (doubleTap && !touch.cancelBubble) {
 
10689                                 if (L.Browser.pointer) {
 
10690                                         // work around .type being readonly with MSPointer* events
 
10696                                                 newTouch[i] = prop && prop.bind ? prop.bind(touch) : prop;
 
10700                                 touch.type = 'dblclick';
 
10706                 var pre = '_leaflet_',
 
10707                     touchstart = this._touchstart,
 
10708                     touchend = this._touchend;
 
10710                 obj[pre + touchstart + id] = onTouchStart;
 
10711                 obj[pre + touchend + id] = onTouchEnd;
 
10712                 obj[pre + 'dblclick' + id] = handler;
 
10714                 obj.addEventListener(touchstart, onTouchStart, false);
 
10715                 obj.addEventListener(touchend, onTouchEnd, false);
 
10717                 // On some platforms (notably, chrome on win10 + touchscreen + mouse),
 
10718                 // the browser doesn't fire touchend/pointerup events but does fire
 
10719                 // native dblclicks. See #4127.
 
10720                 if (!L.Browser.edge) {
 
10721                         obj.addEventListener('dblclick', handler, false);
 
10727         removeDoubleTapListener: function (obj, id) {
 
10728                 var pre = '_leaflet_',
 
10729                     touchstart = obj[pre + this._touchstart + id],
 
10730                     touchend = obj[pre + this._touchend + id],
 
10731                     dblclick = obj[pre + 'dblclick' + id];
 
10733                 obj.removeEventListener(this._touchstart, touchstart, false);
 
10734                 obj.removeEventListener(this._touchend, touchend, false);
 
10735                 if (!L.Browser.edge) {
 
10736                         obj.removeEventListener('dblclick', dblclick, false);
 
10746  * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
 
10749 L.extend(L.DomEvent, {
 
10751         POINTER_DOWN:   L.Browser.msPointer ? 'MSPointerDown'   : 'pointerdown',
 
10752         POINTER_MOVE:   L.Browser.msPointer ? 'MSPointerMove'   : 'pointermove',
 
10753         POINTER_UP:     L.Browser.msPointer ? 'MSPointerUp'     : 'pointerup',
 
10754         POINTER_CANCEL: L.Browser.msPointer ? 'MSPointerCancel' : 'pointercancel',
 
10755         TAG_WHITE_LIST: ['INPUT', 'SELECT', 'OPTION'],
 
10760         // Provides a touch events wrapper for (ms)pointer events.
 
10761         // ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
 
10763         addPointerListener: function (obj, type, handler, id) {
 
10765                 if (type === 'touchstart') {
 
10766                         this._addPointerStart(obj, handler, id);
 
10768                 } else if (type === 'touchmove') {
 
10769                         this._addPointerMove(obj, handler, id);
 
10771                 } else if (type === 'touchend') {
 
10772                         this._addPointerEnd(obj, handler, id);
 
10778         removePointerListener: function (obj, type, id) {
 
10779                 var handler = obj['_leaflet_' + type + id];
 
10781                 if (type === 'touchstart') {
 
10782                         obj.removeEventListener(this.POINTER_DOWN, handler, false);
 
10784                 } else if (type === 'touchmove') {
 
10785                         obj.removeEventListener(this.POINTER_MOVE, handler, false);
 
10787                 } else if (type === 'touchend') {
 
10788                         obj.removeEventListener(this.POINTER_UP, handler, false);
 
10789                         obj.removeEventListener(this.POINTER_CANCEL, handler, false);
 
10795         _addPointerStart: function (obj, handler, id) {
 
10796                 var onDown = L.bind(function (e) {
 
10797                         if (e.pointerType !== 'mouse' && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
 
10798                                 // In IE11, some touch events needs to fire for form controls, or
 
10799                                 // the controls will stop working. We keep a whitelist of tag names that
 
10800                                 // need these events. For other target tags, we prevent default on the event.
 
10801                                 if (this.TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
 
10802                                         L.DomEvent.preventDefault(e);
 
10808                         this._handlePointer(e, handler);
 
10811                 obj['_leaflet_touchstart' + id] = onDown;
 
10812                 obj.addEventListener(this.POINTER_DOWN, onDown, false);
 
10814                 // need to keep track of what pointers and how many are active to provide e.touches emulation
 
10815                 if (!this._pointerDocListener) {
 
10816                         var pointerUp = L.bind(this._globalPointerUp, this);
 
10818                         // we listen documentElement as any drags that end by moving the touch off the screen get fired there
 
10819                         document.documentElement.addEventListener(this.POINTER_DOWN, L.bind(this._globalPointerDown, this), true);
 
10820                         document.documentElement.addEventListener(this.POINTER_MOVE, L.bind(this._globalPointerMove, this), true);
 
10821                         document.documentElement.addEventListener(this.POINTER_UP, pointerUp, true);
 
10822                         document.documentElement.addEventListener(this.POINTER_CANCEL, pointerUp, true);
 
10824                         this._pointerDocListener = true;
 
10828         _globalPointerDown: function (e) {
 
10829                 this._pointers[e.pointerId] = e;
 
10830                 this._pointersCount++;
 
10833         _globalPointerMove: function (e) {
 
10834                 if (this._pointers[e.pointerId]) {
 
10835                         this._pointers[e.pointerId] = e;
 
10839         _globalPointerUp: function (e) {
 
10840                 delete this._pointers[e.pointerId];
 
10841                 this._pointersCount--;
 
10844         _handlePointer: function (e, handler) {
 
10846                 for (var i in this._pointers) {
 
10847                         e.touches.push(this._pointers[i]);
 
10849                 e.changedTouches = [e];
 
10854         _addPointerMove: function (obj, handler, id) {
 
10855                 var onMove = L.bind(function (e) {
 
10856                         // don't fire touch moves when mouse isn't down
 
10857                         if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
 
10859                         this._handlePointer(e, handler);
 
10862                 obj['_leaflet_touchmove' + id] = onMove;
 
10863                 obj.addEventListener(this.POINTER_MOVE, onMove, false);
 
10866         _addPointerEnd: function (obj, handler, id) {
 
10867                 var onUp = L.bind(function (e) {
 
10868                         this._handlePointer(e, handler);
 
10871                 obj['_leaflet_touchend' + id] = onUp;
 
10872                 obj.addEventListener(this.POINTER_UP, onUp, false);
 
10873                 obj.addEventListener(this.POINTER_CANCEL, onUp, false);
 
10880  * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
 
10884 // @section Interaction Options
 
10885 L.Map.mergeOptions({
 
10886         // @section Touch interaction options
 
10887         // @option touchZoom: Boolean|String = *
 
10888         // Whether the map can be zoomed by touch-dragging with two fingers. If
 
10889         // passed `'center'`, it will zoom to the center of the view regardless of
 
10890         // where the touch events (fingers) were. Enabled for touch-capable web
 
10891         // browsers except for old Androids.
 
10892         touchZoom: L.Browser.touch && !L.Browser.android23,
 
10894         // @option bounceAtZoomLimits: Boolean = true
 
10895         // Set it to false if you don't want the map to zoom beyond min/max zoom
 
10896         // and then bounce back when pinch-zooming.
 
10897         bounceAtZoomLimits: true
 
10900 L.Map.TouchZoom = L.Handler.extend({
 
10901         addHooks: function () {
 
10902                 L.DomUtil.addClass(this._map._container, 'leaflet-touch-zoom');
 
10903                 L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);
 
10906         removeHooks: function () {
 
10907                 L.DomUtil.removeClass(this._map._container, 'leaflet-touch-zoom');
 
10908                 L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this);
 
10911         _onTouchStart: function (e) {
 
10912                 var map = this._map;
 
10913                 if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
 
10915                 var p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
10916                     p2 = map.mouseEventToContainerPoint(e.touches[1]);
 
10918                 this._centerPoint = map.getSize()._divideBy(2);
 
10919                 this._startLatLng = map.containerPointToLatLng(this._centerPoint);
 
10920                 if (map.options.touchZoom !== 'center') {
 
10921                         this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
 
10924                 this._startDist = p1.distanceTo(p2);
 
10925                 this._startZoom = map.getZoom();
 
10927                 this._moved = false;
 
10928                 this._zooming = true;
 
10933                     .on(document, 'touchmove', this._onTouchMove, this)
 
10934                     .on(document, 'touchend', this._onTouchEnd, this);
 
10936                 L.DomEvent.preventDefault(e);
 
10939         _onTouchMove: function (e) {
 
10940                 if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
 
10942                 var map = this._map,
 
10943                     p1 = map.mouseEventToContainerPoint(e.touches[0]),
 
10944                     p2 = map.mouseEventToContainerPoint(e.touches[1]),
 
10945                     scale = p1.distanceTo(p2) / this._startDist;
 
10948                 this._zoom = map.getScaleZoom(scale, this._startZoom);
 
10950                 if (!map.options.bounceAtZoomLimits && (
 
10951                         (this._zoom < map.getMinZoom() && scale < 1) ||
 
10952                         (this._zoom > map.getMaxZoom() && scale > 1))) {
 
10953                         this._zoom = map._limitZoom(this._zoom);
 
10956                 if (map.options.touchZoom === 'center') {
 
10957                         this._center = this._startLatLng;
 
10958                         if (scale === 1) { return; }
 
10960                         // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
 
10961                         var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
 
10962                         if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
 
10963                         this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
 
10966                 if (!this._moved) {
 
10967                         map._moveStart(true);
 
10968                         this._moved = true;
 
10971                 L.Util.cancelAnimFrame(this._animRequest);
 
10973                 var moveFn = L.bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
 
10974                 this._animRequest = L.Util.requestAnimFrame(moveFn, this, true);
 
10976                 L.DomEvent.preventDefault(e);
 
10979         _onTouchEnd: function () {
 
10980                 if (!this._moved || !this._zooming) {
 
10981                         this._zooming = false;
 
10985                 this._zooming = false;
 
10986                 L.Util.cancelAnimFrame(this._animRequest);
 
10989                     .off(document, 'touchmove', this._onTouchMove)
 
10990                     .off(document, 'touchend', this._onTouchEnd);
 
10992                 // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
 
10993                 if (this._map.options.zoomAnimation) {
 
10994                         this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
 
10996                         this._map._resetView(this._center, this._map._limitZoom(this._zoom));
 
11001 // @section Handlers
 
11002 // @property touchZoom: Handler
 
11003 // Touch zoom handler.
 
11004 L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);
 
11009  * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
 
11013 // @section Interaction Options
 
11014 L.Map.mergeOptions({
 
11015         // @section Touch interaction options
 
11016         // @option tap: Boolean = true
 
11017         // Enables mobile hacks for supporting instant taps (fixing 200ms click
 
11018         // delay on iOS/Android) and touch holds (fired as `contextmenu` events).
 
11021         // @option tapTolerance: Number = 15
 
11022         // The max number of pixels a user can shift his finger during touch
 
11023         // for it to be considered a valid tap.
 
11027 L.Map.Tap = L.Handler.extend({
 
11028         addHooks: function () {
 
11029                 L.DomEvent.on(this._map._container, 'touchstart', this._onDown, this);
 
11032         removeHooks: function () {
 
11033                 L.DomEvent.off(this._map._container, 'touchstart', this._onDown, this);
 
11036         _onDown: function (e) {
 
11037                 if (!e.touches) { return; }
 
11039                 L.DomEvent.preventDefault(e);
 
11041                 this._fireClick = true;
 
11043                 // don't simulate click or track longpress if more than 1 touch
 
11044                 if (e.touches.length > 1) {
 
11045                         this._fireClick = false;
 
11046                         clearTimeout(this._holdTimeout);
 
11050                 var first = e.touches[0],
 
11053                 this._startPos = this._newPos = new L.Point(first.clientX, first.clientY);
 
11055                 // if touching a link, highlight it
 
11056                 if (el.tagName && el.tagName.toLowerCase() === 'a') {
 
11057                         L.DomUtil.addClass(el, 'leaflet-active');
 
11060                 // simulate long hold but setting a timeout
 
11061                 this._holdTimeout = setTimeout(L.bind(function () {
 
11062                         if (this._isTapValid()) {
 
11063                                 this._fireClick = false;
 
11065                                 this._simulateEvent('contextmenu', first);
 
11069                 this._simulateEvent('mousedown', first);
 
11071                 L.DomEvent.on(document, {
 
11072                         touchmove: this._onMove,
 
11073                         touchend: this._onUp
 
11077         _onUp: function (e) {
 
11078                 clearTimeout(this._holdTimeout);
 
11080                 L.DomEvent.off(document, {
 
11081                         touchmove: this._onMove,
 
11082                         touchend: this._onUp
 
11085                 if (this._fireClick && e && e.changedTouches) {
 
11087                         var first = e.changedTouches[0],
 
11090                         if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
 
11091                                 L.DomUtil.removeClass(el, 'leaflet-active');
 
11094                         this._simulateEvent('mouseup', first);
 
11096                         // simulate click if the touch didn't move too much
 
11097                         if (this._isTapValid()) {
 
11098                                 this._simulateEvent('click', first);
 
11103         _isTapValid: function () {
 
11104                 return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
 
11107         _onMove: function (e) {
 
11108                 var first = e.touches[0];
 
11109                 this._newPos = new L.Point(first.clientX, first.clientY);
 
11110                 this._simulateEvent('mousemove', first);
 
11113         _simulateEvent: function (type, e) {
 
11114                 var simulatedEvent = document.createEvent('MouseEvents');
 
11116                 simulatedEvent._simulated = true;
 
11117                 e.target._simulatedClick = true;
 
11119                 simulatedEvent.initMouseEvent(
 
11120                         type, true, true, window, 1,
 
11121                         e.screenX, e.screenY,
 
11122                         e.clientX, e.clientY,
 
11123                         false, false, false, false, 0, null);
 
11125                 e.target.dispatchEvent(simulatedEvent);
 
11129 // @section Handlers
 
11130 // @property tap: Handler
 
11131 // Mobile touch hacks (quick tap and touch hold) handler.
 
11132 if (L.Browser.touch && !L.Browser.pointer) {
 
11133         L.Map.addInitHook('addHandler', 'tap', L.Map.Tap);
 
11139  * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
 
11140  * (zoom to a selected bounding box), enabled by default.
 
11144 // @section Interaction Options
 
11145 L.Map.mergeOptions({
 
11146         // @option boxZoom: Boolean = true
 
11147         // Whether the map can be zoomed to a rectangular area specified by
 
11148         // dragging the mouse while pressing the shift key.
 
11152 L.Map.BoxZoom = L.Handler.extend({
 
11153         initialize: function (map) {
 
11155                 this._container = map._container;
 
11156                 this._pane = map._panes.overlayPane;
 
11159         addHooks: function () {
 
11160                 L.DomEvent.on(this._container, 'mousedown', this._onMouseDown, this);
 
11163         removeHooks: function () {
 
11164                 L.DomEvent.off(this._container, 'mousedown', this._onMouseDown, this);
 
11167         moved: function () {
 
11168                 return this._moved;
 
11171         _resetState: function () {
 
11172                 this._moved = false;
 
11175         _onMouseDown: function (e) {
 
11176                 if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
 
11178                 this._resetState();
 
11180                 L.DomUtil.disableTextSelection();
 
11181                 L.DomUtil.disableImageDrag();
 
11183                 this._startPoint = this._map.mouseEventToContainerPoint(e);
 
11185                 L.DomEvent.on(document, {
 
11186                         contextmenu: L.DomEvent.stop,
 
11187                         mousemove: this._onMouseMove,
 
11188                         mouseup: this._onMouseUp,
 
11189                         keydown: this._onKeyDown
 
11193         _onMouseMove: function (e) {
 
11194                 if (!this._moved) {
 
11195                         this._moved = true;
 
11197                         this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._container);
 
11198                         L.DomUtil.addClass(this._container, 'leaflet-crosshair');
 
11200                         this._map.fire('boxzoomstart');
 
11203                 this._point = this._map.mouseEventToContainerPoint(e);
 
11205                 var bounds = new L.Bounds(this._point, this._startPoint),
 
11206                     size = bounds.getSize();
 
11208                 L.DomUtil.setPosition(this._box, bounds.min);
 
11210                 this._box.style.width  = size.x + 'px';
 
11211                 this._box.style.height = size.y + 'px';
 
11214         _finish: function () {
 
11216                         L.DomUtil.remove(this._box);
 
11217                         L.DomUtil.removeClass(this._container, 'leaflet-crosshair');
 
11220                 L.DomUtil.enableTextSelection();
 
11221                 L.DomUtil.enableImageDrag();
 
11223                 L.DomEvent.off(document, {
 
11224                         contextmenu: L.DomEvent.stop,
 
11225                         mousemove: this._onMouseMove,
 
11226                         mouseup: this._onMouseUp,
 
11227                         keydown: this._onKeyDown
 
11231         _onMouseUp: function (e) {
 
11232                 if ((e.which !== 1) && (e.button !== 1)) { return; }
 
11236                 if (!this._moved) { return; }
 
11237                 // Postpone to next JS tick so internal click event handling
 
11238                 // still see it as "moved".
 
11239                 setTimeout(L.bind(this._resetState, this), 0);
 
11241                 var bounds = new L.LatLngBounds(
 
11242                         this._map.containerPointToLatLng(this._startPoint),
 
11243                         this._map.containerPointToLatLng(this._point));
 
11247                         .fire('boxzoomend', {boxZoomBounds: bounds});
 
11250         _onKeyDown: function (e) {
 
11251                 if (e.keyCode === 27) {
 
11257 // @section Handlers
 
11258 // @property boxZoom: Handler
 
11259 // Box (shift-drag with mouse) zoom handler.
 
11260 L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom);
 
11265  * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
 
11269 // @section Keyboard Navigation Options
 
11270 L.Map.mergeOptions({
 
11271         // @option keyboard: Boolean = true
 
11272         // Makes the map focusable and allows users to navigate the map with keyboard
 
11273         // arrows and `+`/`-` keys.
 
11276         // @option keyboardPanDelta: Number = 80
 
11277         // Amount of pixels to pan when pressing an arrow key.
 
11278         keyboardPanDelta: 80
 
11281 L.Map.Keyboard = L.Handler.extend({
 
11288                 zoomIn:  [187, 107, 61, 171],
 
11289                 zoomOut: [189, 109, 54, 173]
 
11292         initialize: function (map) {
 
11295                 this._setPanDelta(map.options.keyboardPanDelta);
 
11296                 this._setZoomDelta(map.options.zoomDelta);
 
11299         addHooks: function () {
 
11300                 var container = this._map._container;
 
11302                 // make the container focusable by tabbing
 
11303                 if (container.tabIndex <= 0) {
 
11304                         container.tabIndex = '0';
 
11307                 L.DomEvent.on(container, {
 
11308                         focus: this._onFocus,
 
11309                         blur: this._onBlur,
 
11310                         mousedown: this._onMouseDown
 
11314                         focus: this._addHooks,
 
11315                         blur: this._removeHooks
 
11319         removeHooks: function () {
 
11320                 this._removeHooks();
 
11322                 L.DomEvent.off(this._map._container, {
 
11323                         focus: this._onFocus,
 
11324                         blur: this._onBlur,
 
11325                         mousedown: this._onMouseDown
 
11329                         focus: this._addHooks,
 
11330                         blur: this._removeHooks
 
11334         _onMouseDown: function () {
 
11335                 if (this._focused) { return; }
 
11337                 var body = document.body,
 
11338                     docEl = document.documentElement,
 
11339                     top = body.scrollTop || docEl.scrollTop,
 
11340                     left = body.scrollLeft || docEl.scrollLeft;
 
11342                 this._map._container.focus();
 
11344                 window.scrollTo(left, top);
 
11347         _onFocus: function () {
 
11348                 this._focused = true;
 
11349                 this._map.fire('focus');
 
11352         _onBlur: function () {
 
11353                 this._focused = false;
 
11354                 this._map.fire('blur');
 
11357         _setPanDelta: function (panDelta) {
 
11358                 var keys = this._panKeys = {},
 
11359                     codes = this.keyCodes,
 
11362                 for (i = 0, len = codes.left.length; i < len; i++) {
 
11363                         keys[codes.left[i]] = [-1 * panDelta, 0];
 
11365                 for (i = 0, len = codes.right.length; i < len; i++) {
 
11366                         keys[codes.right[i]] = [panDelta, 0];
 
11368                 for (i = 0, len = codes.down.length; i < len; i++) {
 
11369                         keys[codes.down[i]] = [0, panDelta];
 
11371                 for (i = 0, len = codes.up.length; i < len; i++) {
 
11372                         keys[codes.up[i]] = [0, -1 * panDelta];
 
11376         _setZoomDelta: function (zoomDelta) {
 
11377                 var keys = this._zoomKeys = {},
 
11378                     codes = this.keyCodes,
 
11381                 for (i = 0, len = codes.zoomIn.length; i < len; i++) {
 
11382                         keys[codes.zoomIn[i]] = zoomDelta;
 
11384                 for (i = 0, len = codes.zoomOut.length; i < len; i++) {
 
11385                         keys[codes.zoomOut[i]] = -zoomDelta;
 
11389         _addHooks: function () {
 
11390                 L.DomEvent.on(document, 'keydown', this._onKeyDown, this);
 
11393         _removeHooks: function () {
 
11394                 L.DomEvent.off(document, 'keydown', this._onKeyDown, this);
 
11397         _onKeyDown: function (e) {
 
11398                 if (e.altKey || e.ctrlKey || e.metaKey) { return; }
 
11400                 var key = e.keyCode,
 
11404                 if (key in this._panKeys) {
 
11406                         if (map._panAnim && map._panAnim._inProgress) { return; }
 
11408                         offset = this._panKeys[key];
 
11410                                 offset = L.point(offset).multiplyBy(3);
 
11415                         if (map.options.maxBounds) {
 
11416                                 map.panInsideBounds(map.options.maxBounds);
 
11419                 } else if (key in this._zoomKeys) {
 
11420                         map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
 
11422                 } else if (key === 27) {
 
11429                 L.DomEvent.stop(e);
 
11433 // @section Handlers
 
11434 // @section Handlers
 
11435 // @property keyboard: Handler
 
11436 // Keyboard navigation handler.
 
11437 L.Map.addInitHook('addHandler', 'keyboard', L.Map.Keyboard);
 
11442  * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
 
11446 /* @namespace Marker
 
11447  * @section Interaction handlers
 
11449  * 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:
 
11452  * marker.dragging.disable();
 
11455  * @property dragging: Handler
 
11456  * Marker dragging handler (by both mouse and touch).
 
11459 L.Handler.MarkerDrag = L.Handler.extend({
 
11460         initialize: function (marker) {
 
11461                 this._marker = marker;
 
11464         addHooks: function () {
 
11465                 var icon = this._marker._icon;
 
11467                 if (!this._draggable) {
 
11468                         this._draggable = new L.Draggable(icon, icon, true);
 
11471                 this._draggable.on({
 
11472                         dragstart: this._onDragStart,
 
11473                         drag: this._onDrag,
 
11474                         dragend: this._onDragEnd
 
11477                 L.DomUtil.addClass(icon, 'leaflet-marker-draggable');
 
11480         removeHooks: function () {
 
11481                 this._draggable.off({
 
11482                         dragstart: this._onDragStart,
 
11483                         drag: this._onDrag,
 
11484                         dragend: this._onDragEnd
 
11485                 }, this).disable();
 
11487                 if (this._marker._icon) {
 
11488                         L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable');
 
11492         moved: function () {
 
11493                 return this._draggable && this._draggable._moved;
 
11496         _onDragStart: function () {
 
11497                 // @section Dragging events
 
11498                 // @event dragstart: Event
 
11499                 // Fired when the user starts dragging the marker.
 
11501                 // @event movestart: Event
 
11502                 // Fired when the marker starts moving (because of dragging).
 
11504                 this._oldLatLng = this._marker.getLatLng();
 
11508                     .fire('dragstart');
 
11511         _onDrag: function (e) {
 
11512                 var marker = this._marker,
 
11513                     shadow = marker._shadow,
 
11514                     iconPos = L.DomUtil.getPosition(marker._icon),
 
11515                     latlng = marker._map.layerPointToLatLng(iconPos);
 
11517                 // update shadow position
 
11519                         L.DomUtil.setPosition(shadow, iconPos);
 
11522                 marker._latlng = latlng;
 
11524                 e.oldLatLng = this._oldLatLng;
 
11526                 // @event drag: Event
 
11527                 // Fired repeatedly while the user drags the marker.
 
11533         _onDragEnd: function (e) {
 
11534                 // @event dragend: DragEndEvent
 
11535                 // Fired when the user stops dragging the marker.
 
11537                 // @event moveend: Event
 
11538                 // Fired when the marker stops moving (because of dragging).
 
11539                 delete this._oldLatLng;
 
11542                     .fire('dragend', e);
 
11552  * L.Control is a base class for implementing map controls. Handles positioning.
 
11553  * All other controls extend from this class.
 
11556 L.Control = L.Class.extend({
 
11558         // @aka Control options
 
11560                 // @option position: String = 'topright'
 
11561                 // The position of the control (one of the map corners). Possible values are `'topleft'`,
 
11562                 // `'topright'`, `'bottomleft'` or `'bottomright'`
 
11563                 position: 'topright'
 
11566         initialize: function (options) {
 
11567                 L.setOptions(this, options);
 
11571          * Classes extending L.Control will inherit the following methods:
 
11573          * @method getPosition: string
 
11574          * Returns the position of the control.
 
11576         getPosition: function () {
 
11577                 return this.options.position;
 
11580         // @method setPosition(position: string): this
 
11581         // Sets the position of the control.
 
11582         setPosition: function (position) {
 
11583                 var map = this._map;
 
11586                         map.removeControl(this);
 
11589                 this.options.position = position;
 
11592                         map.addControl(this);
 
11598         // @method getContainer: HTMLElement
 
11599         // Returns the HTMLElement that contains the control.
 
11600         getContainer: function () {
 
11601                 return this._container;
 
11604         // @method addTo(map: Map): this
 
11605         // Adds the control to the given map.
 
11606         addTo: function (map) {
 
11610                 var container = this._container = this.onAdd(map),
 
11611                     pos = this.getPosition(),
 
11612                     corner = map._controlCorners[pos];
 
11614                 L.DomUtil.addClass(container, 'leaflet-control');
 
11616                 if (pos.indexOf('bottom') !== -1) {
 
11617                         corner.insertBefore(container, corner.firstChild);
 
11619                         corner.appendChild(container);
 
11625         // @method remove: this
 
11626         // Removes the control from the map it is currently active on.
 
11627         remove: function () {
 
11632                 L.DomUtil.remove(this._container);
 
11634                 if (this.onRemove) {
 
11635                         this.onRemove(this._map);
 
11643         _refocusOnMap: function (e) {
 
11644                 // if map exists and event is not a keyboard event
 
11645                 if (this._map && e && e.screenX > 0 && e.screenY > 0) {
 
11646                         this._map.getContainer().focus();
 
11651 L.control = function (options) {
 
11652         return new L.Control(options);
 
11655 /* @section Extension methods
 
11658  * Every control should extend from `L.Control` and (re-)implement the following methods.
 
11660  * @method onAdd(map: Map): HTMLElement
 
11661  * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
 
11663  * @method onRemove(map: Map)
 
11664  * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
 
11668  * @section Methods for Layers and Controls
 
11671         // @method addControl(control: Control): this
 
11672         // Adds the given control to the map
 
11673         addControl: function (control) {
 
11674                 control.addTo(this);
 
11678         // @method removeControl(control: Control): this
 
11679         // Removes the given control from the map
 
11680         removeControl: function (control) {
 
11685         _initControlPos: function () {
 
11686                 var corners = this._controlCorners = {},
 
11688                     container = this._controlContainer =
 
11689                             L.DomUtil.create('div', l + 'control-container', this._container);
 
11691                 function createCorner(vSide, hSide) {
 
11692                         var className = l + vSide + ' ' + l + hSide;
 
11694                         corners[vSide + hSide] = L.DomUtil.create('div', className, container);
 
11697                 createCorner('top', 'left');
 
11698                 createCorner('top', 'right');
 
11699                 createCorner('bottom', 'left');
 
11700                 createCorner('bottom', 'right');
 
11703         _clearControlPos: function () {
 
11704                 L.DomUtil.remove(this._controlContainer);
 
11711  * @class Control.Zoom
 
11712  * @aka L.Control.Zoom
 
11713  * @inherits Control
 
11715  * 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`.
 
11718 L.Control.Zoom = L.Control.extend({
 
11720         // @aka Control.Zoom options
 
11722                 position: 'topleft',
 
11724                 // @option zoomInText: String = '+'
 
11725                 // The text set on the 'zoom in' button.
 
11728                 // @option zoomInTitle: String = 'Zoom in'
 
11729                 // The title set on the 'zoom in' button.
 
11730                 zoomInTitle: 'Zoom in',
 
11732                 // @option zoomOutText: String = '-'
 
11733                 // The text set on the 'zoom out' button.
 
11736                 // @option zoomOutTitle: String = 'Zoom out'
 
11737                 // The title set on the 'zoom out' button.
 
11738                 zoomOutTitle: 'Zoom out'
 
11741         onAdd: function (map) {
 
11742                 var zoomName = 'leaflet-control-zoom',
 
11743                     container = L.DomUtil.create('div', zoomName + ' leaflet-bar'),
 
11744                     options = this.options;
 
11746                 this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
 
11747                         zoomName + '-in',  container, this._zoomIn);
 
11748                 this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
 
11749                         zoomName + '-out', container, this._zoomOut);
 
11751                 this._updateDisabled();
 
11752                 map.on('zoomend zoomlevelschange', this._updateDisabled, this);
 
11757         onRemove: function (map) {
 
11758                 map.off('zoomend zoomlevelschange', this._updateDisabled, this);
 
11761         disable: function () {
 
11762                 this._disabled = true;
 
11763                 this._updateDisabled();
 
11767         enable: function () {
 
11768                 this._disabled = false;
 
11769                 this._updateDisabled();
 
11773         _zoomIn: function (e) {
 
11774                 if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
 
11775                         this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
 
11779         _zoomOut: function (e) {
 
11780                 if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
 
11781                         this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
 
11785         _createButton: function (html, title, className, container, fn) {
 
11786                 var link = L.DomUtil.create('a', className, container);
 
11787                 link.innerHTML = html;
 
11789                 link.title = title;
 
11792                     .on(link, 'mousedown dblclick', L.DomEvent.stopPropagation)
 
11793                     .on(link, 'click', L.DomEvent.stop)
 
11794                     .on(link, 'click', fn, this)
 
11795                     .on(link, 'click', this._refocusOnMap, this);
 
11800         _updateDisabled: function () {
 
11801                 var map = this._map,
 
11802                     className = 'leaflet-disabled';
 
11804                 L.DomUtil.removeClass(this._zoomInButton, className);
 
11805                 L.DomUtil.removeClass(this._zoomOutButton, className);
 
11807                 if (this._disabled || map._zoom === map.getMinZoom()) {
 
11808                         L.DomUtil.addClass(this._zoomOutButton, className);
 
11810                 if (this._disabled || map._zoom === map.getMaxZoom()) {
 
11811                         L.DomUtil.addClass(this._zoomInButton, className);
 
11817 // @section Control options
 
11818 // @option zoomControl: Boolean = true
 
11819 // Whether a [zoom control](#control-zoom) is added to the map by default.
 
11820 L.Map.mergeOptions({
 
11824 L.Map.addInitHook(function () {
 
11825         if (this.options.zoomControl) {
 
11826                 this.zoomControl = new L.Control.Zoom();
 
11827                 this.addControl(this.zoomControl);
 
11831 // @namespace Control.Zoom
 
11832 // @factory L.control.zoom(options: Control.Zoom options)
 
11833 // Creates a zoom control
 
11834 L.control.zoom = function (options) {
 
11835         return new L.Control.Zoom(options);
 
11841  * @class Control.Attribution
 
11842  * @aka L.Control.Attribution
 
11843  * @inherits Control
 
11845  * 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.
 
11848 L.Control.Attribution = L.Control.extend({
 
11850         // @aka Control.Attribution options
 
11852                 position: 'bottomright',
 
11854                 // @option prefix: String = 'Leaflet'
 
11855                 // The HTML text shown before the attributions. Pass `false` to disable.
 
11856                 prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
 
11859         initialize: function (options) {
 
11860                 L.setOptions(this, options);
 
11862                 this._attributions = {};
 
11865         onAdd: function (map) {
 
11866                 map.attributionControl = this;
 
11867                 this._container = L.DomUtil.create('div', 'leaflet-control-attribution');
 
11869                         L.DomEvent.disableClickPropagation(this._container);
 
11872                 // TODO ugly, refactor
 
11873                 for (var i in map._layers) {
 
11874                         if (map._layers[i].getAttribution) {
 
11875                                 this.addAttribution(map._layers[i].getAttribution());
 
11881                 return this._container;
 
11884         // @method setPrefix(prefix: String): this
 
11885         // Sets the text before the attributions.
 
11886         setPrefix: function (prefix) {
 
11887                 this.options.prefix = prefix;
 
11892         // @method addAttribution(text: String): this
 
11893         // Adds an attribution text (e.g. `'Vector data © Mapbox'`).
 
11894         addAttribution: function (text) {
 
11895                 if (!text) { return this; }
 
11897                 if (!this._attributions[text]) {
 
11898                         this._attributions[text] = 0;
 
11900                 this._attributions[text]++;
 
11907         // @method removeAttribution(text: String): this
 
11908         // Removes an attribution text.
 
11909         removeAttribution: function (text) {
 
11910                 if (!text) { return this; }
 
11912                 if (this._attributions[text]) {
 
11913                         this._attributions[text]--;
 
11920         _update: function () {
 
11921                 if (!this._map) { return; }
 
11925                 for (var i in this._attributions) {
 
11926                         if (this._attributions[i]) {
 
11931                 var prefixAndAttribs = [];
 
11933                 if (this.options.prefix) {
 
11934                         prefixAndAttribs.push(this.options.prefix);
 
11936                 if (attribs.length) {
 
11937                         prefixAndAttribs.push(attribs.join(', '));
 
11940                 this._container.innerHTML = prefixAndAttribs.join(' | ');
 
11945 // @section Control options
 
11946 // @option attributionControl: Boolean = true
 
11947 // Whether a [attribution control](#control-attribution) is added to the map by default.
 
11948 L.Map.mergeOptions({
 
11949         attributionControl: true
 
11952 L.Map.addInitHook(function () {
 
11953         if (this.options.attributionControl) {
 
11954                 new L.Control.Attribution().addTo(this);
 
11958 // @namespace Control.Attribution
 
11959 // @factory L.control.attribution(options: Control.Attribution options)
 
11960 // Creates an attribution control.
 
11961 L.control.attribution = function (options) {
 
11962         return new L.Control.Attribution(options);
 
11968  * @class Control.Scale
 
11969  * @aka L.Control.Scale
 
11970  * @inherits Control
 
11972  * 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`.
 
11977  * L.control.scale().addTo(map);
 
11981 L.Control.Scale = L.Control.extend({
 
11983         // @aka Control.Scale options
 
11985                 position: 'bottomleft',
 
11987                 // @option maxWidth: Number = 100
 
11988                 // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
 
11991                 // @option metric: Boolean = True
 
11992                 // Whether to show the metric scale line (m/km).
 
11995                 // @option imperial: Boolean = True
 
11996                 // Whether to show the imperial scale line (mi/ft).
 
11999                 // @option updateWhenIdle: Boolean = false
 
12000                 // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
 
12003         onAdd: function (map) {
 
12004                 var className = 'leaflet-control-scale',
 
12005                     container = L.DomUtil.create('div', className),
 
12006                     options = this.options;
 
12008                 this._addScales(options, className + '-line', container);
 
12010                 map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
12011                 map.whenReady(this._update, this);
 
12016         onRemove: function (map) {
 
12017                 map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
 
12020         _addScales: function (options, className, container) {
 
12021                 if (options.metric) {
 
12022                         this._mScale = L.DomUtil.create('div', className, container);
 
12024                 if (options.imperial) {
 
12025                         this._iScale = L.DomUtil.create('div', className, container);
 
12029         _update: function () {
 
12030                 var map = this._map,
 
12031                     y = map.getSize().y / 2;
 
12033                 var maxMeters = map.distance(
 
12034                                 map.containerPointToLatLng([0, y]),
 
12035                                 map.containerPointToLatLng([this.options.maxWidth, y]));
 
12037                 this._updateScales(maxMeters);
 
12040         _updateScales: function (maxMeters) {
 
12041                 if (this.options.metric && maxMeters) {
 
12042                         this._updateMetric(maxMeters);
 
12044                 if (this.options.imperial && maxMeters) {
 
12045                         this._updateImperial(maxMeters);
 
12049         _updateMetric: function (maxMeters) {
 
12050                 var meters = this._getRoundNum(maxMeters),
 
12051                     label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
 
12053                 this._updateScale(this._mScale, label, meters / maxMeters);
 
12056         _updateImperial: function (maxMeters) {
 
12057                 var maxFeet = maxMeters * 3.2808399,
 
12058                     maxMiles, miles, feet;
 
12060                 if (maxFeet > 5280) {
 
12061                         maxMiles = maxFeet / 5280;
 
12062                         miles = this._getRoundNum(maxMiles);
 
12063                         this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
 
12066                         feet = this._getRoundNum(maxFeet);
 
12067                         this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
 
12071         _updateScale: function (scale, text, ratio) {
 
12072                 scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
 
12073                 scale.innerHTML = text;
 
12076         _getRoundNum: function (num) {
 
12077                 var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
 
12090 // @factory L.control.scale(options?: Control.Scale options)
 
12091 // Creates an scale control with the given options.
 
12092 L.control.scale = function (options) {
 
12093         return new L.Control.Scale(options);
 
12099  * @class Control.Layers
 
12100  * @aka L.Control.Layers
 
12101  * @inherits Control
 
12103  * 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`.
 
12108  * var baseLayers = {
 
12109  *      "Mapbox": mapbox,
 
12110  *      "OpenStreetMap": osm
 
12114  *      "Marker": marker,
 
12115  *      "Roads": roadsLayer
 
12118  * L.control.layers(baseLayers, overlays).addTo(map);
 
12121  * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
 
12125  *     "<someName1>": layer1,
 
12126  *     "<someName2>": layer2
 
12130  * The layer names can contain HTML, which allows you to add additional styling to the items:
 
12133  * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
 
12138 L.Control.Layers = L.Control.extend({
 
12140         // @aka Control.Layers options
 
12142                 // @option collapsed: Boolean = true
 
12143                 // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
 
12145                 position: 'topright',
 
12147                 // @option autoZIndex: Boolean = true
 
12148                 // 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.
 
12151                 // @option hideSingleBase: Boolean = false
 
12152                 // If `true`, the base layers in the control will be hidden when there is only one.
 
12153                 hideSingleBase: false
 
12156         initialize: function (baseLayers, overlays, options) {
 
12157                 L.setOptions(this, options);
 
12160                 this._lastZIndex = 0;
 
12161                 this._handlingClick = false;
 
12163                 for (var i in baseLayers) {
 
12164                         this._addLayer(baseLayers[i], i);
 
12167                 for (i in overlays) {
 
12168                         this._addLayer(overlays[i], i, true);
 
12172         onAdd: function (map) {
 
12173                 this._initLayout();
 
12177                 map.on('zoomend', this._checkDisabledLayers, this);
 
12179                 return this._container;
 
12182         onRemove: function () {
 
12183                 this._map.off('zoomend', this._checkDisabledLayers, this);
 
12185                 for (var i = 0; i < this._layers.length; i++) {
 
12186                         this._layers[i].layer.off('add remove', this._onLayerChange, this);
 
12190         // @method addBaseLayer(layer: Layer, name: String): this
 
12191         // Adds a base layer (radio button entry) with the given name to the control.
 
12192         addBaseLayer: function (layer, name) {
 
12193                 this._addLayer(layer, name);
 
12194                 return (this._map) ? this._update() : this;
 
12197         // @method addOverlay(layer: Layer, name: String): this
 
12198         // Adds an overlay (checkbox entry) with the given name to the control.
 
12199         addOverlay: function (layer, name) {
 
12200                 this._addLayer(layer, name, true);
 
12201                 return (this._map) ? this._update() : this;
 
12204         // @method removeLayer(layer: Layer): this
 
12205         // Remove the given layer from the control.
 
12206         removeLayer: function (layer) {
 
12207                 layer.off('add remove', this._onLayerChange, this);
 
12209                 var obj = this._getLayer(L.stamp(layer));
 
12211                         this._layers.splice(this._layers.indexOf(obj), 1);
 
12213                 return (this._map) ? this._update() : this;
 
12216         // @method expand(): this
 
12217         // Expand the control container if collapsed.
 
12218         expand: function () {
 
12219                 L.DomUtil.addClass(this._container, 'leaflet-control-layers-expanded');
 
12220                 this._form.style.height = null;
 
12221                 var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
 
12222                 if (acceptableHeight < this._form.clientHeight) {
 
12223                         L.DomUtil.addClass(this._form, 'leaflet-control-layers-scrollbar');
 
12224                         this._form.style.height = acceptableHeight + 'px';
 
12226                         L.DomUtil.removeClass(this._form, 'leaflet-control-layers-scrollbar');
 
12228                 this._checkDisabledLayers();
 
12232         // @method collapse(): this
 
12233         // Collapse the control container if expanded.
 
12234         collapse: function () {
 
12235                 L.DomUtil.removeClass(this._container, 'leaflet-control-layers-expanded');
 
12239         _initLayout: function () {
 
12240                 var className = 'leaflet-control-layers',
 
12241                     container = this._container = L.DomUtil.create('div', className);
 
12243                 // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
 
12244                 container.setAttribute('aria-haspopup', true);
 
12246                 L.DomEvent.disableClickPropagation(container);
 
12247                 if (!L.Browser.touch) {
 
12248                         L.DomEvent.disableScrollPropagation(container);
 
12251                 var form = this._form = L.DomUtil.create('form', className + '-list');
 
12253                 if (this.options.collapsed) {
 
12254                         if (!L.Browser.android) {
 
12255                                 L.DomEvent.on(container, {
 
12256                                         mouseenter: this.expand,
 
12257                                         mouseleave: this.collapse
 
12261                         var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container);
 
12263                         link.title = 'Layers';
 
12265                         if (L.Browser.touch) {
 
12267                                     .on(link, 'click', L.DomEvent.stop)
 
12268                                     .on(link, 'click', this.expand, this);
 
12270                                 L.DomEvent.on(link, 'focus', this.expand, this);
 
12273                         // work around for Firefox Android issue https://github.com/Leaflet/Leaflet/issues/2033
 
12274                         L.DomEvent.on(form, 'click', function () {
 
12275                                 setTimeout(L.bind(this._onInputClick, this), 0);
 
12278                         this._map.on('click', this.collapse, this);
 
12279                         // TODO keyboard accessibility
 
12284                 this._baseLayersList = L.DomUtil.create('div', className + '-base', form);
 
12285                 this._separator = L.DomUtil.create('div', className + '-separator', form);
 
12286                 this._overlaysList = L.DomUtil.create('div', className + '-overlays', form);
 
12288                 container.appendChild(form);
 
12291         _getLayer: function (id) {
 
12292                 for (var i = 0; i < this._layers.length; i++) {
 
12294                         if (this._layers[i] && L.stamp(this._layers[i].layer) === id) {
 
12295                                 return this._layers[i];
 
12300         _addLayer: function (layer, name, overlay) {
 
12301                 layer.on('add remove', this._onLayerChange, this);
 
12303                 this._layers.push({
 
12309                 if (this.options.autoZIndex && layer.setZIndex) {
 
12310                         this._lastZIndex++;
 
12311                         layer.setZIndex(this._lastZIndex);
 
12315         _update: function () {
 
12316                 if (!this._container) { return this; }
 
12318                 L.DomUtil.empty(this._baseLayersList);
 
12319                 L.DomUtil.empty(this._overlaysList);
 
12321                 var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
 
12323                 for (i = 0; i < this._layers.length; i++) {
 
12324                         obj = this._layers[i];
 
12325                         this._addItem(obj);
 
12326                         overlaysPresent = overlaysPresent || obj.overlay;
 
12327                         baseLayersPresent = baseLayersPresent || !obj.overlay;
 
12328                         baseLayersCount += !obj.overlay ? 1 : 0;
 
12331                 // Hide base layers section if there's only one layer.
 
12332                 if (this.options.hideSingleBase) {
 
12333                         baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
 
12334                         this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
 
12337                 this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
 
12342         _onLayerChange: function (e) {
 
12343                 if (!this._handlingClick) {
 
12347                 var obj = this._getLayer(L.stamp(e.target));
 
12350                 // @section Layer events
 
12351                 // @event baselayerchange: LayersControlEvent
 
12352                 // Fired when the base layer is changed through the [layer control](#control-layers).
 
12353                 // @event overlayadd: LayersControlEvent
 
12354                 // Fired when an overlay is selected through the [layer control](#control-layers).
 
12355                 // @event overlayremove: LayersControlEvent
 
12356                 // Fired when an overlay is deselected through the [layer control](#control-layers).
 
12357                 // @namespace Control.Layers
 
12358                 var type = obj.overlay ?
 
12359                         (e.type === 'add' ? 'overlayadd' : 'overlayremove') :
 
12360                         (e.type === 'add' ? 'baselayerchange' : null);
 
12363                         this._map.fire(type, obj);
 
12367         // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
 
12368         _createRadioElement: function (name, checked) {
 
12370                 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
 
12371                                 name + '"' + (checked ? ' checked="checked"' : '') + '/>';
 
12373                 var radioFragment = document.createElement('div');
 
12374                 radioFragment.innerHTML = radioHtml;
 
12376                 return radioFragment.firstChild;
 
12379         _addItem: function (obj) {
 
12380                 var label = document.createElement('label'),
 
12381                     checked = this._map.hasLayer(obj.layer),
 
12385                         input = document.createElement('input');
 
12386                         input.type = 'checkbox';
 
12387                         input.className = 'leaflet-control-layers-selector';
 
12388                         input.defaultChecked = checked;
 
12390                         input = this._createRadioElement('leaflet-base-layers', checked);
 
12393                 input.layerId = L.stamp(obj.layer);
 
12395                 L.DomEvent.on(input, 'click', this._onInputClick, this);
 
12397                 var name = document.createElement('span');
 
12398                 name.innerHTML = ' ' + obj.name;
 
12400                 // Helps from preventing layer control flicker when checkboxes are disabled
 
12401                 // https://github.com/Leaflet/Leaflet/issues/2771
 
12402                 var holder = document.createElement('div');
 
12404                 label.appendChild(holder);
 
12405                 holder.appendChild(input);
 
12406                 holder.appendChild(name);
 
12408                 var container = obj.overlay ? this._overlaysList : this._baseLayersList;
 
12409                 container.appendChild(label);
 
12411                 this._checkDisabledLayers();
 
12415         _onInputClick: function () {
 
12416                 var inputs = this._form.getElementsByTagName('input'),
 
12417                     input, layer, hasLayer;
 
12418                 var addedLayers = [],
 
12419                     removedLayers = [];
 
12421                 this._handlingClick = true;
 
12423                 for (var i = inputs.length - 1; i >= 0; i--) {
 
12425                         layer = this._getLayer(input.layerId).layer;
 
12426                         hasLayer = this._map.hasLayer(layer);
 
12428                         if (input.checked && !hasLayer) {
 
12429                                 addedLayers.push(layer);
 
12431                         } else if (!input.checked && hasLayer) {
 
12432                                 removedLayers.push(layer);
 
12436                 // Bugfix issue 2318: Should remove all old layers before readding new ones
 
12437                 for (i = 0; i < removedLayers.length; i++) {
 
12438                         this._map.removeLayer(removedLayers[i]);
 
12440                 for (i = 0; i < addedLayers.length; i++) {
 
12441                         this._map.addLayer(addedLayers[i]);
 
12444                 this._handlingClick = false;
 
12446                 this._refocusOnMap();
 
12449         _checkDisabledLayers: function () {
 
12450                 var inputs = this._form.getElementsByTagName('input'),
 
12453                     zoom = this._map.getZoom();
 
12455                 for (var i = inputs.length - 1; i >= 0; i--) {
 
12457                         layer = this._getLayer(input.layerId).layer;
 
12458                         input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
 
12459                                          (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
 
12464         _expand: function () {
 
12465                 // Backward compatibility, remove me in 1.1.
 
12466                 return this.expand();
 
12469         _collapse: function () {
 
12470                 // Backward compatibility, remove me in 1.1.
 
12471                 return this.collapse();
 
12477 // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
 
12478 // 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.
 
12479 L.control.layers = function (baseLayers, overlays, options) {
 
12480         return new L.Control.Layers(baseLayers, overlays, options);
 
12486  * @class PosAnimation
 
12487  * @aka L.PosAnimation
 
12488  * @inherits Evented
 
12489  * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
 
12493  * var fx = new L.PosAnimation();
 
12494  * fx.run(el, [300, 500], 0.5);
 
12497  * @constructor L.PosAnimation()
 
12498  * Creates a `PosAnimation` object.
 
12502 L.PosAnimation = L.Evented.extend({
 
12504         // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
 
12505         // Run an animation of a given element to a new position, optionally setting
 
12506         // duration in seconds (`0.25` by default) and easing linearity factor (3rd
 
12507         // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
 
12508         // `0.5` by default).
 
12509         run: function (el, newPos, duration, easeLinearity) {
 
12513                 this._inProgress = true;
 
12514                 this._duration = duration || 0.25;
 
12515                 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
 
12517                 this._startPos = L.DomUtil.getPosition(el);
 
12518                 this._offset = newPos.subtract(this._startPos);
 
12519                 this._startTime = +new Date();
 
12521                 // @event start: Event
 
12522                 // Fired when the animation starts
 
12523                 this.fire('start');
 
12529         // Stops the animation (if currently running).
 
12530         stop: function () {
 
12531                 if (!this._inProgress) { return; }
 
12537         _animate: function () {
 
12539                 this._animId = L.Util.requestAnimFrame(this._animate, this);
 
12543         _step: function (round) {
 
12544                 var elapsed = (+new Date()) - this._startTime,
 
12545                     duration = this._duration * 1000;
 
12547                 if (elapsed < duration) {
 
12548                         this._runFrame(this._easeOut(elapsed / duration), round);
 
12555         _runFrame: function (progress, round) {
 
12556                 var pos = this._startPos.add(this._offset.multiplyBy(progress));
 
12560                 L.DomUtil.setPosition(this._el, pos);
 
12562                 // @event step: Event
 
12563                 // Fired continuously during the animation.
 
12567         _complete: function () {
 
12568                 L.Util.cancelAnimFrame(this._animId);
 
12570                 this._inProgress = false;
 
12571                 // @event end: Event
 
12572                 // Fired when the animation ends.
 
12576         _easeOut: function (t) {
 
12577                 return 1 - Math.pow(1 - t, this._easeOutPower);
 
12584  * Extends L.Map to handle panning animations.
 
12589         setView: function (center, zoom, options) {
 
12591                 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
 
12592                 center = this._limitCenter(L.latLng(center), zoom, this.options.maxBounds);
 
12593                 options = options || {};
 
12597                 if (this._loaded && !options.reset && options !== true) {
 
12599                         if (options.animate !== undefined) {
 
12600                                 options.zoom = L.extend({animate: options.animate}, options.zoom);
 
12601                                 options.pan = L.extend({animate: options.animate, duration: options.duration}, options.pan);
 
12604                         // try animating pan or zoom
 
12605                         var moved = (this._zoom !== zoom) ?
 
12606                                 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
 
12607                                 this._tryAnimatedPan(center, options.pan);
 
12610                                 // prevent resize handler call, the view will refresh after animation anyway
 
12611                                 clearTimeout(this._sizeTimer);
 
12616                 // animation didn't start, just reset the map view
 
12617                 this._resetView(center, zoom);
 
12622         panBy: function (offset, options) {
 
12623                 offset = L.point(offset).round();
 
12624                 options = options || {};
 
12626                 if (!offset.x && !offset.y) {
 
12627                         return this.fire('moveend');
 
12629                 // If we pan too far, Chrome gets issues with tiles
 
12630                 // and makes them disappear or appear in the wrong place (slightly offset) #2602
 
12631                 if (options.animate !== true && !this.getSize().contains(offset)) {
 
12632                         this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
 
12636                 if (!this._panAnim) {
 
12637                         this._panAnim = new L.PosAnimation();
 
12640                                 'step': this._onPanTransitionStep,
 
12641                                 'end': this._onPanTransitionEnd
 
12645                 // don't fire movestart if animating inertia
 
12646                 if (!options.noMoveStart) {
 
12647                         this.fire('movestart');
 
12650                 // animate pan unless animate: false specified
 
12651                 if (options.animate !== false) {
 
12652                         L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim');
 
12654                         var newPos = this._getMapPanePos().subtract(offset).round();
 
12655                         this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
 
12657                         this._rawPanBy(offset);
 
12658                         this.fire('move').fire('moveend');
 
12664         _onPanTransitionStep: function () {
 
12668         _onPanTransitionEnd: function () {
 
12669                 L.DomUtil.removeClass(this._mapPane, 'leaflet-pan-anim');
 
12670                 this.fire('moveend');
 
12673         _tryAnimatedPan: function (center, options) {
 
12674                 // difference between the new and current centers in pixels
 
12675                 var offset = this._getCenterOffset(center)._floor();
 
12677                 // don't animate too far unless animate: true specified in options
 
12678                 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
 
12680                 this.panBy(offset, options);
 
12689  * Extends L.Map to handle zoom animations.
 
12693 // @section Animation Options
 
12694 L.Map.mergeOptions({
 
12695         // @option zoomAnimation: Boolean = true
 
12696         // Whether the map zoom animation is enabled. By default it's enabled
 
12697         // in all browsers that support CSS3 Transitions except Android.
 
12698         zoomAnimation: true,
 
12700         // @option zoomAnimationThreshold: Number = 4
 
12701         // Won't animate zoom if the zoom difference exceeds this value.
 
12702         zoomAnimationThreshold: 4
 
12705 var zoomAnimated = L.DomUtil.TRANSITION && L.Browser.any3d && !L.Browser.mobileOpera;
 
12707 if (zoomAnimated) {
 
12709         L.Map.addInitHook(function () {
 
12710                 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
 
12711                 this._zoomAnimated = this.options.zoomAnimation;
 
12713                 // zoom transitions run with the same duration for all layers, so if one of transitionend events
 
12714                 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
 
12715                 if (this._zoomAnimated) {
 
12717                         this._createAnimProxy();
 
12719                         L.DomEvent.on(this._proxy, L.DomUtil.TRANSITION_END, this._catchTransitionEnd, this);
 
12724 L.Map.include(!zoomAnimated ? {} : {
 
12726         _createAnimProxy: function () {
 
12728                 var proxy = this._proxy = L.DomUtil.create('div', 'leaflet-proxy leaflet-zoom-animated');
 
12729                 this._panes.mapPane.appendChild(proxy);
 
12731                 this.on('zoomanim', function (e) {
 
12732                         var prop = L.DomUtil.TRANSFORM,
 
12733                             transform = proxy.style[prop];
 
12735                         L.DomUtil.setTransform(proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
 
12737                         // workaround for case when transform is the same and so transitionend event is not fired
 
12738                         if (transform === proxy.style[prop] && this._animatingZoom) {
 
12739                                 this._onZoomTransitionEnd();
 
12743                 this.on('load moveend', function () {
 
12744                         var c = this.getCenter(),
 
12745                             z = this.getZoom();
 
12746                         L.DomUtil.setTransform(proxy, this.project(c, z), this.getZoomScale(z, 1));
 
12750         _catchTransitionEnd: function (e) {
 
12751                 if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
 
12752                         this._onZoomTransitionEnd();
 
12756         _nothingToAnimate: function () {
 
12757                 return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
 
12760         _tryAnimatedZoom: function (center, zoom, options) {
 
12762                 if (this._animatingZoom) { return true; }
 
12764                 options = options || {};
 
12766                 // don't animate if disabled, not supported or zoom difference is too large
 
12767                 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
 
12768                         Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
 
12770                 // offset is the pixel coords of the zoom origin relative to the current center
 
12771                 var scale = this.getZoomScale(zoom),
 
12772                     offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
 
12774                 // don't animate if the zoom origin isn't within one screen from the current center, unless forced
 
12775                 if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
 
12777                 L.Util.requestAnimFrame(function () {
 
12780                             ._animateZoom(center, zoom, true);
 
12786         _animateZoom: function (center, zoom, startAnim, noUpdate) {
 
12788                         this._animatingZoom = true;
 
12790                         // remember what center/zoom to set after animation
 
12791                         this._animateToCenter = center;
 
12792                         this._animateToZoom = zoom;
 
12794                         L.DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim');
 
12797                 // @event zoomanim: ZoomAnimEvent
 
12798                 // Fired on every frame of a zoom animation
 
12799                 this.fire('zoomanim', {
 
12805                 // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
 
12806                 setTimeout(L.bind(this._onZoomTransitionEnd, this), 250);
 
12809         _onZoomTransitionEnd: function () {
 
12810                 if (!this._animatingZoom) { return; }
 
12812                 L.DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim');
 
12814                 this._animatingZoom = false;
 
12816                 this._move(this._animateToCenter, this._animateToZoom);
 
12818                 // This anim frame should prevent an obscure iOS webkit tile loading race condition.
 
12819                 L.Util.requestAnimFrame(function () {
 
12820                         this._moveEnd(true);
 
12828 // @section Methods for modifying map state
 
12831         // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
 
12832         // Sets the view of the map (geographical center and zoom) performing a smooth
 
12833         // pan-zoom animation.
 
12834         flyTo: function (targetCenter, targetZoom, options) {
 
12836                 options = options || {};
 
12837                 if (options.animate === false || !L.Browser.any3d) {
 
12838                         return this.setView(targetCenter, targetZoom, options);
 
12843                 var from = this.project(this.getCenter()),
 
12844                     to = this.project(targetCenter),
 
12845                     size = this.getSize(),
 
12846                     startZoom = this._zoom;
 
12848                 targetCenter = L.latLng(targetCenter);
 
12849                 targetZoom = targetZoom === undefined ? startZoom : targetZoom;
 
12851                 var w0 = Math.max(size.x, size.y),
 
12852                     w1 = w0 * this.getZoomScale(startZoom, targetZoom),
 
12853                     u1 = (to.distanceTo(from)) || 1,
 
12858                         var s1 = i ? -1 : 1,
 
12860                             t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
 
12861                             b1 = 2 * s2 * rho2 * u1,
 
12863                             sq = Math.sqrt(b * b + 1) - b;
 
12865                             // workaround for floating point precision bug when sq = 0, log = -Infinite,
 
12866                             // thus triggering an infinite loop in flyTo
 
12867                             var log = sq < 0.000000001 ? -18 : Math.log(sq);
 
12872                 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
 
12873                 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
 
12874                 function tanh(n) { return sinh(n) / cosh(n); }
 
12878                 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
 
12879                 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
 
12881                 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
 
12883                 var start = Date.now(),
 
12884                     S = (r(1) - r0) / rho,
 
12885                     duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
 
12888                         var t = (Date.now() - start) / duration,
 
12889                             s = easeOut(t) * S;
 
12892                                 this._flyToFrame = L.Util.requestAnimFrame(frame, this);
 
12895                                         this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
 
12896                                         this.getScaleZoom(w0 / w(s), startZoom),
 
12901                                         ._move(targetCenter, targetZoom)
 
12906                 this._moveStart(true);
 
12912         // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
 
12913         // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
 
12914         // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
 
12915         flyToBounds: function (bounds, options) {
 
12916                 var target = this._getBoundsCenterZoom(bounds, options);
 
12917                 return this.flyTo(target.center, target.zoom, options);
 
12924  * Provides L.Map with convenient shortcuts for using browser geolocation features.
 
12930         // @section Geolocation methods
 
12931         _defaultLocateOptions: {
 
12935                 // maxZoom: <Number>
 
12937                 // enableHighAccuracy: false
 
12940         // @method locate(options?: Locate options): this
 
12941         // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
 
12942         // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
 
12943         // and optionally sets the map view to the user's location with respect to
 
12944         // detection accuracy (or to the world view if geolocation failed).
 
12945         // Note that, if your page doesn't use HTTPS, this method will fail in
 
12946         // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
 
12947         // See `Locate options` for more details.
 
12948         locate: function (options) {
 
12950                 options = this._locateOptions = L.extend({}, this._defaultLocateOptions, options);
 
12952                 if (!('geolocation' in navigator)) {
 
12953                         this._handleGeolocationError({
 
12955                                 message: 'Geolocation not supported.'
 
12960                 var onResponse = L.bind(this._handleGeolocationResponse, this),
 
12961                     onError = L.bind(this._handleGeolocationError, this);
 
12963                 if (options.watch) {
 
12964                         this._locationWatchId =
 
12965                                 navigator.geolocation.watchPosition(onResponse, onError, options);
 
12967                         navigator.geolocation.getCurrentPosition(onResponse, onError, options);
 
12972         // @method stopLocate(): this
 
12973         // Stops watching location previously initiated by `map.locate({watch: true})`
 
12974         // and aborts resetting the map view if map.locate was called with
 
12975         // `{setView: true}`.
 
12976         stopLocate: function () {
 
12977                 if (navigator.geolocation && navigator.geolocation.clearWatch) {
 
12978                         navigator.geolocation.clearWatch(this._locationWatchId);
 
12980                 if (this._locateOptions) {
 
12981                         this._locateOptions.setView = false;
 
12986         _handleGeolocationError: function (error) {
 
12987                 var c = error.code,
 
12988                     message = error.message ||
 
12989                             (c === 1 ? 'permission denied' :
 
12990                             (c === 2 ? 'position unavailable' : 'timeout'));
 
12992                 if (this._locateOptions.setView && !this._loaded) {
 
12996                 // @section Location events
 
12997                 // @event locationerror: ErrorEvent
 
12998                 // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
 
12999                 this.fire('locationerror', {
 
13001                         message: 'Geolocation error: ' + message + '.'
 
13005         _handleGeolocationResponse: function (pos) {
 
13006                 var lat = pos.coords.latitude,
 
13007                     lng = pos.coords.longitude,
 
13008                     latlng = new L.LatLng(lat, lng),
 
13009                     bounds = latlng.toBounds(pos.coords.accuracy),
 
13010                     options = this._locateOptions;
 
13012                 if (options.setView) {
 
13013                         var zoom = this.getBoundsZoom(bounds);
 
13014                         this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
 
13020                         timestamp: pos.timestamp
 
13023                 for (var i in pos.coords) {
 
13024                         if (typeof pos.coords[i] === 'number') {
 
13025                                 data[i] = pos.coords[i];
 
13029                 // @event locationfound: LocationEvent
 
13030                 // Fired when geolocation (using the [`locate`](#map-locate) method)
 
13031                 // went successfully.
 
13032                 this.fire('locationfound', data);
 
13038 }(window, document));
 
13039 //# sourceMappingURL=leaflet-src.map