e366062ab3c05279ef7f071c8282357dae146da0
[rails.git] / vendor / assets / leaflet / leaflet.js
1 /*
2  Leaflet 1.0.3, a JS library for interactive maps. http://leafletjs.com
3  (c) 2010-2016 Vladimir Agafonkin, (c) 2010-2011 CloudMade
4 */
5 (function (window, document, undefined) {
6 var L = {
7         version: "1.0.3"
8 };
9
10 function expose() {
11         var oldL = window.L;
12
13         L.noConflict = function () {
14                 window.L = oldL;
15                 return this;
16         };
17
18         window.L = L;
19 }
20
21 // define Leaflet for Node module pattern loaders, including Browserify
22 if (typeof module === 'object' && typeof module.exports === 'object') {
23         module.exports = L;
24
25 // define Leaflet as an AMD module
26 } else if (typeof define === 'function' && define.amd) {
27         define(L);
28 }
29
30 // define Leaflet as a global L variable, saving the original L to restore later if needed
31 if (typeof window !== 'undefined') {
32         expose();
33 }
34
35
36
37 /*
38  * @namespace Util
39  *
40  * Various utility functions, used by Leaflet internally.
41  */
42
43 L.Util = {
44
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) {
48                 var i, j, len, src;
49
50                 for (j = 1, len = arguments.length; j < len; j++) {
51                         src = arguments[j];
52                         for (i in src) {
53                                 dest[i] = src[i];
54                         }
55                 }
56                 return dest;
57         },
58
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 () {
62                 function F() {}
63                 return function (proto) {
64                         F.prototype = proto;
65                         return new F();
66                 };
67         })(),
68
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;
74
75                 if (fn.bind) {
76                         return fn.bind.apply(fn, slice.call(arguments, 1));
77                 }
78
79                 var args = slice.call(arguments, 2);
80
81                 return function () {
82                         return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
83                 };
84         },
85
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) {
89                 /*eslint-disable */
90                 obj._leaflet_id = obj._leaflet_id || ++L.Util.lastId;
91                 return obj._leaflet_id;
92                 /*eslint-enable */
93         },
94
95         // @property lastId: Number
96         // Last unique ID used by [`stamp()`](#util-stamp)
97         lastId: 0,
98
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;
108
109                 later = function () {
110                         // reset lock and call if queued
111                         lock = false;
112                         if (args) {
113                                 wrapperFn.apply(context, args);
114                                 args = false;
115                         }
116                 };
117
118                 wrapperFn = function () {
119                         if (lock) {
120                                 // called too soon, queue to call later
121                                 args = arguments;
122
123                         } else {
124                                 // call and lock until later
125                                 fn.apply(context, arguments);
126                                 setTimeout(later, time);
127                                 lock = true;
128                         }
129                 };
130
131                 return wrapperFn;
132         },
133
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) {
139                 var max = range[1],
140                     min = range[0],
141                     d = max - min;
142                 return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
143         },
144
145         // @function falseFn(): Function
146         // Returns a function which always returns `false`.
147         falseFn: function () { return false; },
148
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;
154         },
155
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, '');
160         },
161
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+/);
166         },
167
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) : {};
173                 }
174                 for (var i in options) {
175                         obj.options[i] = options[i];
176                 }
177                 return obj.options;
178         },
179
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) {
186                 var params = [];
187                 for (var i in obj) {
188                         params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
189                 }
190                 return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
191         },
192
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];
201
202                         if (value === undefined) {
203                                 throw new Error('No value provided for variable ' + str);
204
205                         } else if (typeof value === 'function') {
206                                 value = value(data);
207                         }
208                         return value;
209                 });
210         },
211
212         templateRe: /\{ *([\w_\-]+) *\}/g,
213
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]');
218         },
219
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; }
225                 }
226                 return -1;
227         },
228
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='
234 };
235
236 (function () {
237         // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
238
239         function getPrefixed(name) {
240                 return window['webkit' + name] || window['moz' + name] || window['ms' + name];
241         }
242
243         var lastTime = 0;
244
245         // fallback for IE 7-8
246         function timeoutDefer(fn) {
247                 var time = +new Date(),
248                     timeToCall = Math.max(0, 16 - (time - lastTime));
249
250                 lastTime = time + timeToCall;
251                 return window.setTimeout(fn, timeToCall);
252         }
253
254         var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer,
255             cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
256                        getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
257
258
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) {
267                         fn.call(context);
268                 } else {
269                         return requestFn.call(window, L.bind(fn, context));
270                 }
271         };
272
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) {
276                 if (id) {
277                         cancelFn.call(window, id);
278                 }
279         };
280 })();
281
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;
287
288
289
290
291 // @class Class
292 // @aka L.Class
293
294 // @section
295 // @uninheritable
296
297 // Thanks to John Resig and Dean Edwards for inspiration!
298
299 L.Class = function () {};
300
301 L.Class.extend = function (props) {
302
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 () {
307
308                 // call the constructor
309                 if (this.initialize) {
310                         this.initialize.apply(this, arguments);
311                 }
312
313                 // call all constructor hooks
314                 this.callInitHooks();
315         };
316
317         var parentProto = NewClass.__super__ = this.prototype;
318
319         var proto = L.Util.create(parentProto);
320         proto.constructor = NewClass;
321
322         NewClass.prototype = proto;
323
324         // inherit parent's statics
325         for (var i in this) {
326                 if (this.hasOwnProperty(i) && i !== 'prototype') {
327                         NewClass[i] = this[i];
328                 }
329         }
330
331         // mix static properties into the class
332         if (props.statics) {
333                 L.extend(NewClass, props.statics);
334                 delete props.statics;
335         }
336
337         // mix includes into the prototype
338         if (props.includes) {
339                 L.Util.extend.apply(null, [proto].concat(props.includes));
340                 delete props.includes;
341         }
342
343         // merge options
344         if (proto.options) {
345                 props.options = L.Util.extend(L.Util.create(proto.options), props.options);
346         }
347
348         // mix given properties into the prototype
349         L.extend(proto, props);
350
351         proto._initHooks = [];
352
353         // add method for calling all hooks
354         proto.callInitHooks = function () {
355
356                 if (this._initHooksCalled) { return; }
357
358                 if (parentProto.callInitHooks) {
359                         parentProto.callInitHooks.call(this);
360                 }
361
362                 this._initHooksCalled = true;
363
364                 for (var i = 0, len = proto._initHooks.length; i < len; i++) {
365                         proto._initHooks[i].call(this);
366                 }
367         };
368
369         return NewClass;
370 };
371
372
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);
377         return this;
378 };
379
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);
384         return this;
385 };
386
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);
391
392         var init = typeof fn === 'function' ? fn : function () {
393                 this[fn].apply(this, args);
394         };
395
396         this.prototype._initHooks = this.prototype._initHooks || [];
397         this.prototype._initHooks.push(init);
398         return this;
399 };
400
401
402
403 /*
404  * @class Evented
405  * @aka L.Evented
406  * @inherits Class
407  *
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).
409  *
410  * @example
411  *
412  * ```js
413  * map.on('click', function(e) {
414  *      alert(e.latlng);
415  * } );
416  * ```
417  *
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:
419  *
420  * ```js
421  * function onClick(e) { ... }
422  *
423  * map.on('click', onClick);
424  * map.off('click', onClick);
425  * ```
426  */
427
428
429 L.Evented = L.Class.extend({
430
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'`).
433          *
434          * @alternative
435          * @method on(eventMap: Object): this
436          * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
437          */
438         on: function (types, fn, context) {
439
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);
446                         }
447
448                 } else {
449                         // types can be a string of space-separated words
450                         types = L.Util.splitWords(types);
451
452                         for (var i = 0, len = types.length; i < len; i++) {
453                                 this._on(types[i], fn, context);
454                         }
455                 }
456
457                 return this;
458         },
459
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.
462          *
463          * @alternative
464          * @method off(eventMap: Object): this
465          * Removes a set of type/listener pairs.
466          *
467          * @alternative
468          * @method off: this
469          * Removes all listeners to all events on the object.
470          */
471         off: function (types, fn, context) {
472
473                 if (!types) {
474                         // clear all listeners if called without arguments
475                         delete this._events;
476
477                 } else if (typeof types === 'object') {
478                         for (var type in types) {
479                                 this._off(type, types[type], fn);
480                         }
481
482                 } else {
483                         types = L.Util.splitWords(types);
484
485                         for (var i = 0, len = types.length; i < len; i++) {
486                                 this._off(types[i], fn, context);
487                         }
488                 }
489
490                 return this;
491         },
492
493         // attach listener (without syntactic sugar now)
494         _on: function (type, fn, context) {
495                 this._events = this._events || {};
496
497                 /* get/init listeners for type */
498                 var typeListeners = this._events[type];
499                 if (!typeListeners) {
500                         typeListeners = [];
501                         this._events[type] = typeListeners;
502                 }
503
504                 if (context === this) {
505                         // Less memory footprint.
506                         context = undefined;
507                 }
508                 var newListener = {fn: fn, ctx: context},
509                     listeners = typeListeners;
510
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) {
514                                 return;
515                         }
516                 }
517
518                 listeners.push(newListener);
519         },
520
521         _off: function (type, fn, context) {
522                 var listeners,
523                     i,
524                     len;
525
526                 if (!this._events) { return; }
527
528                 listeners = this._events[type];
529
530                 if (!listeners) {
531                         return;
532                 }
533
534                 if (!fn) {
535                         // Set all removed listeners to noop so they are not called if remove happens in fire
536                         for (i = 0, len = listeners.length; i < len; i++) {
537                                 listeners[i].fn = L.Util.falseFn;
538                         }
539                         // clear all listeners for a type if function isn't specified
540                         delete this._events[type];
541                         return;
542                 }
543
544                 if (context === this) {
545                         context = undefined;
546                 }
547
548                 if (listeners) {
549
550                         // find fn and remove it
551                         for (i = 0, len = listeners.length; i < len; i++) {
552                                 var l = listeners[i];
553                                 if (l.ctx !== context) { continue; }
554                                 if (l.fn === fn) {
555
556                                         // set the removed listener to noop so that's not called if remove happens in fire
557                                         l.fn = L.Util.falseFn;
558
559                                         if (this._firingCount) {
560                                                 /* copy array in case events are being fired */
561                                                 this._events[type] = listeners = listeners.slice();
562                                         }
563                                         listeners.splice(i, 1);
564
565                                         return;
566                                 }
567                         }
568                 }
569         },
570
571         // @method fire(type: String, data?: Object, propagate?: Boolean): this
572         // Fires an event of the specified type. You can optionally provide an data
573         // object — the first argument of the listener function will contain its
574         // properties. The event can optionally be propagated to event parents.
575         fire: function (type, data, propagate) {
576                 if (!this.listens(type, propagate)) { return this; }
577
578                 var event = L.Util.extend({}, data, {type: type, target: this});
579
580                 if (this._events) {
581                         var listeners = this._events[type];
582
583                         if (listeners) {
584                                 this._firingCount = (this._firingCount + 1) || 1;
585                                 for (var i = 0, len = listeners.length; i < len; i++) {
586                                         var l = listeners[i];
587                                         l.fn.call(l.ctx || this, event);
588                                 }
589
590                                 this._firingCount--;
591                         }
592                 }
593
594                 if (propagate) {
595                         // propagate the event to parents (set with addEventParent)
596                         this._propagateEvent(event);
597                 }
598
599                 return this;
600         },
601
602         // @method listens(type: String): Boolean
603         // Returns `true` if a particular event type has any listeners attached to it.
604         listens: function (type, propagate) {
605                 var listeners = this._events && this._events[type];
606                 if (listeners && listeners.length) { return true; }
607
608                 if (propagate) {
609                         // also check parents for listeners if event propagates
610                         for (var id in this._eventParents) {
611                                 if (this._eventParents[id].listens(type, propagate)) { return true; }
612                         }
613                 }
614                 return false;
615         },
616
617         // @method once(…): this
618         // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
619         once: function (types, fn, context) {
620
621                 if (typeof types === 'object') {
622                         for (var type in types) {
623                                 this.once(type, types[type], fn);
624                         }
625                         return this;
626                 }
627
628                 var handler = L.bind(function () {
629                         this
630                             .off(types, fn, context)
631                             .off(types, handler, context);
632                 }, this);
633
634                 // add a listener that's executed once and removed after that
635                 return this
636                     .on(types, fn, context)
637                     .on(types, handler, context);
638         },
639
640         // @method addEventParent(obj: Evented): this
641         // Adds an event parent - an `Evented` that will receive propagated events
642         addEventParent: function (obj) {
643                 this._eventParents = this._eventParents || {};
644                 this._eventParents[L.stamp(obj)] = obj;
645                 return this;
646         },
647
648         // @method removeEventParent(obj: Evented): this
649         // Removes an event parent, so it will stop receiving propagated events
650         removeEventParent: function (obj) {
651                 if (this._eventParents) {
652                         delete this._eventParents[L.stamp(obj)];
653                 }
654                 return this;
655         },
656
657         _propagateEvent: function (e) {
658                 for (var id in this._eventParents) {
659                         this._eventParents[id].fire(e.type, L.extend({layer: e.target}, e), true);
660                 }
661         }
662 });
663
664 var proto = L.Evented.prototype;
665
666 // aliases; we should ditch those eventually
667
668 // @method addEventListener(…): this
669 // Alias to [`on(…)`](#evented-on)
670 proto.addEventListener = proto.on;
671
672 // @method removeEventListener(…): this
673 // Alias to [`off(…)`](#evented-off)
674
675 // @method clearAllEventListeners(…): this
676 // Alias to [`off()`](#evented-off)
677 proto.removeEventListener = proto.clearAllEventListeners = proto.off;
678
679 // @method addOneTimeEventListener(…): this
680 // Alias to [`once(…)`](#evented-once)
681 proto.addOneTimeEventListener = proto.once;
682
683 // @method fireEvent(…): this
684 // Alias to [`fire(…)`](#evented-fire)
685 proto.fireEvent = proto.fire;
686
687 // @method hasEventListeners(…): Boolean
688 // Alias to [`listens(…)`](#evented-listens)
689 proto.hasEventListeners = proto.listens;
690
691 L.Mixin = {Events: proto};
692
693
694
695 /*
696  * @namespace Browser
697  * @aka L.Browser
698  *
699  * A namespace with static properties for browser/feature detection used by Leaflet internally.
700  *
701  * @example
702  *
703  * ```js
704  * if (L.Browser.ielt9) {
705  *   alert('Upgrade your browser, dude!');
706  * }
707  * ```
708  */
709
710 (function () {
711
712         var ua = navigator.userAgent.toLowerCase(),
713             doc = document.documentElement,
714
715             ie = 'ActiveXObject' in window,
716
717             webkit    = ua.indexOf('webkit') !== -1,
718             phantomjs = ua.indexOf('phantom') !== -1,
719             android23 = ua.search('android [23]') !== -1,
720             chrome    = ua.indexOf('chrome') !== -1,
721             gecko     = ua.indexOf('gecko') !== -1  && !webkit && !window.opera && !ie,
722
723             win = navigator.platform.indexOf('Win') === 0,
724
725             mobile = typeof orientation !== 'undefined' || ua.indexOf('mobile') !== -1,
726             msPointer = !window.PointerEvent && window.MSPointerEvent,
727             pointer = window.PointerEvent || msPointer,
728
729             ie3d = ie && ('transition' in doc.style),
730             webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23,
731             gecko3d = 'MozPerspective' in doc.style,
732             opera12 = 'OTransition' in doc.style;
733
734
735         var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
736                         (window.DocumentTouch && document instanceof window.DocumentTouch));
737
738         L.Browser = {
739
740                 // @property ie: Boolean
741                 // `true` for all Internet Explorer versions (not Edge).
742                 ie: ie,
743
744                 // @property ielt9: Boolean
745                 // `true` for Internet Explorer versions less than 9.
746                 ielt9: ie && !document.addEventListener,
747
748                 // @property edge: Boolean
749                 // `true` for the Edge web browser.
750                 edge: 'msLaunchUri' in navigator && !('documentMode' in document),
751
752                 // @property webkit: Boolean
753                 // `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
754                 webkit: webkit,
755
756                 // @property gecko: Boolean
757                 // `true` for gecko-based browsers like Firefox.
758                 gecko: gecko,
759
760                 // @property android: Boolean
761                 // `true` for any browser running on an Android platform.
762                 android: ua.indexOf('android') !== -1,
763
764                 // @property android23: Boolean
765                 // `true` for browsers running on Android 2 or Android 3.
766                 android23: android23,
767
768                 // @property chrome: Boolean
769                 // `true` for the Chrome browser.
770                 chrome: chrome,
771
772                 // @property safari: Boolean
773                 // `true` for the Safari browser.
774                 safari: !chrome && ua.indexOf('safari') !== -1,
775
776
777                 // @property win: Boolean
778                 // `true` when the browser is running in a Windows platform
779                 win: win,
780
781
782                 // @property ie3d: Boolean
783                 // `true` for all Internet Explorer versions supporting CSS transforms.
784                 ie3d: ie3d,
785
786                 // @property webkit3d: Boolean
787                 // `true` for webkit-based browsers supporting CSS transforms.
788                 webkit3d: webkit3d,
789
790                 // @property gecko3d: Boolean
791                 // `true` for gecko-based browsers supporting CSS transforms.
792                 gecko3d: gecko3d,
793
794                 // @property opera12: Boolean
795                 // `true` for the Opera browser supporting CSS transforms (version 12 or later).
796                 opera12: opera12,
797
798                 // @property any3d: Boolean
799                 // `true` for all browsers supporting CSS transforms.
800                 any3d: !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantomjs,
801
802
803                 // @property mobile: Boolean
804                 // `true` for all browsers running in a mobile device.
805                 mobile: mobile,
806
807                 // @property mobileWebkit: Boolean
808                 // `true` for all webkit-based browsers in a mobile device.
809                 mobileWebkit: mobile && webkit,
810
811                 // @property mobileWebkit3d: Boolean
812                 // `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
813                 mobileWebkit3d: mobile && webkit3d,
814
815                 // @property mobileOpera: Boolean
816                 // `true` for the Opera browser in a mobile device.
817                 mobileOpera: mobile && window.opera,
818
819                 // @property mobileGecko: Boolean
820                 // `true` for gecko-based browsers running in a mobile device.
821                 mobileGecko: mobile && gecko,
822
823
824                 // @property touch: Boolean
825                 // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
826                 // This does not necessarily mean that the browser is running in a computer with
827                 // a touchscreen, it only means that the browser is capable of understanding
828                 // touch events.
829                 touch: !!touch,
830
831                 // @property msPointer: Boolean
832                 // `true` for browsers implementing the Microsoft touch events model (notably IE10).
833                 msPointer: !!msPointer,
834
835                 // @property pointer: Boolean
836                 // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
837                 pointer: !!pointer,
838
839
840                 // @property retina: Boolean
841                 // `true` for browsers on a high-resolution "retina" screen.
842                 retina: (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1
843         };
844
845 }());
846
847
848
849 /*
850  * @class Point
851  * @aka L.Point
852  *
853  * Represents a point with `x` and `y` coordinates in pixels.
854  *
855  * @example
856  *
857  * ```js
858  * var point = L.point(200, 300);
859  * ```
860  *
861  * All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent:
862  *
863  * ```js
864  * map.panBy([200, 300]);
865  * map.panBy(L.point(200, 300));
866  * ```
867  */
868
869 L.Point = function (x, y, round) {
870         // @property x: Number; The `x` coordinate of the point
871         this.x = (round ? Math.round(x) : x);
872         // @property y: Number; The `y` coordinate of the point
873         this.y = (round ? Math.round(y) : y);
874 };
875
876 L.Point.prototype = {
877
878         // @method clone(): Point
879         // Returns a copy of the current point.
880         clone: function () {
881                 return new L.Point(this.x, this.y);
882         },
883
884         // @method add(otherPoint: Point): Point
885         // Returns the result of addition of the current and the given points.
886         add: function (point) {
887                 // non-destructive, returns a new point
888                 return this.clone()._add(L.point(point));
889         },
890
891         _add: function (point) {
892                 // destructive, used directly for performance in situations where it's safe to modify existing point
893                 this.x += point.x;
894                 this.y += point.y;
895                 return this;
896         },
897
898         // @method subtract(otherPoint: Point): Point
899         // Returns the result of subtraction of the given point from the current.
900         subtract: function (point) {
901                 return this.clone()._subtract(L.point(point));
902         },
903
904         _subtract: function (point) {
905                 this.x -= point.x;
906                 this.y -= point.y;
907                 return this;
908         },
909
910         // @method divideBy(num: Number): Point
911         // Returns the result of division of the current point by the given number.
912         divideBy: function (num) {
913                 return this.clone()._divideBy(num);
914         },
915
916         _divideBy: function (num) {
917                 this.x /= num;
918                 this.y /= num;
919                 return this;
920         },
921
922         // @method multiplyBy(num: Number): Point
923         // Returns the result of multiplication of the current point by the given number.
924         multiplyBy: function (num) {
925                 return this.clone()._multiplyBy(num);
926         },
927
928         _multiplyBy: function (num) {
929                 this.x *= num;
930                 this.y *= num;
931                 return this;
932         },
933
934         // @method scaleBy(scale: Point): Point
935         // Multiply each coordinate of the current point by each coordinate of
936         // `scale`. In linear algebra terms, multiply the point by the
937         // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
938         // defined by `scale`.
939         scaleBy: function (point) {
940                 return new L.Point(this.x * point.x, this.y * point.y);
941         },
942
943         // @method unscaleBy(scale: Point): Point
944         // Inverse of `scaleBy`. Divide each coordinate of the current point by
945         // each coordinate of `scale`.
946         unscaleBy: function (point) {
947                 return new L.Point(this.x / point.x, this.y / point.y);
948         },
949
950         // @method round(): Point
951         // Returns a copy of the current point with rounded coordinates.
952         round: function () {
953                 return this.clone()._round();
954         },
955
956         _round: function () {
957                 this.x = Math.round(this.x);
958                 this.y = Math.round(this.y);
959                 return this;
960         },
961
962         // @method floor(): Point
963         // Returns a copy of the current point with floored coordinates (rounded down).
964         floor: function () {
965                 return this.clone()._floor();
966         },
967
968         _floor: function () {
969                 this.x = Math.floor(this.x);
970                 this.y = Math.floor(this.y);
971                 return this;
972         },
973
974         // @method ceil(): Point
975         // Returns a copy of the current point with ceiled coordinates (rounded up).
976         ceil: function () {
977                 return this.clone()._ceil();
978         },
979
980         _ceil: function () {
981                 this.x = Math.ceil(this.x);
982                 this.y = Math.ceil(this.y);
983                 return this;
984         },
985
986         // @method distanceTo(otherPoint: Point): Number
987         // Returns the cartesian distance between the current and the given points.
988         distanceTo: function (point) {
989                 point = L.point(point);
990
991                 var x = point.x - this.x,
992                     y = point.y - this.y;
993
994                 return Math.sqrt(x * x + y * y);
995         },
996
997         // @method equals(otherPoint: Point): Boolean
998         // Returns `true` if the given point has the same coordinates.
999         equals: function (point) {
1000                 point = L.point(point);
1001
1002                 return point.x === this.x &&
1003                        point.y === this.y;
1004         },
1005
1006         // @method contains(otherPoint: Point): Boolean
1007         // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
1008         contains: function (point) {
1009                 point = L.point(point);
1010
1011                 return Math.abs(point.x) <= Math.abs(this.x) &&
1012                        Math.abs(point.y) <= Math.abs(this.y);
1013         },
1014
1015         // @method toString(): String
1016         // Returns a string representation of the point for debugging purposes.
1017         toString: function () {
1018                 return 'Point(' +
1019                         L.Util.formatNum(this.x) + ', ' +
1020                         L.Util.formatNum(this.y) + ')';
1021         }
1022 };
1023
1024 // @factory L.point(x: Number, y: Number, round?: Boolean)
1025 // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
1026
1027 // @alternative
1028 // @factory L.point(coords: Number[])
1029 // Expects an array of the form `[x, y]` instead.
1030
1031 // @alternative
1032 // @factory L.point(coords: Object)
1033 // Expects a plain object of the form `{x: Number, y: Number}` instead.
1034 L.point = function (x, y, round) {
1035         if (x instanceof L.Point) {
1036                 return x;
1037         }
1038         if (L.Util.isArray(x)) {
1039                 return new L.Point(x[0], x[1]);
1040         }
1041         if (x === undefined || x === null) {
1042                 return x;
1043         }
1044         if (typeof x === 'object' && 'x' in x && 'y' in x) {
1045                 return new L.Point(x.x, x.y);
1046         }
1047         return new L.Point(x, y, round);
1048 };
1049
1050
1051
1052 /*
1053  * @class Bounds
1054  * @aka L.Bounds
1055  *
1056  * Represents a rectangular area in pixel coordinates.
1057  *
1058  * @example
1059  *
1060  * ```js
1061  * var p1 = L.point(10, 10),
1062  * p2 = L.point(40, 60),
1063  * bounds = L.bounds(p1, p2);
1064  * ```
1065  *
1066  * All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
1067  *
1068  * ```js
1069  * otherBounds.intersects([[10, 10], [40, 60]]);
1070  * ```
1071  */
1072
1073 L.Bounds = function (a, b) {
1074         if (!a) { return; }
1075
1076         var points = b ? [a, b] : a;
1077
1078         for (var i = 0, len = points.length; i < len; i++) {
1079                 this.extend(points[i]);
1080         }
1081 };
1082
1083 L.Bounds.prototype = {
1084         // @method extend(point: Point): this
1085         // Extends the bounds to contain the given point.
1086         extend: function (point) { // (Point)
1087                 point = L.point(point);
1088
1089                 // @property min: Point
1090                 // The top left corner of the rectangle.
1091                 // @property max: Point
1092                 // The bottom right corner of the rectangle.
1093                 if (!this.min && !this.max) {
1094                         this.min = point.clone();
1095                         this.max = point.clone();
1096                 } else {
1097                         this.min.x = Math.min(point.x, this.min.x);
1098                         this.max.x = Math.max(point.x, this.max.x);
1099                         this.min.y = Math.min(point.y, this.min.y);
1100                         this.max.y = Math.max(point.y, this.max.y);
1101                 }
1102                 return this;
1103         },
1104
1105         // @method getCenter(round?: Boolean): Point
1106         // Returns the center point of the bounds.
1107         getCenter: function (round) {
1108                 return new L.Point(
1109                         (this.min.x + this.max.x) / 2,
1110                         (this.min.y + this.max.y) / 2, round);
1111         },
1112
1113         // @method getBottomLeft(): Point
1114         // Returns the bottom-left point of the bounds.
1115         getBottomLeft: function () {
1116                 return new L.Point(this.min.x, this.max.y);
1117         },
1118
1119         // @method getTopRight(): Point
1120         // Returns the top-right point of the bounds.
1121         getTopRight: function () { // -> Point
1122                 return new L.Point(this.max.x, this.min.y);
1123         },
1124
1125         // @method getSize(): Point
1126         // Returns the size of the given bounds
1127         getSize: function () {
1128                 return this.max.subtract(this.min);
1129         },
1130
1131         // @method contains(otherBounds: Bounds): Boolean
1132         // Returns `true` if the rectangle contains the given one.
1133         // @alternative
1134         // @method contains(point: Point): Boolean
1135         // Returns `true` if the rectangle contains the given point.
1136         contains: function (obj) {
1137                 var min, max;
1138
1139                 if (typeof obj[0] === 'number' || obj instanceof L.Point) {
1140                         obj = L.point(obj);
1141                 } else {
1142                         obj = L.bounds(obj);
1143                 }
1144
1145                 if (obj instanceof L.Bounds) {
1146                         min = obj.min;
1147                         max = obj.max;
1148                 } else {
1149                         min = max = obj;
1150                 }
1151
1152                 return (min.x >= this.min.x) &&
1153                        (max.x <= this.max.x) &&
1154                        (min.y >= this.min.y) &&
1155                        (max.y <= this.max.y);
1156         },
1157
1158         // @method intersects(otherBounds: Bounds): Boolean
1159         // Returns `true` if the rectangle intersects the given bounds. Two bounds
1160         // intersect if they have at least one point in common.
1161         intersects: function (bounds) { // (Bounds) -> Boolean
1162                 bounds = L.bounds(bounds);
1163
1164                 var min = this.min,
1165                     max = this.max,
1166                     min2 = bounds.min,
1167                     max2 = bounds.max,
1168                     xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
1169                     yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
1170
1171                 return xIntersects && yIntersects;
1172         },
1173
1174         // @method overlaps(otherBounds: Bounds): Boolean
1175         // Returns `true` if the rectangle overlaps the given bounds. Two bounds
1176         // overlap if their intersection is an area.
1177         overlaps: function (bounds) { // (Bounds) -> Boolean
1178                 bounds = L.bounds(bounds);
1179
1180                 var min = this.min,
1181                     max = this.max,
1182                     min2 = bounds.min,
1183                     max2 = bounds.max,
1184                     xOverlaps = (max2.x > min.x) && (min2.x < max.x),
1185                     yOverlaps = (max2.y > min.y) && (min2.y < max.y);
1186
1187                 return xOverlaps && yOverlaps;
1188         },
1189
1190         isValid: function () {
1191                 return !!(this.min && this.max);
1192         }
1193 };
1194
1195
1196 // @factory L.bounds(topLeft: Point, bottomRight: Point)
1197 // Creates a Bounds object from two coordinates (usually top-left and bottom-right corners).
1198 // @alternative
1199 // @factory L.bounds(points: Point[])
1200 // Creates a Bounds object from the points it contains
1201 L.bounds = function (a, b) {
1202         if (!a || a instanceof L.Bounds) {
1203                 return a;
1204         }
1205         return new L.Bounds(a, b);
1206 };
1207
1208
1209
1210 /*
1211  * @class Transformation
1212  * @aka L.Transformation
1213  *
1214  * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
1215  * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
1216  * the reverse. Used by Leaflet in its projections code.
1217  *
1218  * @example
1219  *
1220  * ```js
1221  * var transformation = new L.Transformation(2, 5, -1, 10),
1222  *      p = L.point(1, 2),
1223  *      p2 = transformation.transform(p), //  L.point(7, 8)
1224  *      p3 = transformation.untransform(p2); //  L.point(1, 2)
1225  * ```
1226  */
1227
1228
1229 // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
1230 // Creates a `Transformation` object with the given coefficients.
1231 L.Transformation = function (a, b, c, d) {
1232         this._a = a;
1233         this._b = b;
1234         this._c = c;
1235         this._d = d;
1236 };
1237
1238 L.Transformation.prototype = {
1239         // @method transform(point: Point, scale?: Number): Point
1240         // Returns a transformed point, optionally multiplied by the given scale.
1241         // Only accepts actual `L.Point` instances, not arrays.
1242         transform: function (point, scale) { // (Point, Number) -> Point
1243                 return this._transform(point.clone(), scale);
1244         },
1245
1246         // destructive transform (faster)
1247         _transform: function (point, scale) {
1248                 scale = scale || 1;
1249                 point.x = scale * (this._a * point.x + this._b);
1250                 point.y = scale * (this._c * point.y + this._d);
1251                 return point;
1252         },
1253
1254         // @method untransform(point: Point, scale?: Number): Point
1255         // Returns the reverse transformation of the given point, optionally divided
1256         // by the given scale. Only accepts actual `L.Point` instances, not arrays.
1257         untransform: function (point, scale) {
1258                 scale = scale || 1;
1259                 return new L.Point(
1260                         (point.x / scale - this._b) / this._a,
1261                         (point.y / scale - this._d) / this._c);
1262         }
1263 };
1264
1265
1266
1267 /*
1268  * @namespace DomUtil
1269  *
1270  * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
1271  * tree, used by Leaflet internally.
1272  *
1273  * Most functions expecting or returning a `HTMLElement` also work for
1274  * SVG elements. The only difference is that classes refer to CSS classes
1275  * in HTML and SVG classes in SVG.
1276  */
1277
1278 L.DomUtil = {
1279
1280         // @function get(id: String|HTMLElement): HTMLElement
1281         // Returns an element given its DOM id, or returns the element itself
1282         // if it was passed directly.
1283         get: function (id) {
1284                 return typeof id === 'string' ? document.getElementById(id) : id;
1285         },
1286
1287         // @function getStyle(el: HTMLElement, styleAttrib: String): String
1288         // Returns the value for a certain style attribute on an element,
1289         // including computed values or values set through CSS.
1290         getStyle: function (el, style) {
1291
1292                 var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
1293
1294                 if ((!value || value === 'auto') && document.defaultView) {
1295                         var css = document.defaultView.getComputedStyle(el, null);
1296                         value = css ? css[style] : null;
1297                 }
1298
1299                 return value === 'auto' ? null : value;
1300         },
1301
1302         // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
1303         // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
1304         create: function (tagName, className, container) {
1305
1306                 var el = document.createElement(tagName);
1307                 el.className = className || '';
1308
1309                 if (container) {
1310                         container.appendChild(el);
1311                 }
1312
1313                 return el;
1314         },
1315
1316         // @function remove(el: HTMLElement)
1317         // Removes `el` from its parent element
1318         remove: function (el) {
1319                 var parent = el.parentNode;
1320                 if (parent) {
1321                         parent.removeChild(el);
1322                 }
1323         },
1324
1325         // @function empty(el: HTMLElement)
1326         // Removes all of `el`'s children elements from `el`
1327         empty: function (el) {
1328                 while (el.firstChild) {
1329                         el.removeChild(el.firstChild);
1330                 }
1331         },
1332
1333         // @function toFront(el: HTMLElement)
1334         // Makes `el` the last children of its parent, so it renders in front of the other children.
1335         toFront: function (el) {
1336                 el.parentNode.appendChild(el);
1337         },
1338
1339         // @function toBack(el: HTMLElement)
1340         // Makes `el` the first children of its parent, so it renders back from the other children.
1341         toBack: function (el) {
1342                 var parent = el.parentNode;
1343                 parent.insertBefore(el, parent.firstChild);
1344         },
1345
1346         // @function hasClass(el: HTMLElement, name: String): Boolean
1347         // Returns `true` if the element's class attribute contains `name`.
1348         hasClass: function (el, name) {
1349                 if (el.classList !== undefined) {
1350                         return el.classList.contains(name);
1351                 }
1352                 var className = L.DomUtil.getClass(el);
1353                 return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
1354         },
1355
1356         // @function addClass(el: HTMLElement, name: String)
1357         // Adds `name` to the element's class attribute.
1358         addClass: function (el, name) {
1359                 if (el.classList !== undefined) {
1360                         var classes = L.Util.splitWords(name);
1361                         for (var i = 0, len = classes.length; i < len; i++) {
1362                                 el.classList.add(classes[i]);
1363                         }
1364                 } else if (!L.DomUtil.hasClass(el, name)) {
1365                         var className = L.DomUtil.getClass(el);
1366                         L.DomUtil.setClass(el, (className ? className + ' ' : '') + name);
1367                 }
1368         },
1369
1370         // @function removeClass(el: HTMLElement, name: String)
1371         // Removes `name` from the element's class attribute.
1372         removeClass: function (el, name) {
1373                 if (el.classList !== undefined) {
1374                         el.classList.remove(name);
1375                 } else {
1376                         L.DomUtil.setClass(el, L.Util.trim((' ' + L.DomUtil.getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
1377                 }
1378         },
1379
1380         // @function setClass(el: HTMLElement, name: String)
1381         // Sets the element's class.
1382         setClass: function (el, name) {
1383                 if (el.className.baseVal === undefined) {
1384                         el.className = name;
1385                 } else {
1386                         // in case of SVG element
1387                         el.className.baseVal = name;
1388                 }
1389         },
1390
1391         // @function getClass(el: HTMLElement): String
1392         // Returns the element's class.
1393         getClass: function (el) {
1394                 return el.className.baseVal === undefined ? el.className : el.className.baseVal;
1395         },
1396
1397         // @function setOpacity(el: HTMLElement, opacity: Number)
1398         // Set the opacity of an element (including old IE support).
1399         // `opacity` must be a number from `0` to `1`.
1400         setOpacity: function (el, value) {
1401
1402                 if ('opacity' in el.style) {
1403                         el.style.opacity = value;
1404
1405                 } else if ('filter' in el.style) {
1406                         L.DomUtil._setOpacityIE(el, value);
1407                 }
1408         },
1409
1410         _setOpacityIE: function (el, value) {
1411                 var filter = false,
1412                     filterName = 'DXImageTransform.Microsoft.Alpha';
1413
1414                 // filters collection throws an error if we try to retrieve a filter that doesn't exist
1415                 try {
1416                         filter = el.filters.item(filterName);
1417                 } catch (e) {
1418                         // don't set opacity to 1 if we haven't already set an opacity,
1419                         // it isn't needed and breaks transparent pngs.
1420                         if (value === 1) { return; }
1421                 }
1422
1423                 value = Math.round(value * 100);
1424
1425                 if (filter) {
1426                         filter.Enabled = (value !== 100);
1427                         filter.Opacity = value;
1428                 } else {
1429                         el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
1430                 }
1431         },
1432
1433         // @function testProp(props: String[]): String|false
1434         // Goes through the array of style names and returns the first name
1435         // that is a valid style name for an element. If no such name is found,
1436         // it returns false. Useful for vendor-prefixed styles like `transform`.
1437         testProp: function (props) {
1438
1439                 var style = document.documentElement.style;
1440
1441                 for (var i = 0; i < props.length; i++) {
1442                         if (props[i] in style) {
1443                                 return props[i];
1444                         }
1445                 }
1446                 return false;
1447         },
1448
1449         // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
1450         // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
1451         // and optionally scaled by `scale`. Does not have an effect if the
1452         // browser doesn't support 3D CSS transforms.
1453         setTransform: function (el, offset, scale) {
1454                 var pos = offset || new L.Point(0, 0);
1455
1456                 el.style[L.DomUtil.TRANSFORM] =
1457                         (L.Browser.ie3d ?
1458                                 'translate(' + pos.x + 'px,' + pos.y + 'px)' :
1459                                 'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
1460                         (scale ? ' scale(' + scale + ')' : '');
1461         },
1462
1463         // @function setPosition(el: HTMLElement, position: Point)
1464         // Sets the position of `el` to coordinates specified by `position`,
1465         // using CSS translate or top/left positioning depending on the browser
1466         // (used by Leaflet internally to position its layers).
1467         setPosition: function (el, point) { // (HTMLElement, Point[, Boolean])
1468
1469                 /*eslint-disable */
1470                 el._leaflet_pos = point;
1471                 /*eslint-enable */
1472
1473                 if (L.Browser.any3d) {
1474                         L.DomUtil.setTransform(el, point);
1475                 } else {
1476                         el.style.left = point.x + 'px';
1477                         el.style.top = point.y + 'px';
1478                 }
1479         },
1480
1481         // @function getPosition(el: HTMLElement): Point
1482         // Returns the coordinates of an element previously positioned with setPosition.
1483         getPosition: function (el) {
1484                 // this method is only used for elements previously positioned using setPosition,
1485                 // so it's safe to cache the position for performance
1486
1487                 return el._leaflet_pos || new L.Point(0, 0);
1488         }
1489 };
1490
1491
1492 (function () {
1493         // prefix style property names
1494
1495         // @property TRANSFORM: String
1496         // Vendor-prefixed fransform style name (e.g. `'webkitTransform'` for WebKit).
1497         L.DomUtil.TRANSFORM = L.DomUtil.testProp(
1498                         ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
1499
1500
1501         // webkitTransition comes first because some browser versions that drop vendor prefix don't do
1502         // the same for the transitionend event, in particular the Android 4.1 stock browser
1503
1504         // @property TRANSITION: String
1505         // Vendor-prefixed transform style name.
1506         var transition = L.DomUtil.TRANSITION = L.DomUtil.testProp(
1507                         ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
1508
1509         L.DomUtil.TRANSITION_END =
1510                         transition === 'webkitTransition' || transition === 'OTransition' ? transition + 'End' : 'transitionend';
1511
1512         // @function disableTextSelection()
1513         // Prevents the user from generating `selectstart` DOM events, usually generated
1514         // when the user drags the mouse through a page with text. Used internally
1515         // by Leaflet to override the behaviour of any click-and-drag interaction on
1516         // the map. Affects drag interactions on the whole document.
1517
1518         // @function enableTextSelection()
1519         // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
1520         if ('onselectstart' in document) {
1521                 L.DomUtil.disableTextSelection = function () {
1522                         L.DomEvent.on(window, 'selectstart', L.DomEvent.preventDefault);
1523                 };
1524                 L.DomUtil.enableTextSelection = function () {
1525                         L.DomEvent.off(window, 'selectstart', L.DomEvent.preventDefault);
1526                 };
1527
1528         } else {
1529                 var userSelectProperty = L.DomUtil.testProp(
1530                         ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
1531
1532                 L.DomUtil.disableTextSelection = function () {
1533                         if (userSelectProperty) {
1534                                 var style = document.documentElement.style;
1535                                 this._userSelect = style[userSelectProperty];
1536                                 style[userSelectProperty] = 'none';
1537                         }
1538                 };
1539                 L.DomUtil.enableTextSelection = function () {
1540                         if (userSelectProperty) {
1541                                 document.documentElement.style[userSelectProperty] = this._userSelect;
1542                                 delete this._userSelect;
1543                         }
1544                 };
1545         }
1546
1547         // @function disableImageDrag()
1548         // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
1549         // for `dragstart` DOM events, usually generated when the user drags an image.
1550         L.DomUtil.disableImageDrag = function () {
1551                 L.DomEvent.on(window, 'dragstart', L.DomEvent.preventDefault);
1552         };
1553
1554         // @function enableImageDrag()
1555         // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
1556         L.DomUtil.enableImageDrag = function () {
1557                 L.DomEvent.off(window, 'dragstart', L.DomEvent.preventDefault);
1558         };
1559
1560         // @function preventOutline(el: HTMLElement)
1561         // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
1562         // of the element `el` invisible. Used internally by Leaflet to prevent
1563         // focusable elements from displaying an outline when the user performs a
1564         // drag interaction on them.
1565         L.DomUtil.preventOutline = function (element) {
1566                 while (element.tabIndex === -1) {
1567                         element = element.parentNode;
1568                 }
1569                 if (!element || !element.style) { return; }
1570                 L.DomUtil.restoreOutline();
1571                 this._outlineElement = element;
1572                 this._outlineStyle = element.style.outline;
1573                 element.style.outline = 'none';
1574                 L.DomEvent.on(window, 'keydown', L.DomUtil.restoreOutline, this);
1575         };
1576
1577         // @function restoreOutline()
1578         // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
1579         L.DomUtil.restoreOutline = function () {
1580                 if (!this._outlineElement) { return; }
1581                 this._outlineElement.style.outline = this._outlineStyle;
1582                 delete this._outlineElement;
1583                 delete this._outlineStyle;
1584                 L.DomEvent.off(window, 'keydown', L.DomUtil.restoreOutline, this);
1585         };
1586 })();
1587
1588
1589
1590 /* @class LatLng
1591  * @aka L.LatLng
1592  *
1593  * Represents a geographical point with a certain latitude and longitude.
1594  *
1595  * @example
1596  *
1597  * ```
1598  * var latlng = L.latLng(50.5, 30.5);
1599  * ```
1600  *
1601  * All Leaflet methods that accept LatLng objects also accept them in a simple Array form and simple object form (unless noted otherwise), so these lines are equivalent:
1602  *
1603  * ```
1604  * map.panTo([50, 30]);
1605  * map.panTo({lon: 30, lat: 50});
1606  * map.panTo({lat: 50, lng: 30});
1607  * map.panTo(L.latLng(50, 30));
1608  * ```
1609  */
1610
1611 L.LatLng = function (lat, lng, alt) {
1612         if (isNaN(lat) || isNaN(lng)) {
1613                 throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
1614         }
1615
1616         // @property lat: Number
1617         // Latitude in degrees
1618         this.lat = +lat;
1619
1620         // @property lng: Number
1621         // Longitude in degrees
1622         this.lng = +lng;
1623
1624         // @property alt: Number
1625         // Altitude in meters (optional)
1626         if (alt !== undefined) {
1627                 this.alt = +alt;
1628         }
1629 };
1630
1631 L.LatLng.prototype = {
1632         // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
1633         // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overriden by setting `maxMargin` to a small number.
1634         equals: function (obj, maxMargin) {
1635                 if (!obj) { return false; }
1636
1637                 obj = L.latLng(obj);
1638
1639                 var margin = Math.max(
1640                         Math.abs(this.lat - obj.lat),
1641                         Math.abs(this.lng - obj.lng));
1642
1643                 return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
1644         },
1645
1646         // @method toString(): String
1647         // Returns a string representation of the point (for debugging purposes).
1648         toString: function (precision) {
1649                 return 'LatLng(' +
1650                         L.Util.formatNum(this.lat, precision) + ', ' +
1651                         L.Util.formatNum(this.lng, precision) + ')';
1652         },
1653
1654         // @method distanceTo(otherLatLng: LatLng): Number
1655         // Returns the distance (in meters) to the given `LatLng` calculated using the [Haversine formula](http://en.wikipedia.org/wiki/Haversine_formula).
1656         distanceTo: function (other) {
1657                 return L.CRS.Earth.distance(this, L.latLng(other));
1658         },
1659
1660         // @method wrap(): LatLng
1661         // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
1662         wrap: function () {
1663                 return L.CRS.Earth.wrapLatLng(this);
1664         },
1665
1666         // @method toBounds(sizeInMeters: Number): LatLngBounds
1667         // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
1668         toBounds: function (sizeInMeters) {
1669                 var latAccuracy = 180 * sizeInMeters / 40075017,
1670                     lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
1671
1672                 return L.latLngBounds(
1673                         [this.lat - latAccuracy, this.lng - lngAccuracy],
1674                         [this.lat + latAccuracy, this.lng + lngAccuracy]);
1675         },
1676
1677         clone: function () {
1678                 return new L.LatLng(this.lat, this.lng, this.alt);
1679         }
1680 };
1681
1682
1683
1684 // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
1685 // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
1686
1687 // @alternative
1688 // @factory L.latLng(coords: Array): LatLng
1689 // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
1690
1691 // @alternative
1692 // @factory L.latLng(coords: Object): LatLng
1693 // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
1694
1695 L.latLng = function (a, b, c) {
1696         if (a instanceof L.LatLng) {
1697                 return a;
1698         }
1699         if (L.Util.isArray(a) && typeof a[0] !== 'object') {
1700                 if (a.length === 3) {
1701                         return new L.LatLng(a[0], a[1], a[2]);
1702                 }
1703                 if (a.length === 2) {
1704                         return new L.LatLng(a[0], a[1]);
1705                 }
1706                 return null;
1707         }
1708         if (a === undefined || a === null) {
1709                 return a;
1710         }
1711         if (typeof a === 'object' && 'lat' in a) {
1712                 return new L.LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
1713         }
1714         if (b === undefined) {
1715                 return null;
1716         }
1717         return new L.LatLng(a, b, c);
1718 };
1719
1720
1721
1722 /*
1723  * @class LatLngBounds
1724  * @aka L.LatLngBounds
1725  *
1726  * Represents a rectangular geographical area on a map.
1727  *
1728  * @example
1729  *
1730  * ```js
1731  * var corner1 = L.latLng(40.712, -74.227),
1732  * corner2 = L.latLng(40.774, -74.125),
1733  * bounds = L.latLngBounds(corner1, corner2);
1734  * ```
1735  *
1736  * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
1737  *
1738  * ```js
1739  * map.fitBounds([
1740  *      [40.712, -74.227],
1741  *      [40.774, -74.125]
1742  * ]);
1743  * ```
1744  *
1745  * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range.
1746  */
1747
1748 L.LatLngBounds = function (corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
1749         if (!corner1) { return; }
1750
1751         var latlngs = corner2 ? [corner1, corner2] : corner1;
1752
1753         for (var i = 0, len = latlngs.length; i < len; i++) {
1754                 this.extend(latlngs[i]);
1755         }
1756 };
1757
1758 L.LatLngBounds.prototype = {
1759
1760         // @method extend(latlng: LatLng): this
1761         // Extend the bounds to contain the given point
1762
1763         // @alternative
1764         // @method extend(otherBounds: LatLngBounds): this
1765         // Extend the bounds to contain the given bounds
1766         extend: function (obj) {
1767                 var sw = this._southWest,
1768                     ne = this._northEast,
1769                     sw2, ne2;
1770
1771                 if (obj instanceof L.LatLng) {
1772                         sw2 = obj;
1773                         ne2 = obj;
1774
1775                 } else if (obj instanceof L.LatLngBounds) {
1776                         sw2 = obj._southWest;
1777                         ne2 = obj._northEast;
1778
1779                         if (!sw2 || !ne2) { return this; }
1780
1781                 } else {
1782                         return obj ? this.extend(L.latLng(obj) || L.latLngBounds(obj)) : this;
1783                 }
1784
1785                 if (!sw && !ne) {
1786                         this._southWest = new L.LatLng(sw2.lat, sw2.lng);
1787                         this._northEast = new L.LatLng(ne2.lat, ne2.lng);
1788                 } else {
1789                         sw.lat = Math.min(sw2.lat, sw.lat);
1790                         sw.lng = Math.min(sw2.lng, sw.lng);
1791                         ne.lat = Math.max(ne2.lat, ne.lat);
1792                         ne.lng = Math.max(ne2.lng, ne.lng);
1793                 }
1794
1795                 return this;
1796         },
1797
1798         // @method pad(bufferRatio: Number): LatLngBounds
1799         // Returns bigger bounds created by extending the current bounds by a given percentage in each direction.
1800         pad: function (bufferRatio) {
1801                 var sw = this._southWest,
1802                     ne = this._northEast,
1803                     heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
1804                     widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
1805
1806                 return new L.LatLngBounds(
1807                         new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
1808                         new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
1809         },
1810
1811         // @method getCenter(): LatLng
1812         // Returns the center point of the bounds.
1813         getCenter: function () {
1814                 return new L.LatLng(
1815                         (this._southWest.lat + this._northEast.lat) / 2,
1816                         (this._southWest.lng + this._northEast.lng) / 2);
1817         },
1818
1819         // @method getSouthWest(): LatLng
1820         // Returns the south-west point of the bounds.
1821         getSouthWest: function () {
1822                 return this._southWest;
1823         },
1824
1825         // @method getNorthEast(): LatLng
1826         // Returns the north-east point of the bounds.
1827         getNorthEast: function () {
1828                 return this._northEast;
1829         },
1830
1831         // @method getNorthWest(): LatLng
1832         // Returns the north-west point of the bounds.
1833         getNorthWest: function () {
1834                 return new L.LatLng(this.getNorth(), this.getWest());
1835         },
1836
1837         // @method getSouthEast(): LatLng
1838         // Returns the south-east point of the bounds.
1839         getSouthEast: function () {
1840                 return new L.LatLng(this.getSouth(), this.getEast());
1841         },
1842
1843         // @method getWest(): Number
1844         // Returns the west longitude of the bounds
1845         getWest: function () {
1846                 return this._southWest.lng;
1847         },
1848
1849         // @method getSouth(): Number
1850         // Returns the south latitude of the bounds
1851         getSouth: function () {
1852                 return this._southWest.lat;
1853         },
1854
1855         // @method getEast(): Number
1856         // Returns the east longitude of the bounds
1857         getEast: function () {
1858                 return this._northEast.lng;
1859         },
1860
1861         // @method getNorth(): Number
1862         // Returns the north latitude of the bounds
1863         getNorth: function () {
1864                 return this._northEast.lat;
1865         },
1866
1867         // @method contains(otherBounds: LatLngBounds): Boolean
1868         // Returns `true` if the rectangle contains the given one.
1869
1870         // @alternative
1871         // @method contains (latlng: LatLng): Boolean
1872         // Returns `true` if the rectangle contains the given point.
1873         contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
1874                 if (typeof obj[0] === 'number' || obj instanceof L.LatLng || 'lat' in obj) {
1875                         obj = L.latLng(obj);
1876                 } else {
1877                         obj = L.latLngBounds(obj);
1878                 }
1879
1880                 var sw = this._southWest,
1881                     ne = this._northEast,
1882                     sw2, ne2;
1883
1884                 if (obj instanceof L.LatLngBounds) {
1885                         sw2 = obj.getSouthWest();
1886                         ne2 = obj.getNorthEast();
1887                 } else {
1888                         sw2 = ne2 = obj;
1889                 }
1890
1891                 return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
1892                        (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
1893         },
1894
1895         // @method intersects(otherBounds: LatLngBounds): Boolean
1896         // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
1897         intersects: function (bounds) {
1898                 bounds = L.latLngBounds(bounds);
1899
1900                 var sw = this._southWest,
1901                     ne = this._northEast,
1902                     sw2 = bounds.getSouthWest(),
1903                     ne2 = bounds.getNorthEast(),
1904
1905                     latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
1906                     lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
1907
1908                 return latIntersects && lngIntersects;
1909         },
1910
1911         // @method overlaps(otherBounds: Bounds): Boolean
1912         // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
1913         overlaps: function (bounds) {
1914                 bounds = L.latLngBounds(bounds);
1915
1916                 var sw = this._southWest,
1917                     ne = this._northEast,
1918                     sw2 = bounds.getSouthWest(),
1919                     ne2 = bounds.getNorthEast(),
1920
1921                     latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
1922                     lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
1923
1924                 return latOverlaps && lngOverlaps;
1925         },
1926
1927         // @method toBBoxString(): String
1928         // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data.
1929         toBBoxString: function () {
1930                 return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
1931         },
1932
1933         // @method equals(otherBounds: LatLngBounds): Boolean
1934         // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds.
1935         equals: function (bounds) {
1936                 if (!bounds) { return false; }
1937
1938                 bounds = L.latLngBounds(bounds);
1939
1940                 return this._southWest.equals(bounds.getSouthWest()) &&
1941                        this._northEast.equals(bounds.getNorthEast());
1942         },
1943
1944         // @method isValid(): Boolean
1945         // Returns `true` if the bounds are properly initialized.
1946         isValid: function () {
1947                 return !!(this._southWest && this._northEast);
1948         }
1949 };
1950
1951 // TODO International date line?
1952
1953 // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
1954 // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
1955
1956 // @alternative
1957 // @factory L.latLngBounds(latlngs: LatLng[])
1958 // Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds).
1959 L.latLngBounds = function (a, b) {
1960         if (a instanceof L.LatLngBounds) {
1961                 return a;
1962         }
1963         return new L.LatLngBounds(a, b);
1964 };
1965
1966
1967
1968 /*
1969  * @namespace Projection
1970  * @section
1971  * Leaflet comes with a set of already defined Projections out of the box:
1972  *
1973  * @projection L.Projection.LonLat
1974  *
1975  * Equirectangular, or Plate Carree projection — the most simple projection,
1976  * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
1977  * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
1978  * `EPSG:3395` and `Simple` CRS.
1979  */
1980
1981 L.Projection = {};
1982
1983 L.Projection.LonLat = {
1984         project: function (latlng) {
1985                 return new L.Point(latlng.lng, latlng.lat);
1986         },
1987
1988         unproject: function (point) {
1989                 return new L.LatLng(point.y, point.x);
1990         },
1991
1992         bounds: L.bounds([-180, -90], [180, 90])
1993 };
1994
1995
1996
1997 /*
1998  * @namespace Projection
1999  * @projection L.Projection.SphericalMercator
2000  *
2001  * Spherical Mercator projection — the most common projection for online maps,
2002  * used by almost all free and commercial tile providers. Assumes that Earth is
2003  * a sphere. Used by the `EPSG:3857` CRS.
2004  */
2005
2006 L.Projection.SphericalMercator = {
2007
2008         R: 6378137,
2009         MAX_LATITUDE: 85.0511287798,
2010
2011         project: function (latlng) {
2012                 var d = Math.PI / 180,
2013                     max = this.MAX_LATITUDE,
2014                     lat = Math.max(Math.min(max, latlng.lat), -max),
2015                     sin = Math.sin(lat * d);
2016
2017                 return new L.Point(
2018                                 this.R * latlng.lng * d,
2019                                 this.R * Math.log((1 + sin) / (1 - sin)) / 2);
2020         },
2021
2022         unproject: function (point) {
2023                 var d = 180 / Math.PI;
2024
2025                 return new L.LatLng(
2026                         (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
2027                         point.x * d / this.R);
2028         },
2029
2030         bounds: (function () {
2031                 var d = 6378137 * Math.PI;
2032                 return L.bounds([-d, -d], [d, d]);
2033         })()
2034 };
2035
2036
2037
2038 /*
2039  * @class CRS
2040  * @aka L.CRS
2041  * Abstract class that defines coordinate reference systems for projecting
2042  * geographical points into pixel (screen) coordinates and back (and to
2043  * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
2044  * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
2045  *
2046  * Leaflet defines the most usual CRSs by default. If you want to use a
2047  * CRS not defined by default, take a look at the
2048  * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
2049  */
2050
2051 L.CRS = {
2052         // @method latLngToPoint(latlng: LatLng, zoom: Number): Point
2053         // Projects geographical coordinates into pixel coordinates for a given zoom.
2054         latLngToPoint: function (latlng, zoom) {
2055                 var projectedPoint = this.projection.project(latlng),
2056                     scale = this.scale(zoom);
2057
2058                 return this.transformation._transform(projectedPoint, scale);
2059         },
2060
2061         // @method pointToLatLng(point: Point, zoom: Number): LatLng
2062         // The inverse of `latLngToPoint`. Projects pixel coordinates on a given
2063         // zoom into geographical coordinates.
2064         pointToLatLng: function (point, zoom) {
2065                 var scale = this.scale(zoom),
2066                     untransformedPoint = this.transformation.untransform(point, scale);
2067
2068                 return this.projection.unproject(untransformedPoint);
2069         },
2070
2071         // @method project(latlng: LatLng): Point
2072         // Projects geographical coordinates into coordinates in units accepted for
2073         // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
2074         project: function (latlng) {
2075                 return this.projection.project(latlng);
2076         },
2077
2078         // @method unproject(point: Point): LatLng
2079         // Given a projected coordinate returns the corresponding LatLng.
2080         // The inverse of `project`.
2081         unproject: function (point) {
2082                 return this.projection.unproject(point);
2083         },
2084
2085         // @method scale(zoom: Number): Number
2086         // Returns the scale used when transforming projected coordinates into
2087         // pixel coordinates for a particular zoom. For example, it returns
2088         // `256 * 2^zoom` for Mercator-based CRS.
2089         scale: function (zoom) {
2090                 return 256 * Math.pow(2, zoom);
2091         },
2092
2093         // @method zoom(scale: Number): Number
2094         // Inverse of `scale()`, returns the zoom level corresponding to a scale
2095         // factor of `scale`.
2096         zoom: function (scale) {
2097                 return Math.log(scale / 256) / Math.LN2;
2098         },
2099
2100         // @method getProjectedBounds(zoom: Number): Bounds
2101         // Returns the projection's bounds scaled and transformed for the provided `zoom`.
2102         getProjectedBounds: function (zoom) {
2103                 if (this.infinite) { return null; }
2104
2105                 var b = this.projection.bounds,
2106                     s = this.scale(zoom),
2107                     min = this.transformation.transform(b.min, s),
2108                     max = this.transformation.transform(b.max, s);
2109
2110                 return L.bounds(min, max);
2111         },
2112
2113         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
2114         // Returns the distance between two geographical coordinates.
2115
2116         // @property code: String
2117         // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
2118         //
2119         // @property wrapLng: Number[]
2120         // An array of two numbers defining whether the longitude (horizontal) coordinate
2121         // axis wraps around a given range and how. Defaults to `[-180, 180]` in most
2122         // geographical CRSs. If `undefined`, the longitude axis does not wrap around.
2123         //
2124         // @property wrapLat: Number[]
2125         // Like `wrapLng`, but for the latitude (vertical) axis.
2126
2127         // wrapLng: [min, max],
2128         // wrapLat: [min, max],
2129
2130         // @property infinite: Boolean
2131         // If true, the coordinate space will be unbounded (infinite in both axes)
2132         infinite: false,
2133
2134         // @method wrapLatLng(latlng: LatLng): LatLng
2135         // Returns a `LatLng` where lat and lng has been wrapped according to the
2136         // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
2137         // Only accepts actual `L.LatLng` instances, not arrays.
2138         wrapLatLng: function (latlng) {
2139                 var lng = this.wrapLng ? L.Util.wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
2140                     lat = this.wrapLat ? L.Util.wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
2141                     alt = latlng.alt;
2142
2143                 return L.latLng(lat, lng, alt);
2144         },
2145
2146         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
2147         // Returns a `LatLngBounds` with the same size as the given one, ensuring
2148         // that its center is within the CRS's bounds.
2149         // Only accepts actual `L.LatLngBounds` instances, not arrays.
2150         wrapLatLngBounds: function (bounds) {
2151                 var center = bounds.getCenter(),
2152                     newCenter = this.wrapLatLng(center),
2153                     latShift = center.lat - newCenter.lat,
2154                     lngShift = center.lng - newCenter.lng;
2155
2156                 if (latShift === 0 && lngShift === 0) {
2157                         return bounds;
2158                 }
2159
2160                 var sw = bounds.getSouthWest(),
2161                     ne = bounds.getNorthEast(),
2162                     newSw = L.latLng({lat: sw.lat - latShift, lng: sw.lng - lngShift}),
2163                     newNe = L.latLng({lat: ne.lat - latShift, lng: ne.lng - lngShift});
2164
2165                 return new L.LatLngBounds(newSw, newNe);
2166         }
2167 };
2168
2169
2170
2171 /*
2172  * @namespace CRS
2173  * @crs L.CRS.Simple
2174  *
2175  * A simple CRS that maps longitude and latitude into `x` and `y` directly.
2176  * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
2177  * axis should still be inverted (going from bottom to top). `distance()` returns
2178  * simple euclidean distance.
2179  */
2180
2181 L.CRS.Simple = L.extend({}, L.CRS, {
2182         projection: L.Projection.LonLat,
2183         transformation: new L.Transformation(1, 0, -1, 0),
2184
2185         scale: function (zoom) {
2186                 return Math.pow(2, zoom);
2187         },
2188
2189         zoom: function (scale) {
2190                 return Math.log(scale) / Math.LN2;
2191         },
2192
2193         distance: function (latlng1, latlng2) {
2194                 var dx = latlng2.lng - latlng1.lng,
2195                     dy = latlng2.lat - latlng1.lat;
2196
2197                 return Math.sqrt(dx * dx + dy * dy);
2198         },
2199
2200         infinite: true
2201 });
2202
2203
2204
2205 /*
2206  * @namespace CRS
2207  * @crs L.CRS.Earth
2208  *
2209  * Serves as the base for CRS that are global such that they cover the earth.
2210  * Can only be used as the base for other CRS and cannot be used directly,
2211  * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
2212  * meters.
2213  */
2214
2215 L.CRS.Earth = L.extend({}, L.CRS, {
2216         wrapLng: [-180, 180],
2217
2218         // Mean Earth Radius, as recommended for use by
2219         // the International Union of Geodesy and Geophysics,
2220         // see http://rosettacode.org/wiki/Haversine_formula
2221         R: 6371000,
2222
2223         // distance between two geographical points using spherical law of cosines approximation
2224         distance: function (latlng1, latlng2) {
2225                 var rad = Math.PI / 180,
2226                     lat1 = latlng1.lat * rad,
2227                     lat2 = latlng2.lat * rad,
2228                     a = Math.sin(lat1) * Math.sin(lat2) +
2229                         Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlng2.lng - latlng1.lng) * rad);
2230
2231                 return this.R * Math.acos(Math.min(a, 1));
2232         }
2233 });
2234
2235
2236
2237 /*
2238  * @namespace CRS
2239  * @crs L.CRS.EPSG3857
2240  *
2241  * The most common CRS for online maps, used by almost all free and commercial
2242  * tile providers. Uses Spherical Mercator projection. Set in by default in
2243  * Map's `crs` option.
2244  */
2245
2246 L.CRS.EPSG3857 = L.extend({}, L.CRS.Earth, {
2247         code: 'EPSG:3857',
2248         projection: L.Projection.SphericalMercator,
2249
2250         transformation: (function () {
2251                 var scale = 0.5 / (Math.PI * L.Projection.SphericalMercator.R);
2252                 return new L.Transformation(scale, 0.5, -scale, 0.5);
2253         }())
2254 });
2255
2256 L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, {
2257         code: 'EPSG:900913'
2258 });
2259
2260
2261
2262 /*
2263  * @namespace CRS
2264  * @crs L.CRS.EPSG4326
2265  *
2266  * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
2267  *
2268  * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
2269  * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
2270  * with this CRS, ensure that there are two 256x256 pixel tiles covering the
2271  * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
2272  * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
2273  */
2274
2275 L.CRS.EPSG4326 = L.extend({}, L.CRS.Earth, {
2276         code: 'EPSG:4326',
2277         projection: L.Projection.LonLat,
2278         transformation: new L.Transformation(1 / 180, 1, -1 / 180, 0.5)
2279 });
2280
2281
2282
2283 /*
2284  * @class Map
2285  * @aka L.Map
2286  * @inherits Evented
2287  *
2288  * The central class of the API — it is used to create a map on a page and manipulate it.
2289  *
2290  * @example
2291  *
2292  * ```js
2293  * // initialize the map on the "map" div with a given center and zoom
2294  * var map = L.map('map', {
2295  *      center: [51.505, -0.09],
2296  *      zoom: 13
2297  * });
2298  * ```
2299  *
2300  */
2301
2302 L.Map = L.Evented.extend({
2303
2304         options: {
2305                 // @section Map State Options
2306                 // @option crs: CRS = L.CRS.EPSG3857
2307                 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
2308                 // sure what it means.
2309                 crs: L.CRS.EPSG3857,
2310
2311                 // @option center: LatLng = undefined
2312                 // Initial geographic center of the map
2313                 center: undefined,
2314
2315                 // @option zoom: Number = undefined
2316                 // Initial map zoom level
2317                 zoom: undefined,
2318
2319                 // @option minZoom: Number = undefined
2320                 // Minimum zoom level of the map. Overrides any `minZoom` option set on map layers.
2321                 minZoom: undefined,
2322
2323                 // @option maxZoom: Number = undefined
2324                 // Maximum zoom level of the map. Overrides any `maxZoom` option set on map layers.
2325                 maxZoom: undefined,
2326
2327                 // @option layers: Layer[] = []
2328                 // Array of layers that will be added to the map initially
2329                 layers: [],
2330
2331                 // @option maxBounds: LatLngBounds = null
2332                 // When this option is set, the map restricts the view to the given
2333                 // geographical bounds, bouncing the user back if the user tries to pan
2334                 // outside the view. To set the restriction dynamically, use
2335                 // [`setMaxBounds`](#map-setmaxbounds) method.
2336                 maxBounds: undefined,
2337
2338                 // @option renderer: Renderer = *
2339                 // The default method for drawing vector layers on the map. `L.SVG`
2340                 // or `L.Canvas` by default depending on browser support.
2341                 renderer: undefined,
2342
2343
2344                 // @section Animation Options
2345                 // @option zoomAnimation: Boolean = true
2346                 // Whether the map zoom animation is enabled. By default it's enabled
2347                 // in all browsers that support CSS3 Transitions except Android.
2348                 zoomAnimation: true,
2349
2350                 // @option zoomAnimationThreshold: Number = 4
2351                 // Won't animate zoom if the zoom difference exceeds this value.
2352                 zoomAnimationThreshold: 4,
2353
2354                 // @option fadeAnimation: Boolean = true
2355                 // Whether the tile fade animation is enabled. By default it's enabled
2356                 // in all browsers that support CSS3 Transitions except Android.
2357                 fadeAnimation: true,
2358
2359                 // @option markerZoomAnimation: Boolean = true
2360                 // Whether markers animate their zoom with the zoom animation, if disabled
2361                 // they will disappear for the length of the animation. By default it's
2362                 // enabled in all browsers that support CSS3 Transitions except Android.
2363                 markerZoomAnimation: true,
2364
2365                 // @option transform3DLimit: Number = 2^23
2366                 // Defines the maximum size of a CSS translation transform. The default
2367                 // value should not be changed unless a web browser positions layers in
2368                 // the wrong place after doing a large `panBy`.
2369                 transform3DLimit: 8388608, // Precision limit of a 32-bit float
2370
2371                 // @section Interaction Options
2372                 // @option zoomSnap: Number = 1
2373                 // Forces the map's zoom level to always be a multiple of this, particularly
2374                 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
2375                 // By default, the zoom level snaps to the nearest integer; lower values
2376                 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
2377                 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
2378                 zoomSnap: 1,
2379
2380                 // @option zoomDelta: Number = 1
2381                 // Controls how much the map's zoom level will change after a
2382                 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
2383                 // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
2384                 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
2385                 zoomDelta: 1,
2386
2387                 // @option trackResize: Boolean = true
2388                 // Whether the map automatically handles browser window resize to update itself.
2389                 trackResize: true
2390         },
2391
2392         initialize: function (id, options) { // (HTMLElement or String, Object)
2393                 options = L.setOptions(this, options);
2394
2395                 this._initContainer(id);
2396                 this._initLayout();
2397
2398                 // hack for https://github.com/Leaflet/Leaflet/issues/1980
2399                 this._onResize = L.bind(this._onResize, this);
2400
2401                 this._initEvents();
2402
2403                 if (options.maxBounds) {
2404                         this.setMaxBounds(options.maxBounds);
2405                 }
2406
2407                 if (options.zoom !== undefined) {
2408                         this._zoom = this._limitZoom(options.zoom);
2409                 }
2410
2411                 if (options.center && options.zoom !== undefined) {
2412                         this.setView(L.latLng(options.center), options.zoom, {reset: true});
2413                 }
2414
2415                 this._handlers = [];
2416                 this._layers = {};
2417                 this._zoomBoundLayers = {};
2418                 this._sizeChanged = true;
2419
2420                 this.callInitHooks();
2421
2422                 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
2423                 this._zoomAnimated = L.DomUtil.TRANSITION && L.Browser.any3d && !L.Browser.mobileOpera &&
2424                                 this.options.zoomAnimation;
2425
2426                 // zoom transitions run with the same duration for all layers, so if one of transitionend events
2427                 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
2428                 if (this._zoomAnimated) {
2429                         this._createAnimProxy();
2430                         L.DomEvent.on(this._proxy, L.DomUtil.TRANSITION_END, this._catchTransitionEnd, this);
2431                 }
2432
2433                 this._addLayers(this.options.layers);
2434         },
2435
2436
2437         // @section Methods for modifying map state
2438
2439         // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
2440         // Sets the view of the map (geographical center and zoom) with the given
2441         // animation options.
2442         setView: function (center, zoom, options) {
2443
2444                 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
2445                 center = this._limitCenter(L.latLng(center), zoom, this.options.maxBounds);
2446                 options = options || {};
2447
2448                 this._stop();
2449
2450                 if (this._loaded && !options.reset && options !== true) {
2451
2452                         if (options.animate !== undefined) {
2453                                 options.zoom = L.extend({animate: options.animate}, options.zoom);
2454                                 options.pan = L.extend({animate: options.animate, duration: options.duration}, options.pan);
2455                         }
2456
2457                         // try animating pan or zoom
2458                         var moved = (this._zoom !== zoom) ?
2459                                 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
2460                                 this._tryAnimatedPan(center, options.pan);
2461
2462                         if (moved) {
2463                                 // prevent resize handler call, the view will refresh after animation anyway
2464                                 clearTimeout(this._sizeTimer);
2465                                 return this;
2466                         }
2467                 }
2468
2469                 // animation didn't start, just reset the map view
2470                 this._resetView(center, zoom);
2471
2472                 return this;
2473         },
2474
2475         // @method setZoom(zoom: Number, options: Zoom/pan options): this
2476         // Sets the zoom of the map.
2477         setZoom: function (zoom, options) {
2478                 if (!this._loaded) {
2479                         this._zoom = zoom;
2480                         return this;
2481                 }
2482                 return this.setView(this.getCenter(), zoom, {zoom: options});
2483         },
2484
2485         // @method zoomIn(delta?: Number, options?: Zoom options): this
2486         // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
2487         zoomIn: function (delta, options) {
2488                 delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1);
2489                 return this.setZoom(this._zoom + delta, options);
2490         },
2491
2492         // @method zoomOut(delta?: Number, options?: Zoom options): this
2493         // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
2494         zoomOut: function (delta, options) {
2495                 delta = delta || (L.Browser.any3d ? this.options.zoomDelta : 1);
2496                 return this.setZoom(this._zoom - delta, options);
2497         },
2498
2499         // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
2500         // Zooms the map while keeping a specified geographical point on the map
2501         // stationary (e.g. used internally for scroll zoom and double-click zoom).
2502         // @alternative
2503         // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
2504         // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
2505         setZoomAround: function (latlng, zoom, options) {
2506                 var scale = this.getZoomScale(zoom),
2507                     viewHalf = this.getSize().divideBy(2),
2508                     containerPoint = latlng instanceof L.Point ? latlng : this.latLngToContainerPoint(latlng),
2509
2510                     centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
2511                     newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
2512
2513                 return this.setView(newCenter, zoom, {zoom: options});
2514         },
2515
2516         _getBoundsCenterZoom: function (bounds, options) {
2517
2518                 options = options || {};
2519                 bounds = bounds.getBounds ? bounds.getBounds() : L.latLngBounds(bounds);
2520
2521                 var paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
2522                     paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]),
2523
2524                     zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
2525
2526                 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
2527
2528                 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
2529
2530                     swPoint = this.project(bounds.getSouthWest(), zoom),
2531                     nePoint = this.project(bounds.getNorthEast(), zoom),
2532                     center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
2533
2534                 return {
2535                         center: center,
2536                         zoom: zoom
2537                 };
2538         },
2539
2540         // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
2541         // Sets a map view that contains the given geographical bounds with the
2542         // maximum zoom level possible.
2543         fitBounds: function (bounds, options) {
2544
2545                 bounds = L.latLngBounds(bounds);
2546
2547                 if (!bounds.isValid()) {
2548                         throw new Error('Bounds are not valid.');
2549                 }
2550
2551                 var target = this._getBoundsCenterZoom(bounds, options);
2552                 return this.setView(target.center, target.zoom, options);
2553         },
2554
2555         // @method fitWorld(options?: fitBounds options): this
2556         // Sets a map view that mostly contains the whole world with the maximum
2557         // zoom level possible.
2558         fitWorld: function (options) {
2559                 return this.fitBounds([[-90, -180], [90, 180]], options);
2560         },
2561
2562         // @method panTo(latlng: LatLng, options?: Pan options): this
2563         // Pans the map to a given center.
2564         panTo: function (center, options) { // (LatLng)
2565                 return this.setView(center, this._zoom, {pan: options});
2566         },
2567
2568         // @method panBy(offset: Point): this
2569         // Pans the map by a given number of pixels (animated).
2570         panBy: function (offset, options) {
2571                 offset = L.point(offset).round();
2572                 options = options || {};
2573
2574                 if (!offset.x && !offset.y) {
2575                         return this.fire('moveend');
2576                 }
2577                 // If we pan too far, Chrome gets issues with tiles
2578                 // and makes them disappear or appear in the wrong place (slightly offset) #2602
2579                 if (options.animate !== true && !this.getSize().contains(offset)) {
2580                         this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
2581                         return this;
2582                 }
2583
2584                 if (!this._panAnim) {
2585                         this._panAnim = new L.PosAnimation();
2586
2587                         this._panAnim.on({
2588                                 'step': this._onPanTransitionStep,
2589                                 'end': this._onPanTransitionEnd
2590                         }, this);
2591                 }
2592
2593                 // don't fire movestart if animating inertia
2594                 if (!options.noMoveStart) {
2595                         this.fire('movestart');
2596                 }
2597
2598                 // animate pan unless animate: false specified
2599                 if (options.animate !== false) {
2600                         L.DomUtil.addClass(this._mapPane, 'leaflet-pan-anim');
2601
2602                         var newPos = this._getMapPanePos().subtract(offset).round();
2603                         this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
2604                 } else {
2605                         this._rawPanBy(offset);
2606                         this.fire('move').fire('moveend');
2607                 }
2608
2609                 return this;
2610         },
2611
2612         // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
2613         // Sets the view of the map (geographical center and zoom) performing a smooth
2614         // pan-zoom animation.
2615         flyTo: function (targetCenter, targetZoom, options) {
2616
2617                 options = options || {};
2618                 if (options.animate === false || !L.Browser.any3d) {
2619                         return this.setView(targetCenter, targetZoom, options);
2620                 }
2621
2622                 this._stop();
2623
2624                 var from = this.project(this.getCenter()),
2625                     to = this.project(targetCenter),
2626                     size = this.getSize(),
2627                     startZoom = this._zoom;
2628
2629                 targetCenter = L.latLng(targetCenter);
2630                 targetZoom = targetZoom === undefined ? startZoom : targetZoom;
2631
2632                 var w0 = Math.max(size.x, size.y),
2633                     w1 = w0 * this.getZoomScale(startZoom, targetZoom),
2634                     u1 = (to.distanceTo(from)) || 1,
2635                     rho = 1.42,
2636                     rho2 = rho * rho;
2637
2638                 function r(i) {
2639                         var s1 = i ? -1 : 1,
2640                             s2 = i ? w1 : w0,
2641                             t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
2642                             b1 = 2 * s2 * rho2 * u1,
2643                             b = t1 / b1,
2644                             sq = Math.sqrt(b * b + 1) - b;
2645
2646                             // workaround for floating point precision bug when sq = 0, log = -Infinite,
2647                             // thus triggering an infinite loop in flyTo
2648                             var log = sq < 0.000000001 ? -18 : Math.log(sq);
2649
2650                         return log;
2651                 }
2652
2653                 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
2654                 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
2655                 function tanh(n) { return sinh(n) / cosh(n); }
2656
2657                 var r0 = r(0);
2658
2659                 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
2660                 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
2661
2662                 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
2663
2664                 var start = Date.now(),
2665                     S = (r(1) - r0) / rho,
2666                     duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
2667
2668                 function frame() {
2669                         var t = (Date.now() - start) / duration,
2670                             s = easeOut(t) * S;
2671
2672                         if (t <= 1) {
2673                                 this._flyToFrame = L.Util.requestAnimFrame(frame, this);
2674
2675                                 this._move(
2676                                         this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
2677                                         this.getScaleZoom(w0 / w(s), startZoom),
2678                                         {flyTo: true});
2679
2680                         } else {
2681                                 this
2682                                         ._move(targetCenter, targetZoom)
2683                                         ._moveEnd(true);
2684                         }
2685                 }
2686
2687                 this._moveStart(true);
2688
2689                 frame.call(this);
2690                 return this;
2691         },
2692
2693         // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
2694         // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
2695         // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
2696         flyToBounds: function (bounds, options) {
2697                 var target = this._getBoundsCenterZoom(bounds, options);
2698                 return this.flyTo(target.center, target.zoom, options);
2699         },
2700
2701         // @method setMaxBounds(bounds: Bounds): this
2702         // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
2703         setMaxBounds: function (bounds) {
2704                 bounds = L.latLngBounds(bounds);
2705
2706                 if (!bounds.isValid()) {
2707                         this.options.maxBounds = null;
2708                         return this.off('moveend', this._panInsideMaxBounds);
2709                 } else if (this.options.maxBounds) {
2710                         this.off('moveend', this._panInsideMaxBounds);
2711                 }
2712
2713                 this.options.maxBounds = bounds;
2714
2715                 if (this._loaded) {
2716                         this._panInsideMaxBounds();
2717                 }
2718
2719                 return this.on('moveend', this._panInsideMaxBounds);
2720         },
2721
2722         // @method setMinZoom(zoom: Number): this
2723         // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
2724         setMinZoom: function (zoom) {
2725                 this.options.minZoom = zoom;
2726
2727                 if (this._loaded && this.getZoom() < this.options.minZoom) {
2728                         return this.setZoom(zoom);
2729                 }
2730
2731                 return this;
2732         },
2733
2734         // @method setMaxZoom(zoom: Number): this
2735         // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
2736         setMaxZoom: function (zoom) {
2737                 this.options.maxZoom = zoom;
2738
2739                 if (this._loaded && (this.getZoom() > this.options.maxZoom)) {
2740                         return this.setZoom(zoom);
2741                 }
2742
2743                 return this;
2744         },
2745
2746         // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
2747         // Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any.
2748         panInsideBounds: function (bounds, options) {
2749                 this._enforcingBounds = true;
2750                 var center = this.getCenter(),
2751                     newCenter = this._limitCenter(center, this._zoom, L.latLngBounds(bounds));
2752
2753                 if (!center.equals(newCenter)) {
2754                         this.panTo(newCenter, options);
2755                 }
2756
2757                 this._enforcingBounds = false;
2758                 return this;
2759         },
2760
2761         // @method invalidateSize(options: Zoom/Pan options): this
2762         // Checks if the map container size changed and updates the map if so —
2763         // call it after you've changed the map size dynamically, also animating
2764         // pan by default. If `options.pan` is `false`, panning will not occur.
2765         // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
2766         // that it doesn't happen often even if the method is called many
2767         // times in a row.
2768
2769         // @alternative
2770         // @method invalidateSize(animate: Boolean): this
2771         // Checks if the map container size changed and updates the map if so —
2772         // call it after you've changed the map size dynamically, also animating
2773         // pan by default.
2774         invalidateSize: function (options) {
2775                 if (!this._loaded) { return this; }
2776
2777                 options = L.extend({
2778                         animate: false,
2779                         pan: true
2780                 }, options === true ? {animate: true} : options);
2781
2782                 var oldSize = this.getSize();
2783                 this._sizeChanged = true;
2784                 this._lastCenter = null;
2785
2786                 var newSize = this.getSize(),
2787                     oldCenter = oldSize.divideBy(2).round(),
2788                     newCenter = newSize.divideBy(2).round(),
2789                     offset = oldCenter.subtract(newCenter);
2790
2791                 if (!offset.x && !offset.y) { return this; }
2792
2793                 if (options.animate && options.pan) {
2794                         this.panBy(offset);
2795
2796                 } else {
2797                         if (options.pan) {
2798                                 this._rawPanBy(offset);
2799                         }
2800
2801                         this.fire('move');
2802
2803                         if (options.debounceMoveend) {
2804                                 clearTimeout(this._sizeTimer);
2805                                 this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200);
2806                         } else {
2807                                 this.fire('moveend');
2808                         }
2809                 }
2810
2811                 // @section Map state change events
2812                 // @event resize: ResizeEvent
2813                 // Fired when the map is resized.
2814                 return this.fire('resize', {
2815                         oldSize: oldSize,
2816                         newSize: newSize
2817                 });
2818         },
2819
2820         // @section Methods for modifying map state
2821         // @method stop(): this
2822         // Stops the currently running `panTo` or `flyTo` animation, if any.
2823         stop: function () {
2824                 this.setZoom(this._limitZoom(this._zoom));
2825                 if (!this.options.zoomSnap) {
2826                         this.fire('viewreset');
2827                 }
2828                 return this._stop();
2829         },
2830
2831         // @section Geolocation methods
2832         // @method locate(options?: Locate options): this
2833         // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
2834         // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
2835         // and optionally sets the map view to the user's location with respect to
2836         // detection accuracy (or to the world view if geolocation failed).
2837         // Note that, if your page doesn't use HTTPS, this method will fail in
2838         // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
2839         // See `Locate options` for more details.
2840         locate: function (options) {
2841
2842                 options = this._locateOptions = L.extend({
2843                         timeout: 10000,
2844                         watch: false
2845                         // setView: false
2846                         // maxZoom: <Number>
2847                         // maximumAge: 0
2848                         // enableHighAccuracy: false
2849                 }, options);
2850
2851                 if (!('geolocation' in navigator)) {
2852                         this._handleGeolocationError({
2853                                 code: 0,
2854                                 message: 'Geolocation not supported.'
2855                         });
2856                         return this;
2857                 }
2858
2859                 var onResponse = L.bind(this._handleGeolocationResponse, this),
2860                     onError = L.bind(this._handleGeolocationError, this);
2861
2862                 if (options.watch) {
2863                         this._locationWatchId =
2864                                 navigator.geolocation.watchPosition(onResponse, onError, options);
2865                 } else {
2866                         navigator.geolocation.getCurrentPosition(onResponse, onError, options);
2867                 }
2868                 return this;
2869         },
2870
2871         // @method stopLocate(): this
2872         // Stops watching location previously initiated by `map.locate({watch: true})`
2873         // and aborts resetting the map view if map.locate was called with
2874         // `{setView: true}`.
2875         stopLocate: function () {
2876                 if (navigator.geolocation && navigator.geolocation.clearWatch) {
2877                         navigator.geolocation.clearWatch(this._locationWatchId);
2878                 }
2879                 if (this._locateOptions) {
2880                         this._locateOptions.setView = false;
2881                 }
2882                 return this;
2883         },
2884
2885         _handleGeolocationError: function (error) {
2886                 var c = error.code,
2887                     message = error.message ||
2888                             (c === 1 ? 'permission denied' :
2889                             (c === 2 ? 'position unavailable' : 'timeout'));
2890
2891                 if (this._locateOptions.setView && !this._loaded) {
2892                         this.fitWorld();
2893                 }
2894
2895                 // @section Location events
2896                 // @event locationerror: ErrorEvent
2897                 // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
2898                 this.fire('locationerror', {
2899                         code: c,
2900                         message: 'Geolocation error: ' + message + '.'
2901                 });
2902         },
2903
2904         _handleGeolocationResponse: function (pos) {
2905                 var lat = pos.coords.latitude,
2906                     lng = pos.coords.longitude,
2907                     latlng = new L.LatLng(lat, lng),
2908                     bounds = latlng.toBounds(pos.coords.accuracy),
2909                     options = this._locateOptions;
2910
2911                 if (options.setView) {
2912                         var zoom = this.getBoundsZoom(bounds);
2913                         this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
2914                 }
2915
2916                 var data = {
2917                         latlng: latlng,
2918                         bounds: bounds,
2919                         timestamp: pos.timestamp
2920                 };
2921
2922                 for (var i in pos.coords) {
2923                         if (typeof pos.coords[i] === 'number') {
2924                                 data[i] = pos.coords[i];
2925                         }
2926                 }
2927
2928                 // @event locationfound: LocationEvent
2929                 // Fired when geolocation (using the [`locate`](#map-locate) method)
2930                 // went successfully.
2931                 this.fire('locationfound', data);
2932         },
2933
2934         // TODO handler.addTo
2935         // TODO Appropiate docs section?
2936         // @section Other Methods
2937         // @method addHandler(name: String, HandlerClass: Function): this
2938         // Adds a new `Handler` to the map, given its name and constructor function.
2939         addHandler: function (name, HandlerClass) {
2940                 if (!HandlerClass) { return this; }
2941
2942                 var handler = this[name] = new HandlerClass(this);
2943
2944                 this._handlers.push(handler);
2945
2946                 if (this.options[name]) {
2947                         handler.enable();
2948                 }
2949
2950                 return this;
2951         },
2952
2953         // @method remove(): this
2954         // Destroys the map and clears all related event listeners.
2955         remove: function () {
2956
2957                 this._initEvents(true);
2958
2959                 if (this._containerId !== this._container._leaflet_id) {
2960                         throw new Error('Map container is being reused by another instance');
2961                 }
2962
2963                 try {
2964                         // throws error in IE6-8
2965                         delete this._container._leaflet_id;
2966                         delete this._containerId;
2967                 } catch (e) {
2968                         /*eslint-disable */
2969                         this._container._leaflet_id = undefined;
2970                         /*eslint-enable */
2971                         this._containerId = undefined;
2972                 }
2973
2974                 L.DomUtil.remove(this._mapPane);
2975
2976                 if (this._clearControlPos) {
2977                         this._clearControlPos();
2978                 }
2979
2980                 this._clearHandlers();
2981
2982                 if (this._loaded) {
2983                         // @section Map state change events
2984                         // @event unload: Event
2985                         // Fired when the map is destroyed with [remove](#map-remove) method.
2986                         this.fire('unload');
2987                 }
2988
2989                 for (var i in this._layers) {
2990                         this._layers[i].remove();
2991                 }
2992
2993                 return this;
2994         },
2995
2996         // @section Other Methods
2997         // @method createPane(name: String, container?: HTMLElement): HTMLElement
2998         // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
2999         // then returns it. The pane is created as a children of `container`, or
3000         // as a children of the main map pane if not set.
3001         createPane: function (name, container) {
3002                 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
3003                     pane = L.DomUtil.create('div', className, container || this._mapPane);
3004
3005                 if (name) {
3006                         this._panes[name] = pane;
3007                 }
3008                 return pane;
3009         },
3010
3011         // @section Methods for Getting Map State
3012
3013         // @method getCenter(): LatLng
3014         // Returns the geographical center of the map view
3015         getCenter: function () {
3016                 this._checkIfLoaded();
3017
3018                 if (this._lastCenter && !this._moved()) {
3019                         return this._lastCenter;
3020                 }
3021                 return this.layerPointToLatLng(this._getCenterLayerPoint());
3022         },
3023
3024         // @method getZoom(): Number
3025         // Returns the current zoom level of the map view
3026         getZoom: function () {
3027                 return this._zoom;
3028         },
3029
3030         // @method getBounds(): LatLngBounds
3031         // Returns the geographical bounds visible in the current map view
3032         getBounds: function () {
3033                 var bounds = this.getPixelBounds(),
3034                     sw = this.unproject(bounds.getBottomLeft()),
3035                     ne = this.unproject(bounds.getTopRight());
3036
3037                 return new L.LatLngBounds(sw, ne);
3038         },
3039
3040         // @method getMinZoom(): Number
3041         // Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default.
3042         getMinZoom: function () {
3043                 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
3044         },
3045
3046         // @method getMaxZoom(): Number
3047         // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
3048         getMaxZoom: function () {
3049                 return this.options.maxZoom === undefined ?
3050                         (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
3051                         this.options.maxZoom;
3052         },
3053
3054         // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean): Number
3055         // Returns the maximum zoom level on which the given bounds fit to the map
3056         // view in its entirety. If `inside` (optional) is set to `true`, the method
3057         // instead returns the minimum zoom level on which the map view fits into
3058         // the given bounds in its entirety.
3059         getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
3060                 bounds = L.latLngBounds(bounds);
3061                 padding = L.point(padding || [0, 0]);
3062
3063                 var zoom = this.getZoom() || 0,
3064                     min = this.getMinZoom(),
3065                     max = this.getMaxZoom(),
3066                     nw = bounds.getNorthWest(),
3067                     se = bounds.getSouthEast(),
3068                     size = this.getSize().subtract(padding),
3069                     boundsSize = L.bounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
3070                     snap = L.Browser.any3d ? this.options.zoomSnap : 1;
3071
3072                 var scale = Math.min(size.x / boundsSize.x, size.y / boundsSize.y);
3073                 zoom = this.getScaleZoom(scale, zoom);
3074
3075                 if (snap) {
3076                         zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
3077                         zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
3078                 }
3079
3080                 return Math.max(min, Math.min(max, zoom));
3081         },
3082
3083         // @method getSize(): Point
3084         // Returns the current size of the map container (in pixels).
3085         getSize: function () {
3086                 if (!this._size || this._sizeChanged) {
3087                         this._size = new L.Point(
3088                                 this._container.clientWidth || 0,
3089                                 this._container.clientHeight || 0);
3090
3091                         this._sizeChanged = false;
3092                 }
3093                 return this._size.clone();
3094         },
3095
3096         // @method getPixelBounds(): Bounds
3097         // Returns the bounds of the current map view in projected pixel
3098         // coordinates (sometimes useful in layer and overlay implementations).
3099         getPixelBounds: function (center, zoom) {
3100                 var topLeftPoint = this._getTopLeftPoint(center, zoom);
3101                 return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
3102         },
3103
3104         // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
3105         // the map pane? "left point of the map layer" can be confusing, specially
3106         // since there can be negative offsets.
3107         // @method getPixelOrigin(): Point
3108         // Returns the projected pixel coordinates of the top left point of
3109         // the map layer (useful in custom layer and overlay implementations).
3110         getPixelOrigin: function () {
3111                 this._checkIfLoaded();
3112                 return this._pixelOrigin;
3113         },
3114
3115         // @method getPixelWorldBounds(zoom?: Number): Bounds
3116         // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
3117         // If `zoom` is omitted, the map's current zoom level is used.
3118         getPixelWorldBounds: function (zoom) {
3119                 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
3120         },
3121
3122         // @section Other Methods
3123
3124         // @method getPane(pane: String|HTMLElement): HTMLElement
3125         // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
3126         getPane: function (pane) {
3127                 return typeof pane === 'string' ? this._panes[pane] : pane;
3128         },
3129
3130         // @method getPanes(): Object
3131         // Returns a plain object containing the names of all [panes](#map-pane) as keys and
3132         // the panes as values.
3133         getPanes: function () {
3134                 return this._panes;
3135         },
3136
3137         // @method getContainer: HTMLElement
3138         // Returns the HTML element that contains the map.
3139         getContainer: function () {
3140                 return this._container;
3141         },
3142
3143
3144         // @section Conversion Methods
3145
3146         // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
3147         // Returns the scale factor to be applied to a map transition from zoom level
3148         // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
3149         getZoomScale: function (toZoom, fromZoom) {
3150                 // TODO replace with universal implementation after refactoring projections
3151                 var crs = this.options.crs;
3152                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3153                 return crs.scale(toZoom) / crs.scale(fromZoom);
3154         },
3155
3156         // @method getScaleZoom(scale: Number, fromZoom: Number): Number
3157         // Returns the zoom level that the map would end up at, if it is at `fromZoom`
3158         // level and everything is scaled by a factor of `scale`. Inverse of
3159         // [`getZoomScale`](#map-getZoomScale).
3160         getScaleZoom: function (scale, fromZoom) {
3161                 var crs = this.options.crs;
3162                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3163                 var zoom = crs.zoom(scale * crs.scale(fromZoom));
3164                 return isNaN(zoom) ? Infinity : zoom;
3165         },
3166
3167         // @method project(latlng: LatLng, zoom: Number): Point
3168         // Projects a geographical coordinate `LatLng` according to the projection
3169         // of the map's CRS, then scales it according to `zoom` and the CRS's
3170         // `Transformation`. The result is pixel coordinate relative to
3171         // the CRS origin.
3172         project: function (latlng, zoom) {
3173                 zoom = zoom === undefined ? this._zoom : zoom;
3174                 return this.options.crs.latLngToPoint(L.latLng(latlng), zoom);
3175         },
3176
3177         // @method unproject(point: Point, zoom: Number): LatLng
3178         // Inverse of [`project`](#map-project).
3179         unproject: function (point, zoom) {
3180                 zoom = zoom === undefined ? this._zoom : zoom;
3181                 return this.options.crs.pointToLatLng(L.point(point), zoom);
3182         },
3183
3184         // @method layerPointToLatLng(point: Point): LatLng
3185         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3186         // returns the corresponding geographical coordinate (for the current zoom level).
3187         layerPointToLatLng: function (point) {
3188                 var projectedPoint = L.point(point).add(this.getPixelOrigin());
3189                 return this.unproject(projectedPoint);
3190         },
3191
3192         // @method latLngToLayerPoint(latlng: LatLng): Point
3193         // Given a geographical coordinate, returns the corresponding pixel coordinate
3194         // relative to the [origin pixel](#map-getpixelorigin).
3195         latLngToLayerPoint: function (latlng) {
3196                 var projectedPoint = this.project(L.latLng(latlng))._round();
3197                 return projectedPoint._subtract(this.getPixelOrigin());
3198         },
3199
3200         // @method wrapLatLng(latlng: LatLng): LatLng
3201         // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
3202         // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
3203         // CRS's bounds.
3204         // By default this means longitude is wrapped around the dateline so its
3205         // value is between -180 and +180 degrees.
3206         wrapLatLng: function (latlng) {
3207                 return this.options.crs.wrapLatLng(L.latLng(latlng));
3208         },
3209
3210         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
3211         // Returns a `LatLngBounds` with the same size as the given one, ensuring that
3212         // its center is within the CRS's bounds.
3213         // By default this means the center longitude is wrapped around the dateline so its
3214         // value is between -180 and +180 degrees, and the majority of the bounds
3215         // overlaps the CRS's bounds.
3216         wrapLatLngBounds: function (latlng) {
3217                 return this.options.crs.wrapLatLngBounds(L.latLngBounds(latlng));
3218         },
3219
3220         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
3221         // Returns the distance between two geographical coordinates according to
3222         // the map's CRS. By default this measures distance in meters.
3223         distance: function (latlng1, latlng2) {
3224                 return this.options.crs.distance(L.latLng(latlng1), L.latLng(latlng2));
3225         },
3226
3227         // @method containerPointToLayerPoint(point: Point): Point
3228         // Given a pixel coordinate relative to the map container, returns the corresponding
3229         // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
3230         containerPointToLayerPoint: function (point) { // (Point)
3231                 return L.point(point).subtract(this._getMapPanePos());
3232         },
3233
3234         // @method layerPointToContainerPoint(point: Point): Point
3235         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3236         // returns the corresponding pixel coordinate relative to the map container.
3237         layerPointToContainerPoint: function (point) { // (Point)
3238                 return L.point(point).add(this._getMapPanePos());
3239         },
3240
3241         // @method containerPointToLatLng(point: Point): LatLng
3242         // Given a pixel coordinate relative to the map container, returns
3243         // the corresponding geographical coordinate (for the current zoom level).
3244         containerPointToLatLng: function (point) {
3245                 var layerPoint = this.containerPointToLayerPoint(L.point(point));
3246                 return this.layerPointToLatLng(layerPoint);
3247         },
3248
3249         // @method latLngToContainerPoint(latlng: LatLng): Point
3250         // Given a geographical coordinate, returns the corresponding pixel coordinate
3251         // relative to the map container.
3252         latLngToContainerPoint: function (latlng) {
3253                 return this.layerPointToContainerPoint(this.latLngToLayerPoint(L.latLng(latlng)));
3254         },
3255
3256         // @method mouseEventToContainerPoint(ev: MouseEvent): Point
3257         // Given a MouseEvent object, returns the pixel coordinate relative to the
3258         // map container where the event took place.
3259         mouseEventToContainerPoint: function (e) {
3260                 return L.DomEvent.getMousePosition(e, this._container);
3261         },
3262
3263         // @method mouseEventToLayerPoint(ev: MouseEvent): Point
3264         // Given a MouseEvent object, returns the pixel coordinate relative to
3265         // the [origin pixel](#map-getpixelorigin) where the event took place.
3266         mouseEventToLayerPoint: function (e) {
3267                 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
3268         },
3269
3270         // @method mouseEventToLatLng(ev: MouseEvent): LatLng
3271         // Given a MouseEvent object, returns geographical coordinate where the
3272         // event took place.
3273         mouseEventToLatLng: function (e) { // (MouseEvent)
3274                 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
3275         },
3276
3277
3278         // map initialization methods
3279
3280         _initContainer: function (id) {
3281                 var container = this._container = L.DomUtil.get(id);
3282
3283                 if (!container) {
3284                         throw new Error('Map container not found.');
3285                 } else if (container._leaflet_id) {
3286                         throw new Error('Map container is already initialized.');
3287                 }
3288
3289                 L.DomEvent.addListener(container, 'scroll', this._onScroll, this);
3290                 this._containerId = L.Util.stamp(container);
3291         },
3292
3293         _initLayout: function () {
3294                 var container = this._container;
3295
3296                 this._fadeAnimated = this.options.fadeAnimation && L.Browser.any3d;
3297
3298                 L.DomUtil.addClass(container, 'leaflet-container' +
3299                         (L.Browser.touch ? ' leaflet-touch' : '') +
3300                         (L.Browser.retina ? ' leaflet-retina' : '') +
3301                         (L.Browser.ielt9 ? ' leaflet-oldie' : '') +
3302                         (L.Browser.safari ? ' leaflet-safari' : '') +
3303                         (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
3304
3305                 var position = L.DomUtil.getStyle(container, 'position');
3306
3307                 if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
3308                         container.style.position = 'relative';
3309                 }
3310
3311                 this._initPanes();
3312
3313                 if (this._initControlPos) {
3314                         this._initControlPos();
3315                 }
3316         },
3317
3318         _initPanes: function () {
3319                 var panes = this._panes = {};
3320                 this._paneRenderers = {};
3321
3322                 // @section
3323                 //
3324                 // Panes are DOM elements used to control the ordering of layers on the map. You
3325                 // can access panes with [`map.getPane`](#map-getpane) or
3326                 // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
3327                 // [`map.createPane`](#map-createpane) method.
3328                 //
3329                 // Every map has the following default panes that differ only in zIndex.
3330                 //
3331                 // @pane mapPane: HTMLElement = 'auto'
3332                 // Pane that contains all other map panes
3333
3334                 this._mapPane = this.createPane('mapPane', this._container);
3335                 L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
3336
3337                 // @pane tilePane: HTMLElement = 200
3338                 // Pane for `GridLayer`s and `TileLayer`s
3339                 this.createPane('tilePane');
3340                 // @pane overlayPane: HTMLElement = 400
3341                 // Pane for vector overlays (`Path`s), like `Polyline`s and `Polygon`s
3342                 this.createPane('shadowPane');
3343                 // @pane shadowPane: HTMLElement = 500
3344                 // Pane for overlay shadows (e.g. `Marker` shadows)
3345                 this.createPane('overlayPane');
3346                 // @pane markerPane: HTMLElement = 600
3347                 // Pane for `Icon`s of `Marker`s
3348                 this.createPane('markerPane');
3349                 // @pane tooltipPane: HTMLElement = 650
3350                 // Pane for tooltip.
3351                 this.createPane('tooltipPane');
3352                 // @pane popupPane: HTMLElement = 700
3353                 // Pane for `Popup`s.
3354                 this.createPane('popupPane');
3355
3356                 if (!this.options.markerZoomAnimation) {
3357                         L.DomUtil.addClass(panes.markerPane, 'leaflet-zoom-hide');
3358                         L.DomUtil.addClass(panes.shadowPane, 'leaflet-zoom-hide');
3359                 }
3360         },
3361
3362
3363         // private methods that modify map state
3364
3365         // @section Map state change events
3366         _resetView: function (center, zoom) {
3367                 L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
3368
3369                 var loading = !this._loaded;
3370                 this._loaded = true;
3371                 zoom = this._limitZoom(zoom);
3372
3373                 this.fire('viewprereset');
3374
3375                 var zoomChanged = this._zoom !== zoom;
3376                 this
3377                         ._moveStart(zoomChanged)
3378                         ._move(center, zoom)
3379                         ._moveEnd(zoomChanged);
3380
3381                 // @event viewreset: Event
3382                 // Fired when the map needs to redraw its content (this usually happens
3383                 // on map zoom or load). Very useful for creating custom overlays.
3384                 this.fire('viewreset');
3385
3386                 // @event load: Event
3387                 // Fired when the map is initialized (when its center and zoom are set
3388                 // for the first time).
3389                 if (loading) {
3390                         this.fire('load');
3391                 }
3392         },
3393
3394         _moveStart: function (zoomChanged) {
3395                 // @event zoomstart: Event
3396                 // Fired when the map zoom is about to change (e.g. before zoom animation).
3397                 // @event movestart: Event
3398                 // Fired when the view of the map starts changing (e.g. user starts dragging the map).
3399                 if (zoomChanged) {
3400                         this.fire('zoomstart');
3401                 }
3402                 return this.fire('movestart');
3403         },
3404
3405         _move: function (center, zoom, data) {
3406                 if (zoom === undefined) {
3407                         zoom = this._zoom;
3408                 }
3409                 var zoomChanged = this._zoom !== zoom;
3410
3411                 this._zoom = zoom;
3412                 this._lastCenter = center;
3413                 this._pixelOrigin = this._getNewPixelOrigin(center);
3414
3415                 // @event zoom: Event
3416                 // Fired repeatedly during any change in zoom level, including zoom
3417                 // and fly animations.
3418                 if (zoomChanged || (data && data.pinch)) {      // Always fire 'zoom' if pinching because #3530
3419                         this.fire('zoom', data);
3420                 }
3421
3422                 // @event move: Event
3423                 // Fired repeatedly during any movement of the map, including pan and
3424                 // fly animations.
3425                 return this.fire('move', data);
3426         },
3427
3428         _moveEnd: function (zoomChanged) {
3429                 // @event zoomend: Event
3430                 // Fired when the map has changed, after any animations.
3431                 if (zoomChanged) {
3432                         this.fire('zoomend');
3433                 }
3434
3435                 // @event moveend: Event
3436                 // Fired when the center of the map stops changing (e.g. user stopped
3437                 // dragging the map).
3438                 return this.fire('moveend');
3439         },
3440
3441         _stop: function () {
3442                 L.Util.cancelAnimFrame(this._flyToFrame);
3443                 if (this._panAnim) {
3444                         this._panAnim.stop();
3445                 }
3446                 return this;
3447         },
3448
3449         _rawPanBy: function (offset) {
3450                 L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
3451         },
3452
3453         _getZoomSpan: function () {
3454                 return this.getMaxZoom() - this.getMinZoom();
3455         },
3456
3457         _panInsideMaxBounds: function () {
3458                 if (!this._enforcingBounds) {
3459                         this.panInsideBounds(this.options.maxBounds);
3460                 }
3461         },
3462
3463         _checkIfLoaded: function () {
3464                 if (!this._loaded) {
3465                         throw new Error('Set map center and zoom first.');
3466                 }
3467         },
3468
3469         // DOM event handling
3470
3471         // @section Interaction events
3472         _initEvents: function (remove) {
3473                 if (!L.DomEvent) { return; }
3474
3475                 this._targets = {};
3476                 this._targets[L.stamp(this._container)] = this;
3477
3478                 var onOff = remove ? 'off' : 'on';
3479
3480                 // @event click: MouseEvent
3481                 // Fired when the user clicks (or taps) the map.
3482                 // @event dblclick: MouseEvent
3483                 // Fired when the user double-clicks (or double-taps) the map.
3484                 // @event mousedown: MouseEvent
3485                 // Fired when the user pushes the mouse button on the map.
3486                 // @event mouseup: MouseEvent
3487                 // Fired when the user releases the mouse button on the map.
3488                 // @event mouseover: MouseEvent
3489                 // Fired when the mouse enters the map.
3490                 // @event mouseout: MouseEvent
3491                 // Fired when the mouse leaves the map.
3492                 // @event mousemove: MouseEvent
3493                 // Fired while the mouse moves over the map.
3494                 // @event contextmenu: MouseEvent
3495                 // Fired when the user pushes the right mouse button on the map, prevents
3496                 // default browser context menu from showing if there are listeners on
3497                 // this event. Also fired on mobile when the user holds a single touch
3498                 // for a second (also called long press).
3499                 // @event keypress: KeyboardEvent
3500                 // Fired when the user presses a key from the keyboard while the map is focused.
3501                 L.DomEvent[onOff](this._container, 'click dblclick mousedown mouseup ' +
3502                         'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
3503
3504                 if (this.options.trackResize) {
3505                         L.DomEvent[onOff](window, 'resize', this._onResize, this);
3506                 }
3507
3508                 if (L.Browser.any3d && this.options.transform3DLimit) {
3509                         this[onOff]('moveend', this._onMoveEnd);
3510                 }
3511         },
3512
3513         _onResize: function () {
3514                 L.Util.cancelAnimFrame(this._resizeRequest);
3515                 this._resizeRequest = L.Util.requestAnimFrame(
3516                         function () { this.invalidateSize({debounceMoveend: true}); }, this);
3517         },
3518
3519         _onScroll: function () {
3520                 this._container.scrollTop  = 0;
3521                 this._container.scrollLeft = 0;
3522         },
3523
3524         _onMoveEnd: function () {
3525                 var pos = this._getMapPanePos();
3526                 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
3527                         // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
3528                         // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
3529                         this._resetView(this.getCenter(), this.getZoom());
3530                 }
3531         },
3532
3533         _findEventTargets: function (e, type) {
3534                 var targets = [],
3535                     target,
3536                     isHover = type === 'mouseout' || type === 'mouseover',
3537                     src = e.target || e.srcElement,
3538                     dragging = false;
3539
3540                 while (src) {
3541                         target = this._targets[L.stamp(src)];
3542                         if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
3543                                 // Prevent firing click after you just dragged an object.
3544                                 dragging = true;
3545                                 break;
3546                         }
3547                         if (target && target.listens(type, true)) {
3548                                 if (isHover && !L.DomEvent._isExternalTarget(src, e)) { break; }
3549                                 targets.push(target);
3550                                 if (isHover) { break; }
3551                         }
3552                         if (src === this._container) { break; }
3553                         src = src.parentNode;
3554                 }
3555                 if (!targets.length && !dragging && !isHover && L.DomEvent._isExternalTarget(src, e)) {
3556                         targets = [this];
3557                 }
3558                 return targets;
3559         },
3560
3561         _handleDOMEvent: function (e) {
3562                 if (!this._loaded || L.DomEvent._skipped(e)) { return; }
3563
3564                 var type = e.type === 'keypress' && e.keyCode === 13 ? 'click' : e.type;
3565
3566                 if (type === 'mousedown') {
3567                         // prevents outline when clicking on keyboard-focusable element
3568                         L.DomUtil.preventOutline(e.target || e.srcElement);
3569                 }
3570
3571                 this._fireDOMEvent(e, type);
3572         },
3573
3574         _fireDOMEvent: function (e, type, targets) {
3575
3576                 if (e.type === 'click') {
3577                         // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
3578                         // @event preclick: MouseEvent
3579                         // Fired before mouse click on the map (sometimes useful when you
3580                         // want something to happen on click before any existing click
3581                         // handlers start running).
3582                         var synth = L.Util.extend({}, e);
3583                         synth.type = 'preclick';
3584                         this._fireDOMEvent(synth, synth.type, targets);
3585                 }
3586
3587                 if (e._stopped) { return; }
3588
3589                 // Find the layer the event is propagating from and its parents.
3590                 targets = (targets || []).concat(this._findEventTargets(e, type));
3591
3592                 if (!targets.length) { return; }
3593
3594                 var target = targets[0];
3595                 if (type === 'contextmenu' && target.listens(type, true)) {
3596                         L.DomEvent.preventDefault(e);
3597                 }
3598
3599                 var data = {
3600                         originalEvent: e
3601                 };
3602
3603                 if (e.type !== 'keypress') {
3604                         var isMarker = target instanceof L.Marker;
3605                         data.containerPoint = isMarker ?
3606                                         this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
3607                         data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
3608                         data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
3609                 }
3610
3611                 for (var i = 0; i < targets.length; i++) {
3612                         targets[i].fire(type, data, true);
3613                         if (data.originalEvent._stopped ||
3614                                 (targets[i].options.nonBubblingEvents && L.Util.indexOf(targets[i].options.nonBubblingEvents, type) !== -1)) { return; }
3615                 }
3616         },
3617
3618         _draggableMoved: function (obj) {
3619                 obj = obj.dragging && obj.dragging.enabled() ? obj : this;
3620                 return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
3621         },
3622
3623         _clearHandlers: function () {
3624                 for (var i = 0, len = this._handlers.length; i < len; i++) {
3625                         this._handlers[i].disable();
3626                 }
3627         },
3628
3629         // @section Other Methods
3630
3631         // @method whenReady(fn: Function, context?: Object): this
3632         // Runs the given function `fn` when the map gets initialized with
3633         // a view (center and zoom) and at least one layer, or immediately
3634         // if it's already initialized, optionally passing a function context.
3635         whenReady: function (callback, context) {
3636                 if (this._loaded) {
3637                         callback.call(context || this, {target: this});
3638                 } else {
3639                         this.on('load', callback, context);
3640                 }
3641                 return this;
3642         },
3643
3644
3645         // private methods for getting map state
3646
3647         _getMapPanePos: function () {
3648                 return L.DomUtil.getPosition(this._mapPane) || new L.Point(0, 0);
3649         },
3650
3651         _moved: function () {
3652                 var pos = this._getMapPanePos();
3653                 return pos && !pos.equals([0, 0]);
3654         },
3655
3656         _getTopLeftPoint: function (center, zoom) {
3657                 var pixelOrigin = center && zoom !== undefined ?
3658                         this._getNewPixelOrigin(center, zoom) :
3659                         this.getPixelOrigin();
3660                 return pixelOrigin.subtract(this._getMapPanePos());
3661         },
3662
3663         _getNewPixelOrigin: function (center, zoom) {
3664                 var viewHalf = this.getSize()._divideBy(2);
3665                 return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
3666         },
3667
3668         _latLngToNewLayerPoint: function (latlng, zoom, center) {
3669                 var topLeft = this._getNewPixelOrigin(center, zoom);
3670                 return this.project(latlng, zoom)._subtract(topLeft);
3671         },
3672
3673         _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
3674                 var topLeft = this._getNewPixelOrigin(center, zoom);
3675                 return L.bounds([
3676                         this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
3677                         this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
3678                         this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
3679                         this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
3680                 ]);
3681         },
3682
3683         // layer point of the current center
3684         _getCenterLayerPoint: function () {
3685                 return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
3686         },
3687
3688         // offset of the specified place to the current center in pixels
3689         _getCenterOffset: function (latlng) {
3690                 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
3691         },
3692
3693         // adjust center for view to get inside bounds
3694         _limitCenter: function (center, zoom, bounds) {
3695
3696                 if (!bounds) { return center; }
3697
3698                 var centerPoint = this.project(center, zoom),
3699                     viewHalf = this.getSize().divideBy(2),
3700                     viewBounds = new L.Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
3701                     offset = this._getBoundsOffset(viewBounds, bounds, zoom);
3702
3703                 // If offset is less than a pixel, ignore.
3704                 // This prevents unstable projections from getting into
3705                 // an infinite loop of tiny offsets.
3706                 if (offset.round().equals([0, 0])) {
3707                         return center;
3708                 }
3709
3710                 return this.unproject(centerPoint.add(offset), zoom);
3711         },
3712
3713         // adjust offset for view to get inside bounds
3714         _limitOffset: function (offset, bounds) {
3715                 if (!bounds) { return offset; }
3716
3717                 var viewBounds = this.getPixelBounds(),
3718                     newBounds = new L.Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
3719
3720                 return offset.add(this._getBoundsOffset(newBounds, bounds));
3721         },
3722
3723         // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
3724         _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
3725                 var projectedMaxBounds = L.bounds(
3726                         this.project(maxBounds.getNorthEast(), zoom),
3727                         this.project(maxBounds.getSouthWest(), zoom)
3728                     ),
3729                     minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
3730                     maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
3731
3732                     dx = this._rebound(minOffset.x, -maxOffset.x),
3733                     dy = this._rebound(minOffset.y, -maxOffset.y);
3734
3735                 return new L.Point(dx, dy);
3736         },
3737
3738         _rebound: function (left, right) {
3739                 return left + right > 0 ?
3740                         Math.round(left - right) / 2 :
3741                         Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
3742         },
3743
3744         _limitZoom: function (zoom) {
3745                 var min = this.getMinZoom(),
3746                     max = this.getMaxZoom(),
3747                     snap = L.Browser.any3d ? this.options.zoomSnap : 1;
3748                 if (snap) {
3749                         zoom = Math.round(zoom / snap) * snap;
3750                 }
3751                 return Math.max(min, Math.min(max, zoom));
3752         },
3753
3754         _onPanTransitionStep: function () {
3755                 this.fire('move');
3756         },
3757
3758         _onPanTransitionEnd: function () {
3759                 L.DomUtil.removeClass(this._mapPane, 'leaflet-pan-anim');
3760                 this.fire('moveend');
3761         },
3762
3763         _tryAnimatedPan: function (center, options) {
3764                 // difference between the new and current centers in pixels
3765                 var offset = this._getCenterOffset(center)._floor();
3766
3767                 // don't animate too far unless animate: true specified in options
3768                 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
3769
3770                 this.panBy(offset, options);
3771
3772                 return true;
3773         },
3774
3775         _createAnimProxy: function () {
3776
3777                 var proxy = this._proxy = L.DomUtil.create('div', 'leaflet-proxy leaflet-zoom-animated');
3778                 this._panes.mapPane.appendChild(proxy);
3779
3780                 this.on('zoomanim', function (e) {
3781                         var prop = L.DomUtil.TRANSFORM,
3782                             transform = proxy.style[prop];
3783
3784                         L.DomUtil.setTransform(proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
3785
3786                         // workaround for case when transform is the same and so transitionend event is not fired
3787                         if (transform === proxy.style[prop] && this._animatingZoom) {
3788                                 this._onZoomTransitionEnd();
3789                         }
3790                 }, this);
3791
3792                 this.on('load moveend', function () {
3793                         var c = this.getCenter(),
3794                             z = this.getZoom();
3795                         L.DomUtil.setTransform(proxy, this.project(c, z), this.getZoomScale(z, 1));
3796                 }, this);
3797         },
3798
3799         _catchTransitionEnd: function (e) {
3800                 if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
3801                         this._onZoomTransitionEnd();
3802                 }
3803         },
3804
3805         _nothingToAnimate: function () {
3806                 return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
3807         },
3808
3809         _tryAnimatedZoom: function (center, zoom, options) {
3810
3811                 if (this._animatingZoom) { return true; }
3812
3813                 options = options || {};
3814
3815                 // don't animate if disabled, not supported or zoom difference is too large
3816                 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
3817                         Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
3818