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