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