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