]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/leaflet/leaflet.js
Update leaflet to 1.3.3
[rails.git] / vendor / assets / leaflet / leaflet.js
1 /* @preserve
2  * Leaflet 1.3.3, a JS library for interactive maps. http://leafletjs.com
3  * (c) 2010-2018 Vladimir Agafonkin, (c) 2010-2011 CloudMade
4  */
5
6 (function (global, factory) {
7         typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
8         typeof define === 'function' && define.amd ? define(['exports'], factory) :
9         (factory((global.L = {})));
10 }(this, (function (exports) { 'use strict';
11
12 var version = "1.3.3";
13
14 /*
15  * @namespace Util
16  *
17  * Various utility functions, used by Leaflet internally.
18  */
19
20 var freeze = Object.freeze;
21 Object.freeze = function (obj) { return obj; };
22
23 // @function extend(dest: Object, src?: Object): Object
24 // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
25 function extend(dest) {
26         var i, j, len, src;
27
28         for (j = 1, len = arguments.length; j < len; j++) {
29                 src = arguments[j];
30                 for (i in src) {
31                         dest[i] = src[i];
32                 }
33         }
34         return dest;
35 }
36
37 // @function create(proto: Object, properties?: Object): Object
38 // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
39 var create = Object.create || (function () {
40         function F() {}
41         return function (proto) {
42                 F.prototype = proto;
43                 return new F();
44         };
45 })();
46
47 // @function bind(fn: Function, …): Function
48 // 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).
49 // Has a `L.bind()` shortcut.
50 function bind(fn, obj) {
51         var slice = Array.prototype.slice;
52
53         if (fn.bind) {
54                 return fn.bind.apply(fn, slice.call(arguments, 1));
55         }
56
57         var args = slice.call(arguments, 2);
58
59         return function () {
60                 return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
61         };
62 }
63
64 // @property lastId: Number
65 // Last unique ID used by [`stamp()`](#util-stamp)
66 var lastId = 0;
67
68 // @function stamp(obj: Object): Number
69 // Returns the unique ID of an object, assigning it one if it doesn't have it.
70 function stamp(obj) {
71         /*eslint-disable */
72         obj._leaflet_id = obj._leaflet_id || ++lastId;
73         return obj._leaflet_id;
74         /* eslint-enable */
75 }
76
77 // @function throttle(fn: Function, time: Number, context: Object): Function
78 // Returns a function which executes function `fn` with the given scope `context`
79 // (so that the `this` keyword refers to `context` inside `fn`'s code). The function
80 // `fn` will be called no more than one time per given amount of `time`. The arguments
81 // received by the bound function will be any arguments passed when binding the
82 // function, followed by any arguments passed when invoking the bound function.
83 // Has an `L.throttle` shortcut.
84 function throttle(fn, time, context) {
85         var lock, args, wrapperFn, later;
86
87         later = function () {
88                 // reset lock and call if queued
89                 lock = false;
90                 if (args) {
91                         wrapperFn.apply(context, args);
92                         args = false;
93                 }
94         };
95
96         wrapperFn = function () {
97                 if (lock) {
98                         // called too soon, queue to call later
99                         args = arguments;
100
101                 } else {
102                         // call and lock until later
103                         fn.apply(context, arguments);
104                         setTimeout(later, time);
105                         lock = true;
106                 }
107         };
108
109         return wrapperFn;
110 }
111
112 // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
113 // Returns the number `num` modulo `range` in such a way so it lies within
114 // `range[0]` and `range[1]`. The returned value will be always smaller than
115 // `range[1]` unless `includeMax` is set to `true`.
116 function wrapNum(x, range, includeMax) {
117         var max = range[1],
118             min = range[0],
119             d = max - min;
120         return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
121 }
122
123 // @function falseFn(): Function
124 // Returns a function which always returns `false`.
125 function falseFn() { return false; }
126
127 // @function formatNum(num: Number, digits?: Number): Number
128 // Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default.
129 function formatNum(num, digits) {
130         var pow = Math.pow(10, (digits === undefined ? 6 : digits));
131         return Math.round(num * pow) / pow;
132 }
133
134 // @function trim(str: String): String
135 // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
136 function trim(str) {
137         return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
138 }
139
140 // @function splitWords(str: String): String[]
141 // Trims and splits the string on whitespace and returns the array of parts.
142 function splitWords(str) {
143         return trim(str).split(/\s+/);
144 }
145
146 // @function setOptions(obj: Object, options: Object): Object
147 // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
148 function setOptions(obj, options) {
149         if (!obj.hasOwnProperty('options')) {
150                 obj.options = obj.options ? create(obj.options) : {};
151         }
152         for (var i in options) {
153                 obj.options[i] = options[i];
154         }
155         return obj.options;
156 }
157
158 // @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
159 // Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
160 // translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
161 // be appended at the end. If `uppercase` is `true`, the parameter names will
162 // be uppercased (e.g. `'?A=foo&B=bar'`)
163 function getParamString(obj, existingUrl, uppercase) {
164         var params = [];
165         for (var i in obj) {
166                 params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
167         }
168         return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
169 }
170
171 var templateRe = /\{ *([\w_-]+) *\}/g;
172
173 // @function template(str: String, data: Object): String
174 // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
175 // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
176 // `('Hello foo, bar')`. You can also specify functions instead of strings for
177 // data values — they will be evaluated passing `data` as an argument.
178 function template(str, data) {
179         return str.replace(templateRe, function (str, key) {
180                 var value = data[key];
181
182                 if (value === undefined) {
183                         throw new Error('No value provided for variable ' + str);
184
185                 } else if (typeof value === 'function') {
186                         value = value(data);
187                 }
188                 return value;
189         });
190 }
191
192 // @function isArray(obj): Boolean
193 // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
194 var isArray = Array.isArray || function (obj) {
195         return (Object.prototype.toString.call(obj) === '[object Array]');
196 };
197
198 // @function indexOf(array: Array, el: Object): Number
199 // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
200 function indexOf(array, el) {
201         for (var i = 0; i < array.length; i++) {
202                 if (array[i] === el) { return i; }
203         }
204         return -1;
205 }
206
207 // @property emptyImageUrl: String
208 // Data URI string containing a base64-encoded empty GIF image.
209 // Used as a hack to free memory from unused images on WebKit-powered
210 // mobile devices (by setting image `src` to this string).
211 var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
212
213 // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
214
215 function getPrefixed(name) {
216         return window['webkit' + name] || window['moz' + name] || window['ms' + name];
217 }
218
219 var lastTime = 0;
220
221 // fallback for IE 7-8
222 function timeoutDefer(fn) {
223         var time = +new Date(),
224             timeToCall = Math.max(0, 16 - (time - lastTime));
225
226         lastTime = time + timeToCall;
227         return window.setTimeout(fn, timeToCall);
228 }
229
230 var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer;
231 var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
232                 getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
233
234 // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
235 // Schedules `fn` to be executed when the browser repaints. `fn` is bound to
236 // `context` if given. When `immediate` is set, `fn` is called immediately if
237 // the browser doesn't have native support for
238 // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
239 // otherwise it's delayed. Returns a request ID that can be used to cancel the request.
240 function requestAnimFrame(fn, context, immediate) {
241         if (immediate && requestFn === timeoutDefer) {
242                 fn.call(context);
243         } else {
244                 return requestFn.call(window, bind(fn, context));
245         }
246 }
247
248 // @function cancelAnimFrame(id: Number): undefined
249 // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
250 function cancelAnimFrame(id) {
251         if (id) {
252                 cancelFn.call(window, id);
253         }
254 }
255
256
257 var Util = (Object.freeze || Object)({
258         freeze: freeze,
259         extend: extend,
260         create: create,
261         bind: bind,
262         lastId: lastId,
263         stamp: stamp,
264         throttle: throttle,
265         wrapNum: wrapNum,
266         falseFn: falseFn,
267         formatNum: formatNum,
268         trim: trim,
269         splitWords: splitWords,
270         setOptions: setOptions,
271         getParamString: getParamString,
272         template: template,
273         isArray: isArray,
274         indexOf: indexOf,
275         emptyImageUrl: emptyImageUrl,
276         requestFn: requestFn,
277         cancelFn: cancelFn,
278         requestAnimFrame: requestAnimFrame,
279         cancelAnimFrame: cancelAnimFrame
280 });
281
282 // @class Class
283 // @aka L.Class
284
285 // @section
286 // @uninheritable
287
288 // Thanks to John Resig and Dean Edwards for inspiration!
289
290 function Class() {}
291
292 Class.extend = function (props) {
293
294         // @function extend(props: Object): Function
295         // [Extends the current class](#class-inheritance) given the properties to be included.
296         // Returns a Javascript function that is a class constructor (to be called with `new`).
297         var NewClass = function () {
298
299                 // call the constructor
300                 if (this.initialize) {
301                         this.initialize.apply(this, arguments);
302                 }
303
304                 // call all constructor hooks
305                 this.callInitHooks();
306         };
307
308         var parentProto = NewClass.__super__ = this.prototype;
309
310         var proto = create(parentProto);
311         proto.constructor = NewClass;
312
313         NewClass.prototype = proto;
314
315         // inherit parent's statics
316         for (var i in this) {
317                 if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') {
318                         NewClass[i] = this[i];
319                 }
320         }
321
322         // mix static properties into the class
323         if (props.statics) {
324                 extend(NewClass, props.statics);
325                 delete props.statics;
326         }
327
328         // mix includes into the prototype
329         if (props.includes) {
330                 checkDeprecatedMixinEvents(props.includes);
331                 extend.apply(null, [proto].concat(props.includes));
332                 delete props.includes;
333         }
334
335         // merge options
336         if (proto.options) {
337                 props.options = extend(create(proto.options), props.options);
338         }
339
340         // mix given properties into the prototype
341         extend(proto, props);
342
343         proto._initHooks = [];
344
345         // add method for calling all hooks
346         proto.callInitHooks = function () {
347
348                 if (this._initHooksCalled) { return; }
349
350                 if (parentProto.callInitHooks) {
351                         parentProto.callInitHooks.call(this);
352                 }
353
354                 this._initHooksCalled = true;
355
356                 for (var i = 0, len = proto._initHooks.length; i < len; i++) {
357                         proto._initHooks[i].call(this);
358                 }
359         };
360
361         return NewClass;
362 };
363
364
365 // @function include(properties: Object): this
366 // [Includes a mixin](#class-includes) into the current class.
367 Class.include = function (props) {
368         extend(this.prototype, props);
369         return this;
370 };
371
372 // @function mergeOptions(options: Object): this
373 // [Merges `options`](#class-options) into the defaults of the class.
374 Class.mergeOptions = function (options) {
375         extend(this.prototype.options, options);
376         return this;
377 };
378
379 // @function addInitHook(fn: Function): this
380 // Adds a [constructor hook](#class-constructor-hooks) to the class.
381 Class.addInitHook = function (fn) { // (Function) || (String, args...)
382         var args = Array.prototype.slice.call(arguments, 1);
383
384         var init = typeof fn === 'function' ? fn : function () {
385                 this[fn].apply(this, args);
386         };
387
388         this.prototype._initHooks = this.prototype._initHooks || [];
389         this.prototype._initHooks.push(init);
390         return this;
391 };
392
393 function checkDeprecatedMixinEvents(includes) {
394         if (typeof L === 'undefined' || !L || !L.Mixin) { return; }
395
396         includes = isArray(includes) ? includes : [includes];
397
398         for (var i = 0; i < includes.length; i++) {
399                 if (includes[i] === L.Mixin.Events) {
400                         console.warn('Deprecated include of L.Mixin.Events: ' +
401                                 'this property will be removed in future releases, ' +
402                                 'please inherit from L.Evented instead.', new Error().stack);
403                 }
404         }
405 }
406
407 /*
408  * @class Evented
409  * @aka L.Evented
410  * @inherits Class
411  *
412  * A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event).
413  *
414  * @example
415  *
416  * ```js
417  * map.on('click', function(e) {
418  *      alert(e.latlng);
419  * } );
420  * ```
421  *
422  * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
423  *
424  * ```js
425  * function onClick(e) { ... }
426  *
427  * map.on('click', onClick);
428  * map.off('click', onClick);
429  * ```
430  */
431
432 var Events = {
433         /* @method on(type: String, fn: Function, context?: Object): this
434          * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`).
435          *
436          * @alternative
437          * @method on(eventMap: Object): this
438          * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
439          */
440         on: function (types, fn, context) {
441
442                 // types can be a map of types/handlers
443                 if (typeof types === 'object') {
444                         for (var type in types) {
445                                 // we don't process space-separated events here for performance;
446                                 // it's a hot path since Layer uses the on(obj) syntax
447                                 this._on(type, types[type], fn);
448                         }
449
450                 } else {
451                         // types can be a string of space-separated words
452                         types = splitWords(types);
453
454                         for (var i = 0, len = types.length; i < len; i++) {
455                                 this._on(types[i], fn, context);
456                         }
457                 }
458
459                 return this;
460         },
461
462         /* @method off(type: String, fn?: Function, context?: Object): this
463          * Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener.
464          *
465          * @alternative
466          * @method off(eventMap: Object): this
467          * Removes a set of type/listener pairs.
468          *
469          * @alternative
470          * @method off: this
471          * Removes all listeners to all events on the object.
472          */
473         off: function (types, fn, context) {
474
475                 if (!types) {
476                         // clear all listeners if called without arguments
477                         delete this._events;
478
479                 } else if (typeof types === 'object') {
480                         for (var type in types) {
481                                 this._off(type, types[type], fn);
482                         }
483
484                 } else {
485                         types = splitWords(types);
486
487                         for (var i = 0, len = types.length; i < len; i++) {
488                                 this._off(types[i], fn, context);
489                         }
490                 }
491
492                 return this;
493         },
494
495         // attach listener (without syntactic sugar now)
496         _on: function (type, fn, context) {
497                 this._events = this._events || {};
498
499                 /* get/init listeners for type */
500                 var typeListeners = this._events[type];
501                 if (!typeListeners) {
502                         typeListeners = [];
503                         this._events[type] = typeListeners;
504                 }
505
506                 if (context === this) {
507                         // Less memory footprint.
508                         context = undefined;
509                 }
510                 var newListener = {fn: fn, ctx: context},
511                     listeners = typeListeners;
512
513                 // check if fn already there
514                 for (var i = 0, len = listeners.length; i < len; i++) {
515                         if (listeners[i].fn === fn && listeners[i].ctx === context) {
516                                 return;
517                         }
518                 }
519
520                 listeners.push(newListener);
521         },
522
523         _off: function (type, fn, context) {
524                 var listeners,
525                     i,
526                     len;
527
528                 if (!this._events) { return; }
529
530                 listeners = this._events[type];
531
532                 if (!listeners) {
533                         return;
534                 }
535
536                 if (!fn) {
537                         // Set all removed listeners to noop so they are not called if remove happens in fire
538                         for (i = 0, len = listeners.length; i < len; i++) {
539                                 listeners[i].fn = falseFn;
540                         }
541                         // clear all listeners for a type if function isn't specified
542                         delete this._events[type];
543                         return;
544                 }
545
546                 if (context === this) {
547                         context = undefined;
548                 }
549
550                 if (listeners) {
551
552                         // find fn and remove it
553                         for (i = 0, len = listeners.length; i < len; i++) {
554                                 var l = listeners[i];
555                                 if (l.ctx !== context) { continue; }
556                                 if (l.fn === fn) {
557
558                                         // set the removed listener to noop so that's not called if remove happens in fire
559                                         l.fn = falseFn;
560
561                                         if (this._firingCount) {
562                                                 /* copy array in case events are being fired */
563                                                 this._events[type] = listeners = listeners.slice();
564                                         }
565                                         listeners.splice(i, 1);
566
567                                         return;
568                                 }
569                         }
570                 }
571         },
572
573         // @method fire(type: String, data?: Object, propagate?: Boolean): this
574         // Fires an event of the specified type. You can optionally provide an data
575         // object — the first argument of the listener function will contain its
576         // properties. The event can optionally be propagated to event parents.
577         fire: function (type, data, propagate) {
578                 if (!this.listens(type, propagate)) { return this; }
579
580                 var event = extend({}, data, {
581                         type: type,
582                         target: this,
583                         sourceTarget: data && data.sourceTarget || this
584                 });
585
586                 if (this._events) {
587                         var listeners = this._events[type];
588
589                         if (listeners) {
590                                 this._firingCount = (this._firingCount + 1) || 1;
591                                 for (var i = 0, len = listeners.length; i < len; i++) {
592                                         var l = listeners[i];
593                                         l.fn.call(l.ctx || this, event);
594                                 }
595
596                                 this._firingCount--;
597                         }
598                 }
599
600                 if (propagate) {
601                         // propagate the event to parents (set with addEventParent)
602                         this._propagateEvent(event);
603                 }
604
605                 return this;
606         },
607
608         // @method listens(type: String): Boolean
609         // Returns `true` if a particular event type has any listeners attached to it.
610         listens: function (type, propagate) {
611                 var listeners = this._events && this._events[type];
612                 if (listeners && listeners.length) { return true; }
613
614                 if (propagate) {
615                         // also check parents for listeners if event propagates
616                         for (var id in this._eventParents) {
617                                 if (this._eventParents[id].listens(type, propagate)) { return true; }
618                         }
619                 }
620                 return false;
621         },
622
623         // @method once(…): this
624         // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
625         once: function (types, fn, context) {
626
627                 if (typeof types === 'object') {
628                         for (var type in types) {
629                                 this.once(type, types[type], fn);
630                         }
631                         return this;
632                 }
633
634                 var handler = bind(function () {
635                         this
636                             .off(types, fn, context)
637                             .off(types, handler, context);
638                 }, this);
639
640                 // add a listener that's executed once and removed after that
641                 return this
642                     .on(types, fn, context)
643                     .on(types, handler, context);
644         },
645
646         // @method addEventParent(obj: Evented): this
647         // Adds an event parent - an `Evented` that will receive propagated events
648         addEventParent: function (obj) {
649                 this._eventParents = this._eventParents || {};
650                 this._eventParents[stamp(obj)] = obj;
651                 return this;
652         },
653
654         // @method removeEventParent(obj: Evented): this
655         // Removes an event parent, so it will stop receiving propagated events
656         removeEventParent: function (obj) {
657                 if (this._eventParents) {
658                         delete this._eventParents[stamp(obj)];
659                 }
660                 return this;
661         },
662
663         _propagateEvent: function (e) {
664                 for (var id in this._eventParents) {
665                         this._eventParents[id].fire(e.type, extend({
666                                 layer: e.target,
667                                 propagatedFrom: e.target
668                         }, e), true);
669                 }
670         }
671 };
672
673 // aliases; we should ditch those eventually
674
675 // @method addEventListener(…): this
676 // Alias to [`on(…)`](#evented-on)
677 Events.addEventListener = Events.on;
678
679 // @method removeEventListener(…): this
680 // Alias to [`off(…)`](#evented-off)
681
682 // @method clearAllEventListeners(…): this
683 // Alias to [`off()`](#evented-off)
684 Events.removeEventListener = Events.clearAllEventListeners = Events.off;
685
686 // @method addOneTimeEventListener(…): this
687 // Alias to [`once(…)`](#evented-once)
688 Events.addOneTimeEventListener = Events.once;
689
690 // @method fireEvent(…): this
691 // Alias to [`fire(…)`](#evented-fire)
692 Events.fireEvent = Events.fire;
693
694 // @method hasEventListeners(…): Boolean
695 // Alias to [`listens(…)`](#evented-listens)
696 Events.hasEventListeners = Events.listens;
697
698 var Evented = Class.extend(Events);
699
700 /*
701  * @class Point
702  * @aka L.Point
703  *
704  * Represents a point with `x` and `y` coordinates in pixels.
705  *
706  * @example
707  *
708  * ```js
709  * var point = L.point(200, 300);
710  * ```
711  *
712  * 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:
713  *
714  * ```js
715  * map.panBy([200, 300]);
716  * map.panBy(L.point(200, 300));
717  * ```
718  *
719  * Note that `Point` does not inherit from Leafet's `Class` object,
720  * which means new classes can't inherit from it, and new methods
721  * can't be added to it with the `include` function.
722  */
723
724 function Point(x, y, round) {
725         // @property x: Number; The `x` coordinate of the point
726         this.x = (round ? Math.round(x) : x);
727         // @property y: Number; The `y` coordinate of the point
728         this.y = (round ? Math.round(y) : y);
729 }
730
731 var trunc = Math.trunc || function (v) {
732         return v > 0 ? Math.floor(v) : Math.ceil(v);
733 };
734
735 Point.prototype = {
736
737         // @method clone(): Point
738         // Returns a copy of the current point.
739         clone: function () {
740                 return new Point(this.x, this.y);
741         },
742
743         // @method add(otherPoint: Point): Point
744         // Returns the result of addition of the current and the given points.
745         add: function (point) {
746                 // non-destructive, returns a new point
747                 return this.clone()._add(toPoint(point));
748         },
749
750         _add: function (point) {
751                 // destructive, used directly for performance in situations where it's safe to modify existing point
752                 this.x += point.x;
753                 this.y += point.y;
754                 return this;
755         },
756
757         // @method subtract(otherPoint: Point): Point
758         // Returns the result of subtraction of the given point from the current.
759         subtract: function (point) {
760                 return this.clone()._subtract(toPoint(point));
761         },
762
763         _subtract: function (point) {
764                 this.x -= point.x;
765                 this.y -= point.y;
766                 return this;
767         },
768
769         // @method divideBy(num: Number): Point
770         // Returns the result of division of the current point by the given number.
771         divideBy: function (num) {
772                 return this.clone()._divideBy(num);
773         },
774
775         _divideBy: function (num) {
776                 this.x /= num;
777                 this.y /= num;
778                 return this;
779         },
780
781         // @method multiplyBy(num: Number): Point
782         // Returns the result of multiplication of the current point by the given number.
783         multiplyBy: function (num) {
784                 return this.clone()._multiplyBy(num);
785         },
786
787         _multiplyBy: function (num) {
788                 this.x *= num;
789                 this.y *= num;
790                 return this;
791         },
792
793         // @method scaleBy(scale: Point): Point
794         // Multiply each coordinate of the current point by each coordinate of
795         // `scale`. In linear algebra terms, multiply the point by the
796         // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
797         // defined by `scale`.
798         scaleBy: function (point) {
799                 return new Point(this.x * point.x, this.y * point.y);
800         },
801
802         // @method unscaleBy(scale: Point): Point
803         // Inverse of `scaleBy`. Divide each coordinate of the current point by
804         // each coordinate of `scale`.
805         unscaleBy: function (point) {
806                 return new Point(this.x / point.x, this.y / point.y);
807         },
808
809         // @method round(): Point
810         // Returns a copy of the current point with rounded coordinates.
811         round: function () {
812                 return this.clone()._round();
813         },
814
815         _round: function () {
816                 this.x = Math.round(this.x);
817                 this.y = Math.round(this.y);
818                 return this;
819         },
820
821         // @method floor(): Point
822         // Returns a copy of the current point with floored coordinates (rounded down).
823         floor: function () {
824                 return this.clone()._floor();
825         },
826
827         _floor: function () {
828                 this.x = Math.floor(this.x);
829                 this.y = Math.floor(this.y);
830                 return this;
831         },
832
833         // @method ceil(): Point
834         // Returns a copy of the current point with ceiled coordinates (rounded up).
835         ceil: function () {
836                 return this.clone()._ceil();
837         },
838
839         _ceil: function () {
840                 this.x = Math.ceil(this.x);
841                 this.y = Math.ceil(this.y);
842                 return this;
843         },
844
845         // @method trunc(): Point
846         // Returns a copy of the current point with truncated coordinates (rounded towards zero).
847         trunc: function () {
848                 return this.clone()._trunc();
849         },
850
851         _trunc: function () {
852                 this.x = trunc(this.x);
853                 this.y = trunc(this.y);
854                 return this;
855         },
856
857         // @method distanceTo(otherPoint: Point): Number
858         // Returns the cartesian distance between the current and the given points.
859         distanceTo: function (point) {
860                 point = toPoint(point);
861
862                 var x = point.x - this.x,
863                     y = point.y - this.y;
864
865                 return Math.sqrt(x * x + y * y);
866         },
867
868         // @method equals(otherPoint: Point): Boolean
869         // Returns `true` if the given point has the same coordinates.
870         equals: function (point) {
871                 point = toPoint(point);
872
873                 return point.x === this.x &&
874                        point.y === this.y;
875         },
876
877         // @method contains(otherPoint: Point): Boolean
878         // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
879         contains: function (point) {
880                 point = toPoint(point);
881
882                 return Math.abs(point.x) <= Math.abs(this.x) &&
883                        Math.abs(point.y) <= Math.abs(this.y);
884         },
885
886         // @method toString(): String
887         // Returns a string representation of the point for debugging purposes.
888         toString: function () {
889                 return 'Point(' +
890                         formatNum(this.x) + ', ' +
891                         formatNum(this.y) + ')';
892         }
893 };
894
895 // @factory L.point(x: Number, y: Number, round?: Boolean)
896 // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
897
898 // @alternative
899 // @factory L.point(coords: Number[])
900 // Expects an array of the form `[x, y]` instead.
901
902 // @alternative
903 // @factory L.point(coords: Object)
904 // Expects a plain object of the form `{x: Number, y: Number}` instead.
905 function toPoint(x, y, round) {
906         if (x instanceof Point) {
907                 return x;
908         }
909         if (isArray(x)) {
910                 return new Point(x[0], x[1]);
911         }
912         if (x === undefined || x === null) {
913                 return x;
914         }
915         if (typeof x === 'object' && 'x' in x && 'y' in x) {
916                 return new Point(x.x, x.y);
917         }
918         return new Point(x, y, round);
919 }
920
921 /*
922  * @class Bounds
923  * @aka L.Bounds
924  *
925  * Represents a rectangular area in pixel coordinates.
926  *
927  * @example
928  *
929  * ```js
930  * var p1 = L.point(10, 10),
931  * p2 = L.point(40, 60),
932  * bounds = L.bounds(p1, p2);
933  * ```
934  *
935  * 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:
936  *
937  * ```js
938  * otherBounds.intersects([[10, 10], [40, 60]]);
939  * ```
940  *
941  * Note that `Bounds` does not inherit from Leafet's `Class` object,
942  * which means new classes can't inherit from it, and new methods
943  * can't be added to it with the `include` function.
944  */
945
946 function Bounds(a, b) {
947         if (!a) { return; }
948
949         var points = b ? [a, b] : a;
950
951         for (var i = 0, len = points.length; i < len; i++) {
952                 this.extend(points[i]);
953         }
954 }
955
956 Bounds.prototype = {
957         // @method extend(point: Point): this
958         // Extends the bounds to contain the given point.
959         extend: function (point) { // (Point)
960                 point = toPoint(point);
961
962                 // @property min: Point
963                 // The top left corner of the rectangle.
964                 // @property max: Point
965                 // The bottom right corner of the rectangle.
966                 if (!this.min && !this.max) {
967                         this.min = point.clone();
968                         this.max = point.clone();
969                 } else {
970                         this.min.x = Math.min(point.x, this.min.x);
971                         this.max.x = Math.max(point.x, this.max.x);
972                         this.min.y = Math.min(point.y, this.min.y);
973                         this.max.y = Math.max(point.y, this.max.y);
974                 }
975                 return this;
976         },
977
978         // @method getCenter(round?: Boolean): Point
979         // Returns the center point of the bounds.
980         getCenter: function (round) {
981                 return new Point(
982                         (this.min.x + this.max.x) / 2,
983                         (this.min.y + this.max.y) / 2, round);
984         },
985
986         // @method getBottomLeft(): Point
987         // Returns the bottom-left point of the bounds.
988         getBottomLeft: function () {
989                 return new Point(this.min.x, this.max.y);
990         },
991
992         // @method getTopRight(): Point
993         // Returns the top-right point of the bounds.
994         getTopRight: function () { // -> Point
995                 return new Point(this.max.x, this.min.y);
996         },
997
998         // @method getTopLeft(): Point
999         // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)).
1000         getTopLeft: function () {
1001                 return this.min; // left, top
1002         },
1003
1004         // @method getBottomRight(): Point
1005         // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)).
1006         getBottomRight: function () {
1007                 return this.max; // right, bottom
1008         },
1009
1010         // @method getSize(): Point
1011         // Returns the size of the given bounds
1012         getSize: function () {
1013                 return this.max.subtract(this.min);
1014         },
1015
1016         // @method contains(otherBounds: Bounds): Boolean
1017         // Returns `true` if the rectangle contains the given one.
1018         // @alternative
1019         // @method contains(point: Point): Boolean
1020         // Returns `true` if the rectangle contains the given point.
1021         contains: function (obj) {
1022                 var min, max;
1023
1024                 if (typeof obj[0] === 'number' || obj instanceof Point) {
1025                         obj = toPoint(obj);
1026                 } else {
1027                         obj = toBounds(obj);
1028                 }
1029
1030                 if (obj instanceof Bounds) {
1031                         min = obj.min;
1032                         max = obj.max;
1033                 } else {
1034                         min = max = obj;
1035                 }
1036
1037                 return (min.x >= this.min.x) &&
1038                        (max.x <= this.max.x) &&
1039                        (min.y >= this.min.y) &&
1040                        (max.y <= this.max.y);
1041         },
1042
1043         // @method intersects(otherBounds: Bounds): Boolean
1044         // Returns `true` if the rectangle intersects the given bounds. Two bounds
1045         // intersect if they have at least one point in common.
1046         intersects: function (bounds) { // (Bounds) -> Boolean
1047                 bounds = toBounds(bounds);
1048
1049                 var min = this.min,
1050                     max = this.max,
1051                     min2 = bounds.min,
1052                     max2 = bounds.max,
1053                     xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
1054                     yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
1055
1056                 return xIntersects && yIntersects;
1057         },
1058
1059         // @method overlaps(otherBounds: Bounds): Boolean
1060         // Returns `true` if the rectangle overlaps the given bounds. Two bounds
1061         // overlap if their intersection is an area.
1062         overlaps: function (bounds) { // (Bounds) -> Boolean
1063                 bounds = toBounds(bounds);
1064
1065                 var min = this.min,
1066                     max = this.max,
1067                     min2 = bounds.min,
1068                     max2 = bounds.max,
1069                     xOverlaps = (max2.x > min.x) && (min2.x < max.x),
1070                     yOverlaps = (max2.y > min.y) && (min2.y < max.y);
1071
1072                 return xOverlaps && yOverlaps;
1073         },
1074
1075         isValid: function () {
1076                 return !!(this.min && this.max);
1077         }
1078 };
1079
1080
1081 // @factory L.bounds(corner1: Point, corner2: Point)
1082 // Creates a Bounds object from two corners coordinate pairs.
1083 // @alternative
1084 // @factory L.bounds(points: Point[])
1085 // Creates a Bounds object from the given array of points.
1086 function toBounds(a, b) {
1087         if (!a || a instanceof Bounds) {
1088                 return a;
1089         }
1090         return new Bounds(a, b);
1091 }
1092
1093 /*
1094  * @class LatLngBounds
1095  * @aka L.LatLngBounds
1096  *
1097  * Represents a rectangular geographical area on a map.
1098  *
1099  * @example
1100  *
1101  * ```js
1102  * var corner1 = L.latLng(40.712, -74.227),
1103  * corner2 = L.latLng(40.774, -74.125),
1104  * bounds = L.latLngBounds(corner1, corner2);
1105  * ```
1106  *
1107  * 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:
1108  *
1109  * ```js
1110  * map.fitBounds([
1111  *      [40.712, -74.227],
1112  *      [40.774, -74.125]
1113  * ]);
1114  * ```
1115  *
1116  * 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.
1117  *
1118  * Note that `LatLngBounds` does not inherit from Leafet's `Class` object,
1119  * which means new classes can't inherit from it, and new methods
1120  * can't be added to it with the `include` function.
1121  */
1122
1123 function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
1124         if (!corner1) { return; }
1125
1126         var latlngs = corner2 ? [corner1, corner2] : corner1;
1127
1128         for (var i = 0, len = latlngs.length; i < len; i++) {
1129                 this.extend(latlngs[i]);
1130         }
1131 }
1132
1133 LatLngBounds.prototype = {
1134
1135         // @method extend(latlng: LatLng): this
1136         // Extend the bounds to contain the given point
1137
1138         // @alternative
1139         // @method extend(otherBounds: LatLngBounds): this
1140         // Extend the bounds to contain the given bounds
1141         extend: function (obj) {
1142                 var sw = this._southWest,
1143                     ne = this._northEast,
1144                     sw2, ne2;
1145
1146                 if (obj instanceof LatLng) {
1147                         sw2 = obj;
1148                         ne2 = obj;
1149
1150                 } else if (obj instanceof LatLngBounds) {
1151                         sw2 = obj._southWest;
1152                         ne2 = obj._northEast;
1153
1154                         if (!sw2 || !ne2) { return this; }
1155
1156                 } else {
1157                         return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this;
1158                 }
1159
1160                 if (!sw && !ne) {
1161                         this._southWest = new LatLng(sw2.lat, sw2.lng);
1162                         this._northEast = new LatLng(ne2.lat, ne2.lng);
1163                 } else {
1164                         sw.lat = Math.min(sw2.lat, sw.lat);
1165                         sw.lng = Math.min(sw2.lng, sw.lng);
1166                         ne.lat = Math.max(ne2.lat, ne.lat);
1167                         ne.lng = Math.max(ne2.lng, ne.lng);
1168                 }
1169
1170                 return this;
1171         },
1172
1173         // @method pad(bufferRatio: Number): LatLngBounds
1174         // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
1175         // For example, a ratio of 0.5 extends the bounds by 50% in each direction.
1176         // Negative values will retract the bounds.
1177         pad: function (bufferRatio) {
1178                 var sw = this._southWest,
1179                     ne = this._northEast,
1180                     heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
1181                     widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
1182
1183                 return new LatLngBounds(
1184                         new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
1185                         new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
1186         },
1187
1188         // @method getCenter(): LatLng
1189         // Returns the center point of the bounds.
1190         getCenter: function () {
1191                 return new LatLng(
1192                         (this._southWest.lat + this._northEast.lat) / 2,
1193                         (this._southWest.lng + this._northEast.lng) / 2);
1194         },
1195
1196         // @method getSouthWest(): LatLng
1197         // Returns the south-west point of the bounds.
1198         getSouthWest: function () {
1199                 return this._southWest;
1200         },
1201
1202         // @method getNorthEast(): LatLng
1203         // Returns the north-east point of the bounds.
1204         getNorthEast: function () {
1205                 return this._northEast;
1206         },
1207
1208         // @method getNorthWest(): LatLng
1209         // Returns the north-west point of the bounds.
1210         getNorthWest: function () {
1211                 return new LatLng(this.getNorth(), this.getWest());
1212         },
1213
1214         // @method getSouthEast(): LatLng
1215         // Returns the south-east point of the bounds.
1216         getSouthEast: function () {
1217                 return new LatLng(this.getSouth(), this.getEast());
1218         },
1219
1220         // @method getWest(): Number
1221         // Returns the west longitude of the bounds
1222         getWest: function () {
1223                 return this._southWest.lng;
1224         },
1225
1226         // @method getSouth(): Number
1227         // Returns the south latitude of the bounds
1228         getSouth: function () {
1229                 return this._southWest.lat;
1230         },
1231
1232         // @method getEast(): Number
1233         // Returns the east longitude of the bounds
1234         getEast: function () {
1235                 return this._northEast.lng;
1236         },
1237
1238         // @method getNorth(): Number
1239         // Returns the north latitude of the bounds
1240         getNorth: function () {
1241                 return this._northEast.lat;
1242         },
1243
1244         // @method contains(otherBounds: LatLngBounds): Boolean
1245         // Returns `true` if the rectangle contains the given one.
1246
1247         // @alternative
1248         // @method contains (latlng: LatLng): Boolean
1249         // Returns `true` if the rectangle contains the given point.
1250         contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
1251                 if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) {
1252                         obj = toLatLng(obj);
1253                 } else {
1254                         obj = toLatLngBounds(obj);
1255                 }
1256
1257                 var sw = this._southWest,
1258                     ne = this._northEast,
1259                     sw2, ne2;
1260
1261                 if (obj instanceof LatLngBounds) {
1262                         sw2 = obj.getSouthWest();
1263                         ne2 = obj.getNorthEast();
1264                 } else {
1265                         sw2 = ne2 = obj;
1266                 }
1267
1268                 return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
1269                        (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
1270         },
1271
1272         // @method intersects(otherBounds: LatLngBounds): Boolean
1273         // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
1274         intersects: function (bounds) {
1275                 bounds = toLatLngBounds(bounds);
1276
1277                 var sw = this._southWest,
1278                     ne = this._northEast,
1279                     sw2 = bounds.getSouthWest(),
1280                     ne2 = bounds.getNorthEast(),
1281
1282                     latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
1283                     lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
1284
1285                 return latIntersects && lngIntersects;
1286         },
1287
1288         // @method overlaps(otherBounds: Bounds): Boolean
1289         // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
1290         overlaps: function (bounds) {
1291                 bounds = toLatLngBounds(bounds);
1292
1293                 var sw = this._southWest,
1294                     ne = this._northEast,
1295                     sw2 = bounds.getSouthWest(),
1296                     ne2 = bounds.getNorthEast(),
1297
1298                     latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
1299                     lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
1300
1301                 return latOverlaps && lngOverlaps;
1302         },
1303
1304         // @method toBBoxString(): String
1305         // 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.
1306         toBBoxString: function () {
1307                 return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
1308         },
1309
1310         // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean
1311         // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overridden by setting `maxMargin` to a small number.
1312         equals: function (bounds, maxMargin) {
1313                 if (!bounds) { return false; }
1314
1315                 bounds = toLatLngBounds(bounds);
1316
1317                 return this._southWest.equals(bounds.getSouthWest(), maxMargin) &&
1318                        this._northEast.equals(bounds.getNorthEast(), maxMargin);
1319         },
1320
1321         // @method isValid(): Boolean
1322         // Returns `true` if the bounds are properly initialized.
1323         isValid: function () {
1324                 return !!(this._southWest && this._northEast);
1325         }
1326 };
1327
1328 // TODO International date line?
1329
1330 // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
1331 // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
1332
1333 // @alternative
1334 // @factory L.latLngBounds(latlngs: LatLng[])
1335 // 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).
1336 function toLatLngBounds(a, b) {
1337         if (a instanceof LatLngBounds) {
1338                 return a;
1339         }
1340         return new LatLngBounds(a, b);
1341 }
1342
1343 /* @class LatLng
1344  * @aka L.LatLng
1345  *
1346  * Represents a geographical point with a certain latitude and longitude.
1347  *
1348  * @example
1349  *
1350  * ```
1351  * var latlng = L.latLng(50.5, 30.5);
1352  * ```
1353  *
1354  * 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:
1355  *
1356  * ```
1357  * map.panTo([50, 30]);
1358  * map.panTo({lon: 30, lat: 50});
1359  * map.panTo({lat: 50, lng: 30});
1360  * map.panTo(L.latLng(50, 30));
1361  * ```
1362  *
1363  * Note that `LatLng` does not inherit from Leaflet's `Class` object,
1364  * which means new classes can't inherit from it, and new methods
1365  * can't be added to it with the `include` function.
1366  */
1367
1368 function LatLng(lat, lng, alt) {
1369         if (isNaN(lat) || isNaN(lng)) {
1370                 throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
1371         }
1372
1373         // @property lat: Number
1374         // Latitude in degrees
1375         this.lat = +lat;
1376
1377         // @property lng: Number
1378         // Longitude in degrees
1379         this.lng = +lng;
1380
1381         // @property alt: Number
1382         // Altitude in meters (optional)
1383         if (alt !== undefined) {
1384                 this.alt = +alt;
1385         }
1386 }
1387
1388 LatLng.prototype = {
1389         // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
1390         // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overridden by setting `maxMargin` to a small number.
1391         equals: function (obj, maxMargin) {
1392                 if (!obj) { return false; }
1393
1394                 obj = toLatLng(obj);
1395
1396                 var margin = Math.max(
1397                         Math.abs(this.lat - obj.lat),
1398                         Math.abs(this.lng - obj.lng));
1399
1400                 return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
1401         },
1402
1403         // @method toString(): String
1404         // Returns a string representation of the point (for debugging purposes).
1405         toString: function (precision) {
1406                 return 'LatLng(' +
1407                         formatNum(this.lat, precision) + ', ' +
1408                         formatNum(this.lng, precision) + ')';
1409         },
1410
1411         // @method distanceTo(otherLatLng: LatLng): Number
1412         // Returns the distance (in meters) to the given `LatLng` calculated using the [Spherical Law of Cosines](https://en.wikipedia.org/wiki/Spherical_law_of_cosines).
1413         distanceTo: function (other) {
1414                 return Earth.distance(this, toLatLng(other));
1415         },
1416
1417         // @method wrap(): LatLng
1418         // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
1419         wrap: function () {
1420                 return Earth.wrapLatLng(this);
1421         },
1422
1423         // @method toBounds(sizeInMeters: Number): LatLngBounds
1424         // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
1425         toBounds: function (sizeInMeters) {
1426                 var latAccuracy = 180 * sizeInMeters / 40075017,
1427                     lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
1428
1429                 return toLatLngBounds(
1430                         [this.lat - latAccuracy, this.lng - lngAccuracy],
1431                         [this.lat + latAccuracy, this.lng + lngAccuracy]);
1432         },
1433
1434         clone: function () {
1435                 return new LatLng(this.lat, this.lng, this.alt);
1436         }
1437 };
1438
1439
1440
1441 // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
1442 // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
1443
1444 // @alternative
1445 // @factory L.latLng(coords: Array): LatLng
1446 // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
1447
1448 // @alternative
1449 // @factory L.latLng(coords: Object): LatLng
1450 // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
1451
1452 function toLatLng(a, b, c) {
1453         if (a instanceof LatLng) {
1454                 return a;
1455         }
1456         if (isArray(a) && typeof a[0] !== 'object') {
1457                 if (a.length === 3) {
1458                         return new LatLng(a[0], a[1], a[2]);
1459                 }
1460                 if (a.length === 2) {
1461                         return new LatLng(a[0], a[1]);
1462                 }
1463                 return null;
1464         }
1465         if (a === undefined || a === null) {
1466                 return a;
1467         }
1468         if (typeof a === 'object' && 'lat' in a) {
1469                 return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
1470         }
1471         if (b === undefined) {
1472                 return null;
1473         }
1474         return new LatLng(a, b, c);
1475 }
1476
1477 /*
1478  * @namespace CRS
1479  * @crs L.CRS.Base
1480  * Object that defines coordinate reference systems for projecting
1481  * geographical points into pixel (screen) coordinates and back (and to
1482  * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
1483  * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
1484  *
1485  * Leaflet defines the most usual CRSs by default. If you want to use a
1486  * CRS not defined by default, take a look at the
1487  * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
1488  *
1489  * Note that the CRS instances do not inherit from Leafet's `Class` object,
1490  * and can't be instantiated. Also, new classes can't inherit from them,
1491  * and methods can't be added to them with the `include` function.
1492  */
1493
1494 var CRS = {
1495         // @method latLngToPoint(latlng: LatLng, zoom: Number): Point
1496         // Projects geographical coordinates into pixel coordinates for a given zoom.
1497         latLngToPoint: function (latlng, zoom) {
1498                 var projectedPoint = this.projection.project(latlng),
1499                     scale = this.scale(zoom);
1500
1501                 return this.transformation._transform(projectedPoint, scale);
1502         },
1503
1504         // @method pointToLatLng(point: Point, zoom: Number): LatLng
1505         // The inverse of `latLngToPoint`. Projects pixel coordinates on a given
1506         // zoom into geographical coordinates.
1507         pointToLatLng: function (point, zoom) {
1508                 var scale = this.scale(zoom),
1509                     untransformedPoint = this.transformation.untransform(point, scale);
1510
1511                 return this.projection.unproject(untransformedPoint);
1512         },
1513
1514         // @method project(latlng: LatLng): Point
1515         // Projects geographical coordinates into coordinates in units accepted for
1516         // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
1517         project: function (latlng) {
1518                 return this.projection.project(latlng);
1519         },
1520
1521         // @method unproject(point: Point): LatLng
1522         // Given a projected coordinate returns the corresponding LatLng.
1523         // The inverse of `project`.
1524         unproject: function (point) {
1525                 return this.projection.unproject(point);
1526         },
1527
1528         // @method scale(zoom: Number): Number
1529         // Returns the scale used when transforming projected coordinates into
1530         // pixel coordinates for a particular zoom. For example, it returns
1531         // `256 * 2^zoom` for Mercator-based CRS.
1532         scale: function (zoom) {
1533                 return 256 * Math.pow(2, zoom);
1534         },
1535
1536         // @method zoom(scale: Number): Number
1537         // Inverse of `scale()`, returns the zoom level corresponding to a scale
1538         // factor of `scale`.
1539         zoom: function (scale) {
1540                 return Math.log(scale / 256) / Math.LN2;
1541         },
1542
1543         // @method getProjectedBounds(zoom: Number): Bounds
1544         // Returns the projection's bounds scaled and transformed for the provided `zoom`.
1545         getProjectedBounds: function (zoom) {
1546                 if (this.infinite) { return null; }
1547
1548                 var b = this.projection.bounds,
1549                     s = this.scale(zoom),
1550                     min = this.transformation.transform(b.min, s),
1551                     max = this.transformation.transform(b.max, s);
1552
1553                 return new Bounds(min, max);
1554         },
1555
1556         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
1557         // Returns the distance between two geographical coordinates.
1558
1559         // @property code: String
1560         // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
1561         //
1562         // @property wrapLng: Number[]
1563         // An array of two numbers defining whether the longitude (horizontal) coordinate
1564         // axis wraps around a given range and how. Defaults to `[-180, 180]` in most
1565         // geographical CRSs. If `undefined`, the longitude axis does not wrap around.
1566         //
1567         // @property wrapLat: Number[]
1568         // Like `wrapLng`, but for the latitude (vertical) axis.
1569
1570         // wrapLng: [min, max],
1571         // wrapLat: [min, max],
1572
1573         // @property infinite: Boolean
1574         // If true, the coordinate space will be unbounded (infinite in both axes)
1575         infinite: false,
1576
1577         // @method wrapLatLng(latlng: LatLng): LatLng
1578         // Returns a `LatLng` where lat and lng has been wrapped according to the
1579         // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
1580         wrapLatLng: function (latlng) {
1581                 var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
1582                     lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
1583                     alt = latlng.alt;
1584
1585                 return new LatLng(lat, lng, alt);
1586         },
1587
1588         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
1589         // Returns a `LatLngBounds` with the same size as the given one, ensuring
1590         // that its center is within the CRS's bounds.
1591         // Only accepts actual `L.LatLngBounds` instances, not arrays.
1592         wrapLatLngBounds: function (bounds) {
1593                 var center = bounds.getCenter(),
1594                     newCenter = this.wrapLatLng(center),
1595                     latShift = center.lat - newCenter.lat,
1596                     lngShift = center.lng - newCenter.lng;
1597
1598                 if (latShift === 0 && lngShift === 0) {
1599                         return bounds;
1600                 }
1601
1602                 var sw = bounds.getSouthWest(),
1603                     ne = bounds.getNorthEast(),
1604                     newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift),
1605                     newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift);
1606
1607                 return new LatLngBounds(newSw, newNe);
1608         }
1609 };
1610
1611 /*
1612  * @namespace CRS
1613  * @crs L.CRS.Earth
1614  *
1615  * Serves as the base for CRS that are global such that they cover the earth.
1616  * Can only be used as the base for other CRS and cannot be used directly,
1617  * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
1618  * meters.
1619  */
1620
1621 var Earth = extend({}, CRS, {
1622         wrapLng: [-180, 180],
1623
1624         // Mean Earth Radius, as recommended for use by
1625         // the International Union of Geodesy and Geophysics,
1626         // see http://rosettacode.org/wiki/Haversine_formula
1627         R: 6371000,
1628
1629         // distance between two geographical points using spherical law of cosines approximation
1630         distance: function (latlng1, latlng2) {
1631                 var rad = Math.PI / 180,
1632                     lat1 = latlng1.lat * rad,
1633                     lat2 = latlng2.lat * rad,
1634                     sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2),
1635                     sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2),
1636                     a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon,
1637                     c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
1638                 return this.R * c;
1639         }
1640 });
1641
1642 /*
1643  * @namespace Projection
1644  * @projection L.Projection.SphericalMercator
1645  *
1646  * Spherical Mercator projection — the most common projection for online maps,
1647  * used by almost all free and commercial tile providers. Assumes that Earth is
1648  * a sphere. Used by the `EPSG:3857` CRS.
1649  */
1650
1651 var SphericalMercator = {
1652
1653         R: 6378137,
1654         MAX_LATITUDE: 85.0511287798,
1655
1656         project: function (latlng) {
1657                 var d = Math.PI / 180,
1658                     max = this.MAX_LATITUDE,
1659                     lat = Math.max(Math.min(max, latlng.lat), -max),
1660                     sin = Math.sin(lat * d);
1661
1662                 return new Point(
1663                         this.R * latlng.lng * d,
1664                         this.R * Math.log((1 + sin) / (1 - sin)) / 2);
1665         },
1666
1667         unproject: function (point) {
1668                 var d = 180 / Math.PI;
1669
1670                 return new LatLng(
1671                         (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
1672                         point.x * d / this.R);
1673         },
1674
1675         bounds: (function () {
1676                 var d = 6378137 * Math.PI;
1677                 return new Bounds([-d, -d], [d, d]);
1678         })()
1679 };
1680
1681 /*
1682  * @class Transformation
1683  * @aka L.Transformation
1684  *
1685  * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
1686  * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
1687  * the reverse. Used by Leaflet in its projections code.
1688  *
1689  * @example
1690  *
1691  * ```js
1692  * var transformation = L.transformation(2, 5, -1, 10),
1693  *      p = L.point(1, 2),
1694  *      p2 = transformation.transform(p), //  L.point(7, 8)
1695  *      p3 = transformation.untransform(p2); //  L.point(1, 2)
1696  * ```
1697  */
1698
1699
1700 // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
1701 // Creates a `Transformation` object with the given coefficients.
1702 function Transformation(a, b, c, d) {
1703         if (isArray(a)) {
1704                 // use array properties
1705                 this._a = a[0];
1706                 this._b = a[1];
1707                 this._c = a[2];
1708                 this._d = a[3];
1709                 return;
1710         }
1711         this._a = a;
1712         this._b = b;
1713         this._c = c;
1714         this._d = d;
1715 }
1716
1717 Transformation.prototype = {
1718         // @method transform(point: Point, scale?: Number): Point
1719         // Returns a transformed point, optionally multiplied by the given scale.
1720         // Only accepts actual `L.Point` instances, not arrays.
1721         transform: function (point, scale) { // (Point, Number) -> Point
1722                 return this._transform(point.clone(), scale);
1723         },
1724
1725         // destructive transform (faster)
1726         _transform: function (point, scale) {
1727                 scale = scale || 1;
1728                 point.x = scale * (this._a * point.x + this._b);
1729                 point.y = scale * (this._c * point.y + this._d);
1730                 return point;
1731         },
1732
1733         // @method untransform(point: Point, scale?: Number): Point
1734         // Returns the reverse transformation of the given point, optionally divided
1735         // by the given scale. Only accepts actual `L.Point` instances, not arrays.
1736         untransform: function (point, scale) {
1737                 scale = scale || 1;
1738                 return new Point(
1739                         (point.x / scale - this._b) / this._a,
1740                         (point.y / scale - this._d) / this._c);
1741         }
1742 };
1743
1744 // factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1745
1746 // @factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1747 // Instantiates a Transformation object with the given coefficients.
1748
1749 // @alternative
1750 // @factory L.transformation(coefficients: Array): Transformation
1751 // Expects an coefficients array of the form
1752 // `[a: Number, b: Number, c: Number, d: Number]`.
1753
1754 function toTransformation(a, b, c, d) {
1755         return new Transformation(a, b, c, d);
1756 }
1757
1758 /*
1759  * @namespace CRS
1760  * @crs L.CRS.EPSG3857
1761  *
1762  * The most common CRS for online maps, used by almost all free and commercial
1763  * tile providers. Uses Spherical Mercator projection. Set in by default in
1764  * Map's `crs` option.
1765  */
1766
1767 var EPSG3857 = extend({}, Earth, {
1768         code: 'EPSG:3857',
1769         projection: SphericalMercator,
1770
1771         transformation: (function () {
1772                 var scale = 0.5 / (Math.PI * SphericalMercator.R);
1773                 return toTransformation(scale, 0.5, -scale, 0.5);
1774         }())
1775 });
1776
1777 var EPSG900913 = extend({}, EPSG3857, {
1778         code: 'EPSG:900913'
1779 });
1780
1781 // @namespace SVG; @section
1782 // There are several static functions which can be called without instantiating L.SVG:
1783
1784 // @function create(name: String): SVGElement
1785 // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
1786 // corresponding to the class name passed. For example, using 'line' will return
1787 // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
1788 function svgCreate(name) {
1789         return document.createElementNS('http://www.w3.org/2000/svg', name);
1790 }
1791
1792 // @function pointsToPath(rings: Point[], closed: Boolean): String
1793 // Generates a SVG path string for multiple rings, with each ring turning
1794 // into "M..L..L.." instructions
1795 function pointsToPath(rings, closed) {
1796         var str = '',
1797         i, j, len, len2, points, p;
1798
1799         for (i = 0, len = rings.length; i < len; i++) {
1800                 points = rings[i];
1801
1802                 for (j = 0, len2 = points.length; j < len2; j++) {
1803                         p = points[j];
1804                         str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
1805                 }
1806
1807                 // closes the ring for polygons; "x" is VML syntax
1808                 str += closed ? (svg ? 'z' : 'x') : '';
1809         }
1810
1811         // SVG complains about empty path strings
1812         return str || 'M0 0';
1813 }
1814
1815 /*
1816  * @namespace Browser
1817  * @aka L.Browser
1818  *
1819  * A namespace with static properties for browser/feature detection used by Leaflet internally.
1820  *
1821  * @example
1822  *
1823  * ```js
1824  * if (L.Browser.ielt9) {
1825  *   alert('Upgrade your browser, dude!');
1826  * }
1827  * ```
1828  */
1829
1830 var style$1 = document.documentElement.style;
1831
1832 // @property ie: Boolean; `true` for all Internet Explorer versions (not Edge).
1833 var ie = 'ActiveXObject' in window;
1834
1835 // @property ielt9: Boolean; `true` for Internet Explorer versions less than 9.
1836 var ielt9 = ie && !document.addEventListener;
1837
1838 // @property edge: Boolean; `true` for the Edge web browser.
1839 var edge = 'msLaunchUri' in navigator && !('documentMode' in document);
1840
1841 // @property webkit: Boolean;
1842 // `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
1843 var webkit = userAgentContains('webkit');
1844
1845 // @property android: Boolean
1846 // `true` for any browser running on an Android platform.
1847 var android = userAgentContains('android');
1848
1849 // @property android23: Boolean; `true` for browsers running on Android 2 or Android 3.
1850 var android23 = userAgentContains('android 2') || userAgentContains('android 3');
1851
1852 /* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */
1853 var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit
1854 // @property androidStock: Boolean; `true` for the Android stock browser (i.e. not Chrome)
1855 var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window);
1856
1857 // @property opera: Boolean; `true` for the Opera browser
1858 var opera = !!window.opera;
1859
1860 // @property chrome: Boolean; `true` for the Chrome browser.
1861 var chrome = userAgentContains('chrome');
1862
1863 // @property gecko: Boolean; `true` for gecko-based browsers like Firefox.
1864 var gecko = userAgentContains('gecko') && !webkit && !opera && !ie;
1865
1866 // @property safari: Boolean; `true` for the Safari browser.
1867 var safari = !chrome && userAgentContains('safari');
1868
1869 var phantom = userAgentContains('phantom');
1870
1871 // @property opera12: Boolean
1872 // `true` for the Opera browser supporting CSS transforms (version 12 or later).
1873 var opera12 = 'OTransition' in style$1;
1874
1875 // @property win: Boolean; `true` when the browser is running in a Windows platform
1876 var win = navigator.platform.indexOf('Win') === 0;
1877
1878 // @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms.
1879 var ie3d = ie && ('transition' in style$1);
1880
1881 // @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms.
1882 var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23;
1883
1884 // @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms.
1885 var gecko3d = 'MozPerspective' in style$1;
1886
1887 // @property any3d: Boolean
1888 // `true` for all browsers supporting CSS transforms.
1889 var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom;
1890
1891 // @property mobile: Boolean; `true` for all browsers running in a mobile device.
1892 var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile');
1893
1894 // @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device.
1895 var mobileWebkit = mobile && webkit;
1896
1897 // @property mobileWebkit3d: Boolean
1898 // `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
1899 var mobileWebkit3d = mobile && webkit3d;
1900
1901 // @property msPointer: Boolean
1902 // `true` for browsers implementing the Microsoft touch events model (notably IE10).
1903 var msPointer = !window.PointerEvent && window.MSPointerEvent;
1904
1905 // @property pointer: Boolean
1906 // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
1907 var pointer = !!(window.PointerEvent || msPointer);
1908
1909 // @property touch: Boolean
1910 // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
1911 // This does not necessarily mean that the browser is running in a computer with
1912 // a touchscreen, it only means that the browser is capable of understanding
1913 // touch events.
1914 var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
1915                 (window.DocumentTouch && document instanceof window.DocumentTouch));
1916
1917 // @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device.
1918 var mobileOpera = mobile && opera;
1919
1920 // @property mobileGecko: Boolean
1921 // `true` for gecko-based browsers running in a mobile device.
1922 var mobileGecko = mobile && gecko;
1923
1924 // @property retina: Boolean
1925 // `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%.
1926 var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1;
1927
1928
1929 // @property canvas: Boolean
1930 // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
1931 var canvas = (function () {
1932         return !!document.createElement('canvas').getContext;
1933 }());
1934
1935 // @property svg: Boolean
1936 // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
1937 var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect);
1938
1939 // @property vml: Boolean
1940 // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
1941 var vml = !svg && (function () {
1942         try {
1943                 var div = document.createElement('div');
1944                 div.innerHTML = '<v:shape adj="1"/>';
1945
1946                 var shape = div.firstChild;
1947                 shape.style.behavior = 'url(#default#VML)';
1948
1949                 return shape && (typeof shape.adj === 'object');
1950
1951         } catch (e) {
1952                 return false;
1953         }
1954 }());
1955
1956
1957 function userAgentContains(str) {
1958         return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
1959 }
1960
1961
1962 var Browser = (Object.freeze || Object)({
1963         ie: ie,
1964         ielt9: ielt9,
1965         edge: edge,
1966         webkit: webkit,
1967         android: android,
1968         android23: android23,
1969         androidStock: androidStock,
1970         opera: opera,
1971         chrome: chrome,
1972         gecko: gecko,
1973         safari: safari,
1974         phantom: phantom,
1975         opera12: opera12,
1976         win: win,
1977         ie3d: ie3d,
1978         webkit3d: webkit3d,
1979         gecko3d: gecko3d,
1980         any3d: any3d,
1981         mobile: mobile,
1982         mobileWebkit: mobileWebkit,
1983         mobileWebkit3d: mobileWebkit3d,
1984         msPointer: msPointer,
1985         pointer: pointer,
1986         touch: touch,
1987         mobileOpera: mobileOpera,
1988         mobileGecko: mobileGecko,
1989         retina: retina,
1990         canvas: canvas,
1991         svg: svg,
1992         vml: vml
1993 });
1994
1995 /*
1996  * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
1997  */
1998
1999
2000 var POINTER_DOWN =   msPointer ? 'MSPointerDown'   : 'pointerdown';
2001 var POINTER_MOVE =   msPointer ? 'MSPointerMove'   : 'pointermove';
2002 var POINTER_UP =     msPointer ? 'MSPointerUp'     : 'pointerup';
2003 var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel';
2004 var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION'];
2005
2006 var _pointers = {};
2007 var _pointerDocListener = false;
2008
2009 // DomEvent.DoubleTap needs to know about this
2010 var _pointersCount = 0;
2011
2012 // Provides a touch events wrapper for (ms)pointer events.
2013 // ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
2014
2015 function addPointerListener(obj, type, handler, id) {
2016         if (type === 'touchstart') {
2017                 _addPointerStart(obj, handler, id);
2018
2019         } else if (type === 'touchmove') {
2020                 _addPointerMove(obj, handler, id);
2021
2022         } else if (type === 'touchend') {
2023                 _addPointerEnd(obj, handler, id);
2024         }
2025
2026         return this;
2027 }
2028
2029 function removePointerListener(obj, type, id) {
2030         var handler = obj['_leaflet_' + type + id];
2031
2032         if (type === 'touchstart') {
2033                 obj.removeEventListener(POINTER_DOWN, handler, false);
2034
2035         } else if (type === 'touchmove') {
2036                 obj.removeEventListener(POINTER_MOVE, handler, false);
2037
2038         } else if (type === 'touchend') {
2039                 obj.removeEventListener(POINTER_UP, handler, false);
2040                 obj.removeEventListener(POINTER_CANCEL, handler, false);
2041         }
2042
2043         return this;
2044 }
2045
2046 function _addPointerStart(obj, handler, id) {
2047         var onDown = bind(function (e) {
2048                 if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
2049                         // In IE11, some touch events needs to fire for form controls, or
2050                         // the controls will stop working. We keep a whitelist of tag names that
2051                         // need these events. For other target tags, we prevent default on the event.
2052                         if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
2053                                 preventDefault(e);
2054                         } else {
2055                                 return;
2056                         }
2057                 }
2058
2059                 _handlePointer(e, handler);
2060         });
2061
2062         obj['_leaflet_touchstart' + id] = onDown;
2063         obj.addEventListener(POINTER_DOWN, onDown, false);
2064
2065         // need to keep track of what pointers and how many are active to provide e.touches emulation
2066         if (!_pointerDocListener) {
2067                 // we listen documentElement as any drags that end by moving the touch off the screen get fired there
2068                 document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true);
2069                 document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true);
2070                 document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true);
2071                 document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
2072
2073                 _pointerDocListener = true;
2074         }
2075 }
2076
2077 function _globalPointerDown(e) {
2078         _pointers[e.pointerId] = e;
2079         _pointersCount++;
2080 }
2081
2082 function _globalPointerMove(e) {
2083         if (_pointers[e.pointerId]) {
2084                 _pointers[e.pointerId] = e;
2085         }
2086 }
2087
2088 function _globalPointerUp(e) {
2089         delete _pointers[e.pointerId];
2090         _pointersCount--;
2091 }
2092
2093 function _handlePointer(e, handler) {
2094         e.touches = [];
2095         for (var i in _pointers) {
2096                 e.touches.push(_pointers[i]);
2097         }
2098         e.changedTouches = [e];
2099
2100         handler(e);
2101 }
2102
2103 function _addPointerMove(obj, handler, id) {
2104         var onMove = function (e) {
2105                 // don't fire touch moves when mouse isn't down
2106                 if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
2107
2108                 _handlePointer(e, handler);
2109         };
2110
2111         obj['_leaflet_touchmove' + id] = onMove;
2112         obj.addEventListener(POINTER_MOVE, onMove, false);
2113 }
2114
2115 function _addPointerEnd(obj, handler, id) {
2116         var onUp = function (e) {
2117                 _handlePointer(e, handler);
2118         };
2119
2120         obj['_leaflet_touchend' + id] = onUp;
2121         obj.addEventListener(POINTER_UP, onUp, false);
2122         obj.addEventListener(POINTER_CANCEL, onUp, false);
2123 }
2124
2125 /*
2126  * Extends the event handling code with double tap support for mobile browsers.
2127  */
2128
2129 var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart';
2130 var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend';
2131 var _pre = '_leaflet_';
2132
2133 // inspired by Zepto touch code by Thomas Fuchs
2134 function addDoubleTapListener(obj, handler, id) {
2135         var last, touch$$1,
2136             doubleTap = false,
2137             delay = 250;
2138
2139         function onTouchStart(e) {
2140                 var count;
2141
2142                 if (pointer) {
2143                         if ((!edge) || e.pointerType === 'mouse') { return; }
2144                         count = _pointersCount;
2145                 } else {
2146                         count = e.touches.length;
2147                 }
2148
2149                 if (count > 1) { return; }
2150
2151                 var now = Date.now(),
2152                     delta = now - (last || now);
2153
2154                 touch$$1 = e.touches ? e.touches[0] : e;
2155                 doubleTap = (delta > 0 && delta <= delay);
2156                 last = now;
2157         }
2158
2159         function onTouchEnd(e) {
2160                 if (doubleTap && !touch$$1.cancelBubble) {
2161                         if (pointer) {
2162                                 if ((!edge) || e.pointerType === 'mouse') { return; }
2163                                 // work around .type being readonly with MSPointer* events
2164                                 var newTouch = {},
2165                                     prop, i;
2166
2167                                 for (i in touch$$1) {
2168                                         prop = touch$$1[i];
2169                                         newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop;
2170                                 }
2171                                 touch$$1 = newTouch;
2172                         }
2173                         touch$$1.type = 'dblclick';
2174                         handler(touch$$1);
2175                         last = null;
2176                 }
2177         }
2178
2179         obj[_pre + _touchstart + id] = onTouchStart;
2180         obj[_pre + _touchend + id] = onTouchEnd;
2181         obj[_pre + 'dblclick' + id] = handler;
2182
2183         obj.addEventListener(_touchstart, onTouchStart, false);
2184         obj.addEventListener(_touchend, onTouchEnd, false);
2185
2186         // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse),
2187         // the browser doesn't fire touchend/pointerup events but does fire
2188         // native dblclicks. See #4127.
2189         // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180.
2190         obj.addEventListener('dblclick', handler, false);
2191
2192         return this;
2193 }
2194
2195 function removeDoubleTapListener(obj, id) {
2196         var touchstart = obj[_pre + _touchstart + id],
2197             touchend = obj[_pre + _touchend + id],
2198             dblclick = obj[_pre + 'dblclick' + id];
2199
2200         obj.removeEventListener(_touchstart, touchstart, false);
2201         obj.removeEventListener(_touchend, touchend, false);
2202         if (!edge) {
2203                 obj.removeEventListener('dblclick', dblclick, false);
2204         }
2205
2206         return this;
2207 }
2208
2209 /*
2210  * @namespace DomUtil
2211  *
2212  * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
2213  * tree, used by Leaflet internally.
2214  *
2215  * Most functions expecting or returning a `HTMLElement` also work for
2216  * SVG elements. The only difference is that classes refer to CSS classes
2217  * in HTML and SVG classes in SVG.
2218  */
2219
2220
2221 // @property TRANSFORM: String
2222 // Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
2223 var TRANSFORM = testProp(
2224         ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
2225
2226 // webkitTransition comes first because some browser versions that drop vendor prefix don't do
2227 // the same for the transitionend event, in particular the Android 4.1 stock browser
2228
2229 // @property TRANSITION: String
2230 // Vendor-prefixed transition style name.
2231 var TRANSITION = testProp(
2232         ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
2233
2234 // @property TRANSITION_END: String
2235 // Vendor-prefixed transitionend event name.
2236 var TRANSITION_END =
2237         TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
2238
2239
2240 // @function get(id: String|HTMLElement): HTMLElement
2241 // Returns an element given its DOM id, or returns the element itself
2242 // if it was passed directly.
2243 function get(id) {
2244         return typeof id === 'string' ? document.getElementById(id) : id;
2245 }
2246
2247 // @function getStyle(el: HTMLElement, styleAttrib: String): String
2248 // Returns the value for a certain style attribute on an element,
2249 // including computed values or values set through CSS.
2250 function getStyle(el, style) {
2251         var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
2252
2253         if ((!value || value === 'auto') && document.defaultView) {
2254                 var css = document.defaultView.getComputedStyle(el, null);
2255                 value = css ? css[style] : null;
2256         }
2257         return value === 'auto' ? null : value;
2258 }
2259
2260 // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
2261 // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
2262 function create$1(tagName, className, container) {
2263         var el = document.createElement(tagName);
2264         el.className = className || '';
2265
2266         if (container) {
2267                 container.appendChild(el);
2268         }
2269         return el;
2270 }
2271
2272 // @function remove(el: HTMLElement)
2273 // Removes `el` from its parent element
2274 function remove(el) {
2275         var parent = el.parentNode;
2276         if (parent) {
2277                 parent.removeChild(el);
2278         }
2279 }
2280
2281 // @function empty(el: HTMLElement)
2282 // Removes all of `el`'s children elements from `el`
2283 function empty(el) {
2284         while (el.firstChild) {
2285                 el.removeChild(el.firstChild);
2286         }
2287 }
2288
2289 // @function toFront(el: HTMLElement)
2290 // Makes `el` the last child of its parent, so it renders in front of the other children.
2291 function toFront(el) {
2292         var parent = el.parentNode;
2293         if (parent.lastChild !== el) {
2294                 parent.appendChild(el);
2295         }
2296 }
2297
2298 // @function toBack(el: HTMLElement)
2299 // Makes `el` the first child of its parent, so it renders behind the other children.
2300 function toBack(el) {
2301         var parent = el.parentNode;
2302         if (parent.firstChild !== el) {
2303                 parent.insertBefore(el, parent.firstChild);
2304         }
2305 }
2306
2307 // @function hasClass(el: HTMLElement, name: String): Boolean
2308 // Returns `true` if the element's class attribute contains `name`.
2309 function hasClass(el, name) {
2310         if (el.classList !== undefined) {
2311                 return el.classList.contains(name);
2312         }
2313         var className = getClass(el);
2314         return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
2315 }
2316
2317 // @function addClass(el: HTMLElement, name: String)
2318 // Adds `name` to the element's class attribute.
2319 function addClass(el, name) {
2320         if (el.classList !== undefined) {
2321                 var classes = splitWords(name);
2322                 for (var i = 0, len = classes.length; i < len; i++) {
2323                         el.classList.add(classes[i]);
2324                 }
2325         } else if (!hasClass(el, name)) {
2326                 var className = getClass(el);
2327                 setClass(el, (className ? className + ' ' : '') + name);
2328         }
2329 }
2330
2331 // @function removeClass(el: HTMLElement, name: String)
2332 // Removes `name` from the element's class attribute.
2333 function removeClass(el, name) {
2334         if (el.classList !== undefined) {
2335                 el.classList.remove(name);
2336         } else {
2337                 setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
2338         }
2339 }
2340
2341 // @function setClass(el: HTMLElement, name: String)
2342 // Sets the element's class.
2343 function setClass(el, name) {
2344         if (el.className.baseVal === undefined) {
2345                 el.className = name;
2346         } else {
2347                 // in case of SVG element
2348                 el.className.baseVal = name;
2349         }
2350 }
2351
2352 // @function getClass(el: HTMLElement): String
2353 // Returns the element's class.
2354 function getClass(el) {
2355         return el.className.baseVal === undefined ? el.className : el.className.baseVal;
2356 }
2357
2358 // @function setOpacity(el: HTMLElement, opacity: Number)
2359 // Set the opacity of an element (including old IE support).
2360 // `opacity` must be a number from `0` to `1`.
2361 function setOpacity(el, value) {
2362         if ('opacity' in el.style) {
2363                 el.style.opacity = value;
2364         } else if ('filter' in el.style) {
2365                 _setOpacityIE(el, value);
2366         }
2367 }
2368
2369 function _setOpacityIE(el, value) {
2370         var filter = false,
2371             filterName = 'DXImageTransform.Microsoft.Alpha';
2372
2373         // filters collection throws an error if we try to retrieve a filter that doesn't exist
2374         try {
2375                 filter = el.filters.item(filterName);
2376         } catch (e) {
2377                 // don't set opacity to 1 if we haven't already set an opacity,
2378                 // it isn't needed and breaks transparent pngs.
2379                 if (value === 1) { return; }
2380         }
2381
2382         value = Math.round(value * 100);
2383
2384         if (filter) {
2385                 filter.Enabled = (value !== 100);
2386                 filter.Opacity = value;
2387         } else {
2388                 el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
2389         }
2390 }
2391
2392 // @function testProp(props: String[]): String|false
2393 // Goes through the array of style names and returns the first name
2394 // that is a valid style name for an element. If no such name is found,
2395 // it returns false. Useful for vendor-prefixed styles like `transform`.
2396 function testProp(props) {
2397         var style = document.documentElement.style;
2398
2399         for (var i = 0; i < props.length; i++) {
2400                 if (props[i] in style) {
2401                         return props[i];
2402                 }
2403         }
2404         return false;
2405 }
2406
2407 // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
2408 // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
2409 // and optionally scaled by `scale`. Does not have an effect if the
2410 // browser doesn't support 3D CSS transforms.
2411 function setTransform(el, offset, scale) {
2412         var pos = offset || new Point(0, 0);
2413
2414         el.style[TRANSFORM] =
2415                 (ie3d ?
2416                         'translate(' + pos.x + 'px,' + pos.y + 'px)' :
2417                         'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
2418                 (scale ? ' scale(' + scale + ')' : '');
2419 }
2420
2421 // @function setPosition(el: HTMLElement, position: Point)
2422 // Sets the position of `el` to coordinates specified by `position`,
2423 // using CSS translate or top/left positioning depending on the browser
2424 // (used by Leaflet internally to position its layers).
2425 function setPosition(el, point) {
2426
2427         /*eslint-disable */
2428         el._leaflet_pos = point;
2429         /* eslint-enable */
2430
2431         if (any3d) {
2432                 setTransform(el, point);
2433         } else {
2434                 el.style.left = point.x + 'px';
2435                 el.style.top = point.y + 'px';
2436         }
2437 }
2438
2439 // @function getPosition(el: HTMLElement): Point
2440 // Returns the coordinates of an element previously positioned with setPosition.
2441 function getPosition(el) {
2442         // this method is only used for elements previously positioned using setPosition,
2443         // so it's safe to cache the position for performance
2444
2445         return el._leaflet_pos || new Point(0, 0);
2446 }
2447
2448 // @function disableTextSelection()
2449 // Prevents the user from generating `selectstart` DOM events, usually generated
2450 // when the user drags the mouse through a page with text. Used internally
2451 // by Leaflet to override the behaviour of any click-and-drag interaction on
2452 // the map. Affects drag interactions on the whole document.
2453
2454 // @function enableTextSelection()
2455 // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
2456 var disableTextSelection;
2457 var enableTextSelection;
2458 var _userSelect;
2459 if ('onselectstart' in document) {
2460         disableTextSelection = function () {
2461                 on(window, 'selectstart', preventDefault);
2462         };
2463         enableTextSelection = function () {
2464                 off(window, 'selectstart', preventDefault);
2465         };
2466 } else {
2467         var userSelectProperty = testProp(
2468                 ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
2469
2470         disableTextSelection = function () {
2471                 if (userSelectProperty) {
2472                         var style = document.documentElement.style;
2473                         _userSelect = style[userSelectProperty];
2474                         style[userSelectProperty] = 'none';
2475                 }
2476         };
2477         enableTextSelection = function () {
2478                 if (userSelectProperty) {
2479                         document.documentElement.style[userSelectProperty] = _userSelect;
2480                         _userSelect = undefined;
2481                 }
2482         };
2483 }
2484
2485 // @function disableImageDrag()
2486 // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
2487 // for `dragstart` DOM events, usually generated when the user drags an image.
2488 function disableImageDrag() {
2489         on(window, 'dragstart', preventDefault);
2490 }
2491
2492 // @function enableImageDrag()
2493 // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
2494 function enableImageDrag() {
2495         off(window, 'dragstart', preventDefault);
2496 }
2497
2498 var _outlineElement;
2499 var _outlineStyle;
2500 // @function preventOutline(el: HTMLElement)
2501 // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
2502 // of the element `el` invisible. Used internally by Leaflet to prevent
2503 // focusable elements from displaying an outline when the user performs a
2504 // drag interaction on them.
2505 function preventOutline(element) {
2506         while (element.tabIndex === -1) {
2507                 element = element.parentNode;
2508         }
2509         if (!element.style) { return; }
2510         restoreOutline();
2511         _outlineElement = element;
2512         _outlineStyle = element.style.outline;
2513         element.style.outline = 'none';
2514         on(window, 'keydown', restoreOutline);
2515 }
2516
2517 // @function restoreOutline()
2518 // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
2519 function restoreOutline() {
2520         if (!_outlineElement) { return; }
2521         _outlineElement.style.outline = _outlineStyle;
2522         _outlineElement = undefined;
2523         _outlineStyle = undefined;
2524         off(window, 'keydown', restoreOutline);
2525 }
2526
2527 // @function getSizedParentNode(el: HTMLElement): HTMLElement
2528 // Finds the closest parent node which size (width and height) is not null.
2529 function getSizedParentNode(element) {
2530         do {
2531                 element = element.parentNode;
2532         } while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body);
2533         return element;
2534 }
2535
2536 // @function getScale(el: HTMLElement): Object
2537 // Computes the CSS scale currently applied on the element.
2538 // Returns an object with `x` and `y` members as horizontal and vertical scales respectively,
2539 // and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
2540 function getScale(element) {
2541         var rect = element.getBoundingClientRect(); // Read-only in old browsers.
2542
2543         return {
2544                 x: rect.width / element.offsetWidth || 1,
2545                 y: rect.height / element.offsetHeight || 1,
2546                 boundingClientRect: rect
2547         };
2548 }
2549
2550
2551 var DomUtil = (Object.freeze || Object)({
2552         TRANSFORM: TRANSFORM,
2553         TRANSITION: TRANSITION,
2554         TRANSITION_END: TRANSITION_END,
2555         get: get,
2556         getStyle: getStyle,
2557         create: create$1,
2558         remove: remove,
2559         empty: empty,
2560         toFront: toFront,
2561         toBack: toBack,
2562         hasClass: hasClass,
2563         addClass: addClass,
2564         removeClass: removeClass,
2565         setClass: setClass,
2566         getClass: getClass,
2567         setOpacity: setOpacity,
2568         testProp: testProp,
2569         setTransform: setTransform,
2570         setPosition: setPosition,
2571         getPosition: getPosition,
2572         disableTextSelection: disableTextSelection,
2573         enableTextSelection: enableTextSelection,
2574         disableImageDrag: disableImageDrag,
2575         enableImageDrag: enableImageDrag,
2576         preventOutline: preventOutline,
2577         restoreOutline: restoreOutline,
2578         getSizedParentNode: getSizedParentNode,
2579         getScale: getScale
2580 });
2581
2582 /*
2583  * @namespace DomEvent
2584  * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
2585  */
2586
2587 // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
2588
2589 // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
2590 // Adds a listener function (`fn`) to a particular DOM event type of the
2591 // element `el`. You can optionally specify the context of the listener
2592 // (object the `this` keyword will point to). You can also pass several
2593 // space-separated types (e.g. `'click dblclick'`).
2594
2595 // @alternative
2596 // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
2597 // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2598 function on(obj, types, fn, context) {
2599
2600         if (typeof types === 'object') {
2601                 for (var type in types) {
2602                         addOne(obj, type, types[type], fn);
2603                 }
2604         } else {
2605                 types = splitWords(types);
2606
2607                 for (var i = 0, len = types.length; i < len; i++) {
2608                         addOne(obj, types[i], fn, context);
2609                 }
2610         }
2611
2612         return this;
2613 }
2614
2615 var eventsKey = '_leaflet_events';
2616
2617 // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
2618 // Removes a previously added listener function.
2619 // Note that if you passed a custom context to on, you must pass the same
2620 // context to `off` in order to remove the listener.
2621
2622 // @alternative
2623 // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
2624 // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2625 function off(obj, types, fn, context) {
2626
2627         if (typeof types === 'object') {
2628                 for (var type in types) {
2629                         removeOne(obj, type, types[type], fn);
2630                 }
2631         } else if (types) {
2632                 types = splitWords(types);
2633
2634                 for (var i = 0, len = types.length; i < len; i++) {
2635                         removeOne(obj, types[i], fn, context);
2636                 }
2637         } else {
2638                 for (var j in obj[eventsKey]) {
2639                         removeOne(obj, j, obj[eventsKey][j]);
2640                 }
2641                 delete obj[eventsKey];
2642         }
2643
2644         return this;
2645 }
2646
2647 function addOne(obj, type, fn, context) {
2648         var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
2649
2650         if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
2651
2652         var handler = function (e) {
2653                 return fn.call(context || obj, e || window.event);
2654         };
2655
2656         var originalHandler = handler;
2657
2658         if (pointer && type.indexOf('touch') === 0) {
2659                 // Needs DomEvent.Pointer.js
2660                 addPointerListener(obj, type, handler, id);
2661
2662         } else if (touch && (type === 'dblclick') && addDoubleTapListener &&
2663                    !(pointer && chrome)) {
2664                 // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
2665                 // See #5180
2666                 addDoubleTapListener(obj, handler, id);
2667
2668         } else if ('addEventListener' in obj) {
2669
2670                 if (type === 'mousewheel') {
2671                         obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
2672
2673                 } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
2674                         handler = function (e) {
2675                                 e = e || window.event;
2676                                 if (isExternalTarget(obj, e)) {
2677                                         originalHandler(e);
2678                                 }
2679                         };
2680                         obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
2681
2682                 } else {
2683                         if (type === 'click' && android) {
2684                                 handler = function (e) {
2685                                         filterClick(e, originalHandler);
2686                                 };
2687                         }
2688                         obj.addEventListener(type, handler, false);
2689                 }
2690
2691         } else if ('attachEvent' in obj) {
2692                 obj.attachEvent('on' + type, handler);
2693         }
2694
2695         obj[eventsKey] = obj[eventsKey] || {};
2696         obj[eventsKey][id] = handler;
2697 }
2698
2699 function removeOne(obj, type, fn, context) {
2700
2701         var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''),
2702             handler = obj[eventsKey] && obj[eventsKey][id];
2703
2704         if (!handler) { return this; }
2705
2706         if (pointer && type.indexOf('touch') === 0) {
2707                 removePointerListener(obj, type, id);
2708
2709         } else if (touch && (type === 'dblclick') && removeDoubleTapListener &&
2710                    !(pointer && chrome)) {
2711                 removeDoubleTapListener(obj, id);
2712
2713         } else if ('removeEventListener' in obj) {
2714
2715                 if (type === 'mousewheel') {
2716                         obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
2717
2718                 } else {
2719                         obj.removeEventListener(
2720                                 type === 'mouseenter' ? 'mouseover' :
2721                                 type === 'mouseleave' ? 'mouseout' : type, handler, false);
2722                 }
2723
2724         } else if ('detachEvent' in obj) {
2725                 obj.detachEvent('on' + type, handler);
2726         }
2727
2728         obj[eventsKey][id] = null;
2729 }
2730
2731 // @function stopPropagation(ev: DOMEvent): this
2732 // Stop the given event from propagation to parent elements. Used inside the listener functions:
2733 // ```js
2734 // L.DomEvent.on(div, 'click', function (ev) {
2735 //      L.DomEvent.stopPropagation(ev);
2736 // });
2737 // ```
2738 function stopPropagation(e) {
2739
2740         if (e.stopPropagation) {
2741                 e.stopPropagation();
2742         } else if (e.originalEvent) {  // In case of Leaflet event.
2743                 e.originalEvent._stopped = true;
2744         } else {
2745                 e.cancelBubble = true;
2746         }
2747         skipped(e);
2748
2749         return this;
2750 }
2751
2752 // @function disableScrollPropagation(el: HTMLElement): this
2753 // Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
2754 function disableScrollPropagation(el) {
2755         addOne(el, 'mousewheel', stopPropagation);
2756         return this;
2757 }
2758
2759 // @function disableClickPropagation(el: HTMLElement): this
2760 // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
2761 // `'mousedown'` and `'touchstart'` events (plus browser variants).
2762 function disableClickPropagation(el) {
2763         on(el, 'mousedown touchstart dblclick', stopPropagation);
2764         addOne(el, 'click', fakeStop);
2765         return this;
2766 }
2767
2768 // @function preventDefault(ev: DOMEvent): this
2769 // Prevents the default action of the DOM Event `ev` from happening (such as
2770 // following a link in the href of the a element, or doing a POST request
2771 // with page reload when a `<form>` is submitted).
2772 // Use it inside listener functions.
2773 function preventDefault(e) {
2774         if (e.preventDefault) {
2775                 e.preventDefault();
2776         } else {
2777                 e.returnValue = false;
2778         }
2779         return this;
2780 }
2781
2782 // @function stop(ev: DOMEvent): this
2783 // Does `stopPropagation` and `preventDefault` at the same time.
2784 function stop(e) {
2785         preventDefault(e);
2786         stopPropagation(e);
2787         return this;
2788 }
2789
2790 // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
2791 // Gets normalized mouse position from a DOM event relative to the
2792 // `container` (border excluded) or to the whole page if not specified.
2793 function getMousePosition(e, container) {
2794         if (!container) {
2795                 return new Point(e.clientX, e.clientY);
2796         }
2797
2798         var scale = getScale(container),
2799             offset = scale.boundingClientRect; // left and top  values are in page scale (like the event clientX/Y)
2800
2801         return new Point(
2802                 // offset.left/top values are in page scale (like clientX/Y),
2803                 // whereas clientLeft/Top (border width) values are the original values (before CSS scale applies).
2804                 (e.clientX - offset.left) / scale.x - container.clientLeft,
2805                 (e.clientY - offset.top) / scale.y - container.clientTop
2806         );
2807 }
2808
2809 // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
2810 // and Firefox scrolls device pixels, not CSS pixels
2811 var wheelPxFactor =
2812         (win && chrome) ? 2 * window.devicePixelRatio :
2813         gecko ? window.devicePixelRatio : 1;
2814
2815 // @function getWheelDelta(ev: DOMEvent): Number
2816 // Gets normalized wheel delta from a mousewheel DOM event, in vertical
2817 // pixels scrolled (negative if scrolling down).
2818 // Events from pointing devices without precise scrolling are mapped to
2819 // a best guess of 60 pixels.
2820 function getWheelDelta(e) {
2821         return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
2822                (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
2823                (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
2824                (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
2825                (e.deltaX || e.deltaZ) ? 0 :     // Skip horizontal/depth wheel events
2826                e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
2827                (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
2828                e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
2829                0;
2830 }
2831
2832 var skipEvents = {};
2833
2834 function fakeStop(e) {
2835         // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e)
2836         skipEvents[e.type] = true;
2837 }
2838
2839 function skipped(e) {
2840         var events = skipEvents[e.type];
2841         // reset when checking, as it's only used in map container and propagates outside of the map
2842         skipEvents[e.type] = false;
2843         return events;
2844 }
2845
2846 // check if element really left/entered the event target (for mouseenter/mouseleave)
2847 function isExternalTarget(el, e) {
2848
2849         var related = e.relatedTarget;
2850
2851         if (!related) { return true; }
2852
2853         try {
2854                 while (related && (related !== el)) {
2855                         related = related.parentNode;
2856                 }
2857         } catch (err) {
2858                 return false;
2859         }
2860         return (related !== el);
2861 }
2862
2863 var lastClick;
2864
2865 // this is a horrible workaround for a bug in Android where a single touch triggers two click events
2866 function filterClick(e, handler) {
2867         var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
2868             elapsed = lastClick && (timeStamp - lastClick);
2869
2870         // are they closer together than 500ms yet more than 100ms?
2871         // Android typically triggers them ~300ms apart while multiple listeners
2872         // on the same event should be triggered far faster;
2873         // or check if click is simulated on the element, and if it is, reject any non-simulated events
2874
2875         if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
2876                 stop(e);
2877                 return;
2878         }
2879         lastClick = timeStamp;
2880
2881         handler(e);
2882 }
2883
2884
2885
2886
2887 var DomEvent = (Object.freeze || Object)({
2888         on: on,
2889         off: off,
2890         stopPropagation: stopPropagation,
2891         disableScrollPropagation: disableScrollPropagation,
2892         disableClickPropagation: disableClickPropagation,
2893         preventDefault: preventDefault,
2894         stop: stop,
2895         getMousePosition: getMousePosition,
2896         getWheelDelta: getWheelDelta,
2897         fakeStop: fakeStop,
2898         skipped: skipped,
2899         isExternalTarget: isExternalTarget,
2900         addListener: on,
2901         removeListener: off
2902 });
2903
2904 /*
2905  * @class PosAnimation
2906  * @aka L.PosAnimation
2907  * @inherits Evented
2908  * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
2909  *
2910  * @example
2911  * ```js
2912  * var fx = new L.PosAnimation();
2913  * fx.run(el, [300, 500], 0.5);
2914  * ```
2915  *
2916  * @constructor L.PosAnimation()
2917  * Creates a `PosAnimation` object.
2918  *
2919  */
2920
2921 var PosAnimation = Evented.extend({
2922
2923         // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
2924         // Run an animation of a given element to a new position, optionally setting
2925         // duration in seconds (`0.25` by default) and easing linearity factor (3rd
2926         // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
2927         // `0.5` by default).
2928         run: function (el, newPos, duration, easeLinearity) {
2929                 this.stop();
2930
2931                 this._el = el;
2932                 this._inProgress = true;
2933                 this._duration = duration || 0.25;
2934                 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
2935
2936                 this._startPos = getPosition(el);
2937                 this._offset = newPos.subtract(this._startPos);
2938                 this._startTime = +new Date();
2939
2940                 // @event start: Event
2941                 // Fired when the animation starts
2942                 this.fire('start');
2943
2944                 this._animate();
2945         },
2946
2947         // @method stop()
2948         // Stops the animation (if currently running).
2949         stop: function () {
2950                 if (!this._inProgress) { return; }
2951
2952                 this._step(true);
2953                 this._complete();
2954         },
2955
2956         _animate: function () {
2957                 // animation loop
2958                 this._animId = requestAnimFrame(this._animate, this);
2959                 this._step();
2960         },
2961
2962         _step: function (round) {
2963                 var elapsed = (+new Date()) - this._startTime,
2964                     duration = this._duration * 1000;
2965
2966                 if (elapsed < duration) {
2967                         this._runFrame(this._easeOut(elapsed / duration), round);
2968                 } else {
2969                         this._runFrame(1);
2970                         this._complete();
2971                 }
2972         },
2973
2974         _runFrame: function (progress, round) {
2975                 var pos = this._startPos.add(this._offset.multiplyBy(progress));
2976                 if (round) {
2977                         pos._round();
2978                 }
2979                 setPosition(this._el, pos);
2980
2981                 // @event step: Event
2982                 // Fired continuously during the animation.
2983                 this.fire('step');
2984         },
2985
2986         _complete: function () {
2987                 cancelAnimFrame(this._animId);
2988
2989                 this._inProgress = false;
2990                 // @event end: Event
2991                 // Fired when the animation ends.
2992                 this.fire('end');
2993         },
2994
2995         _easeOut: function (t) {
2996                 return 1 - Math.pow(1 - t, this._easeOutPower);
2997         }
2998 });
2999
3000 /*
3001  * @class Map
3002  * @aka L.Map
3003  * @inherits Evented
3004  *
3005  * The central class of the API — it is used to create a map on a page and manipulate it.
3006  *
3007  * @example
3008  *
3009  * ```js
3010  * // initialize the map on the "map" div with a given center and zoom
3011  * var map = L.map('map', {
3012  *      center: [51.505, -0.09],
3013  *      zoom: 13
3014  * });
3015  * ```
3016  *
3017  */
3018
3019 var Map = Evented.extend({
3020
3021         options: {
3022                 // @section Map State Options
3023                 // @option crs: CRS = L.CRS.EPSG3857
3024                 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
3025                 // sure what it means.
3026                 crs: EPSG3857,
3027
3028                 // @option center: LatLng = undefined
3029                 // Initial geographic center of the map
3030                 center: undefined,
3031
3032                 // @option zoom: Number = undefined
3033                 // Initial map zoom level
3034                 zoom: undefined,
3035
3036                 // @option minZoom: Number = *
3037                 // Minimum zoom level of the map.
3038                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3039                 // the lowest of their `minZoom` options will be used instead.
3040                 minZoom: undefined,
3041
3042                 // @option maxZoom: Number = *
3043                 // Maximum zoom level of the map.
3044                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3045                 // the highest of their `maxZoom` options will be used instead.
3046                 maxZoom: undefined,
3047
3048                 // @option layers: Layer[] = []
3049                 // Array of layers that will be added to the map initially
3050                 layers: [],
3051
3052                 // @option maxBounds: LatLngBounds = null
3053                 // When this option is set, the map restricts the view to the given
3054                 // geographical bounds, bouncing the user back if the user tries to pan
3055                 // outside the view. To set the restriction dynamically, use
3056                 // [`setMaxBounds`](#map-setmaxbounds) method.
3057                 maxBounds: undefined,
3058
3059                 // @option renderer: Renderer = *
3060                 // The default method for drawing vector layers on the map. `L.SVG`
3061                 // or `L.Canvas` by default depending on browser support.
3062                 renderer: undefined,
3063
3064
3065                 // @section Animation Options
3066                 // @option zoomAnimation: Boolean = true
3067                 // Whether the map zoom animation is enabled. By default it's enabled
3068                 // in all browsers that support CSS3 Transitions except Android.
3069                 zoomAnimation: true,
3070
3071                 // @option zoomAnimationThreshold: Number = 4
3072                 // Won't animate zoom if the zoom difference exceeds this value.
3073                 zoomAnimationThreshold: 4,
3074
3075                 // @option fadeAnimation: Boolean = true
3076                 // Whether the tile fade animation is enabled. By default it's enabled
3077                 // in all browsers that support CSS3 Transitions except Android.
3078                 fadeAnimation: true,
3079
3080                 // @option markerZoomAnimation: Boolean = true
3081                 // Whether markers animate their zoom with the zoom animation, if disabled
3082                 // they will disappear for the length of the animation. By default it's
3083                 // enabled in all browsers that support CSS3 Transitions except Android.
3084                 markerZoomAnimation: true,
3085
3086                 // @option transform3DLimit: Number = 2^23
3087                 // Defines the maximum size of a CSS translation transform. The default
3088                 // value should not be changed unless a web browser positions layers in
3089                 // the wrong place after doing a large `panBy`.
3090                 transform3DLimit: 8388608, // Precision limit of a 32-bit float
3091
3092                 // @section Interaction Options
3093                 // @option zoomSnap: Number = 1
3094                 // Forces the map's zoom level to always be a multiple of this, particularly
3095                 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
3096                 // By default, the zoom level snaps to the nearest integer; lower values
3097                 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
3098                 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
3099                 zoomSnap: 1,
3100
3101                 // @option zoomDelta: Number = 1
3102                 // Controls how much the map's zoom level will change after a
3103                 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
3104                 // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
3105                 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
3106                 zoomDelta: 1,
3107
3108                 // @option trackResize: Boolean = true
3109                 // Whether the map automatically handles browser window resize to update itself.
3110                 trackResize: true
3111         },
3112
3113         initialize: function (id, options) { // (HTMLElement or String, Object)
3114                 options = setOptions(this, options);
3115
3116                 this._initContainer(id);
3117                 this._initLayout();
3118
3119                 // hack for https://github.com/Leaflet/Leaflet/issues/1980
3120                 this._onResize = bind(this._onResize, this);
3121
3122                 this._initEvents();
3123
3124                 if (options.maxBounds) {
3125                         this.setMaxBounds(options.maxBounds);
3126                 }
3127
3128                 if (options.zoom !== undefined) {
3129                         this._zoom = this._limitZoom(options.zoom);
3130                 }
3131
3132                 if (options.center && options.zoom !== undefined) {
3133                         this.setView(toLatLng(options.center), options.zoom, {reset: true});
3134                 }
3135
3136                 this._handlers = [];
3137                 this._layers = {};
3138                 this._zoomBoundLayers = {};
3139                 this._sizeChanged = true;
3140
3141                 this.callInitHooks();
3142
3143                 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
3144                 this._zoomAnimated = TRANSITION && any3d && !mobileOpera &&
3145                                 this.options.zoomAnimation;
3146
3147                 // zoom transitions run with the same duration for all layers, so if one of transitionend events
3148                 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
3149                 if (this._zoomAnimated) {
3150                         this._createAnimProxy();
3151                         on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
3152                 }
3153
3154                 this._addLayers(this.options.layers);
3155         },
3156
3157
3158         // @section Methods for modifying map state
3159
3160         // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
3161         // Sets the view of the map (geographical center and zoom) with the given
3162         // animation options.
3163         setView: function (center, zoom, options) {
3164
3165                 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
3166                 center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
3167                 options = options || {};
3168
3169                 this._stop();
3170
3171                 if (this._loaded && !options.reset && options !== true) {
3172
3173                         if (options.animate !== undefined) {
3174                                 options.zoom = extend({animate: options.animate}, options.zoom);
3175                                 options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
3176                         }
3177
3178                         // try animating pan or zoom
3179                         var moved = (this._zoom !== zoom) ?
3180                                 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
3181                                 this._tryAnimatedPan(center, options.pan);
3182
3183                         if (moved) {
3184                                 // prevent resize handler call, the view will refresh after animation anyway
3185                                 clearTimeout(this._sizeTimer);
3186                                 return this;
3187                         }
3188                 }
3189
3190                 // animation didn't start, just reset the map view
3191                 this._resetView(center, zoom);
3192
3193                 return this;
3194         },
3195
3196         // @method setZoom(zoom: Number, options?: Zoom/pan options): this
3197         // Sets the zoom of the map.
3198         setZoom: function (zoom, options) {
3199                 if (!this._loaded) {
3200                         this._zoom = zoom;
3201                         return this;
3202                 }
3203                 return this.setView(this.getCenter(), zoom, {zoom: options});
3204         },
3205
3206         // @method zoomIn(delta?: Number, options?: Zoom options): this
3207         // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3208         zoomIn: function (delta, options) {
3209                 delta = delta || (any3d ? this.options.zoomDelta : 1);
3210                 return this.setZoom(this._zoom + delta, options);
3211         },
3212
3213         // @method zoomOut(delta?: Number, options?: Zoom options): this
3214         // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3215         zoomOut: function (delta, options) {
3216                 delta = delta || (any3d ? this.options.zoomDelta : 1);
3217                 return this.setZoom(this._zoom - delta, options);
3218         },
3219
3220         // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
3221         // Zooms the map while keeping a specified geographical point on the map
3222         // stationary (e.g. used internally for scroll zoom and double-click zoom).
3223         // @alternative
3224         // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
3225         // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
3226         setZoomAround: function (latlng, zoom, options) {
3227                 var scale = this.getZoomScale(zoom),
3228                     viewHalf = this.getSize().divideBy(2),
3229                     containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
3230
3231                     centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
3232                     newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
3233
3234                 return this.setView(newCenter, zoom, {zoom: options});
3235         },
3236
3237         _getBoundsCenterZoom: function (bounds, options) {
3238
3239                 options = options || {};
3240                 bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
3241
3242                 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3243                     paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3244
3245                     zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
3246
3247                 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
3248
3249                 if (zoom === Infinity) {
3250                         return {
3251                                 center: bounds.getCenter(),
3252                                 zoom: zoom
3253                         };
3254                 }
3255
3256                 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
3257
3258                     swPoint = this.project(bounds.getSouthWest(), zoom),
3259                     nePoint = this.project(bounds.getNorthEast(), zoom),
3260                     center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
3261
3262                 return {
3263                         center: center,
3264                         zoom: zoom
3265                 };
3266         },
3267
3268         // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
3269         // Sets a map view that contains the given geographical bounds with the
3270         // maximum zoom level possible.
3271         fitBounds: function (bounds, options) {
3272
3273                 bounds = toLatLngBounds(bounds);
3274
3275                 if (!bounds.isValid()) {
3276                         throw new Error('Bounds are not valid.');
3277                 }
3278
3279                 var target = this._getBoundsCenterZoom(bounds, options);
3280                 return this.setView(target.center, target.zoom, options);
3281         },
3282
3283         // @method fitWorld(options?: fitBounds options): this
3284         // Sets a map view that mostly contains the whole world with the maximum
3285         // zoom level possible.
3286         fitWorld: function (options) {
3287                 return this.fitBounds([[-90, -180], [90, 180]], options);
3288         },
3289
3290         // @method panTo(latlng: LatLng, options?: Pan options): this
3291         // Pans the map to a given center.
3292         panTo: function (center, options) { // (LatLng)
3293                 return this.setView(center, this._zoom, {pan: options});
3294         },
3295
3296         // @method panBy(offset: Point, options?: Pan options): this
3297         // Pans the map by a given number of pixels (animated).
3298         panBy: function (offset, options) {
3299                 offset = toPoint(offset).round();
3300                 options = options || {};
3301
3302                 if (!offset.x && !offset.y) {
3303                         return this.fire('moveend');
3304                 }
3305                 // If we pan too far, Chrome gets issues with tiles
3306                 // and makes them disappear or appear in the wrong place (slightly offset) #2602
3307                 if (options.animate !== true && !this.getSize().contains(offset)) {
3308                         this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
3309                         return this;
3310                 }
3311
3312                 if (!this._panAnim) {
3313                         this._panAnim = new PosAnimation();
3314
3315                         this._panAnim.on({
3316                                 'step': this._onPanTransitionStep,
3317                                 'end': this._onPanTransitionEnd
3318                         }, this);
3319                 }
3320
3321                 // don't fire movestart if animating inertia
3322                 if (!options.noMoveStart) {
3323                         this.fire('movestart');
3324                 }
3325
3326                 // animate pan unless animate: false specified
3327                 if (options.animate !== false) {
3328                         addClass(this._mapPane, 'leaflet-pan-anim');
3329
3330                         var newPos = this._getMapPanePos().subtract(offset).round();
3331                         this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
3332                 } else {
3333                         this._rawPanBy(offset);
3334                         this.fire('move').fire('moveend');
3335                 }
3336
3337                 return this;
3338         },
3339
3340         // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
3341         // Sets the view of the map (geographical center and zoom) performing a smooth
3342         // pan-zoom animation.
3343         flyTo: function (targetCenter, targetZoom, options) {
3344
3345                 options = options || {};
3346                 if (options.animate === false || !any3d) {
3347                         return this.setView(targetCenter, targetZoom, options);
3348                 }
3349
3350                 this._stop();
3351
3352                 var from = this.project(this.getCenter()),
3353                     to = this.project(targetCenter),
3354                     size = this.getSize(),
3355                     startZoom = this._zoom;
3356
3357                 targetCenter = toLatLng(targetCenter);
3358                 targetZoom = targetZoom === undefined ? startZoom : targetZoom;
3359
3360                 var w0 = Math.max(size.x, size.y),
3361                     w1 = w0 * this.getZoomScale(startZoom, targetZoom),
3362                     u1 = (to.distanceTo(from)) || 1,
3363                     rho = 1.42,
3364                     rho2 = rho * rho;
3365
3366                 function r(i) {
3367                         var s1 = i ? -1 : 1,
3368                             s2 = i ? w1 : w0,
3369                             t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
3370                             b1 = 2 * s2 * rho2 * u1,
3371                             b = t1 / b1,
3372                             sq = Math.sqrt(b * b + 1) - b;
3373
3374                             // workaround for floating point precision bug when sq = 0, log = -Infinite,
3375                             // thus triggering an infinite loop in flyTo
3376                             var log = sq < 0.000000001 ? -18 : Math.log(sq);
3377
3378                         return log;
3379                 }
3380
3381                 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
3382                 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
3383                 function tanh(n) { return sinh(n) / cosh(n); }
3384
3385                 var r0 = r(0);
3386
3387                 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
3388                 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
3389
3390                 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
3391
3392                 var start = Date.now(),
3393                     S = (r(1) - r0) / rho,
3394                     duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
3395
3396                 function frame() {
3397                         var t = (Date.now() - start) / duration,
3398                             s = easeOut(t) * S;
3399
3400                         if (t <= 1) {
3401                                 this._flyToFrame = requestAnimFrame(frame, this);
3402
3403                                 this._move(
3404                                         this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
3405                                         this.getScaleZoom(w0 / w(s), startZoom),
3406                                         {flyTo: true});
3407
3408                         } else {
3409                                 this
3410                                         ._move(targetCenter, targetZoom)
3411                                         ._moveEnd(true);
3412                         }
3413                 }
3414
3415                 this._moveStart(true, options.noMoveStart);
3416
3417                 frame.call(this);
3418                 return this;
3419         },
3420
3421         // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
3422         // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
3423         // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
3424         flyToBounds: function (bounds, options) {
3425                 var target = this._getBoundsCenterZoom(bounds, options);
3426                 return this.flyTo(target.center, target.zoom, options);
3427         },
3428
3429         // @method setMaxBounds(bounds: Bounds): this
3430         // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
3431         setMaxBounds: function (bounds) {
3432                 bounds = toLatLngBounds(bounds);
3433
3434                 if (!bounds.isValid()) {
3435                         this.options.maxBounds = null;
3436                         return this.off('moveend', this._panInsideMaxBounds);
3437                 } else if (this.options.maxBounds) {
3438                         this.off('moveend', this._panInsideMaxBounds);
3439                 }
3440
3441                 this.options.maxBounds = bounds;
3442
3443                 if (this._loaded) {
3444                         this._panInsideMaxBounds();
3445                 }
3446
3447                 return this.on('moveend', this._panInsideMaxBounds);
3448         },
3449
3450         // @method setMinZoom(zoom: Number): this
3451         // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
3452         setMinZoom: function (zoom) {
3453                 var oldZoom = this.options.minZoom;
3454                 this.options.minZoom = zoom;
3455
3456                 if (this._loaded && oldZoom !== zoom) {
3457                         this.fire('zoomlevelschange');
3458
3459                         if (this.getZoom() < this.options.minZoom) {
3460                                 return this.setZoom(zoom);
3461                         }
3462                 }
3463
3464                 return this;
3465         },
3466
3467         // @method setMaxZoom(zoom: Number): this
3468         // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
3469         setMaxZoom: function (zoom) {
3470                 var oldZoom = this.options.maxZoom;
3471                 this.options.maxZoom = zoom;
3472
3473                 if (this._loaded && oldZoom !== zoom) {
3474                         this.fire('zoomlevelschange');
3475
3476                         if (this.getZoom() > this.options.maxZoom) {
3477                                 return this.setZoom(zoom);
3478                         }
3479                 }
3480
3481                 return this;
3482         },
3483
3484         // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
3485         // 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.
3486         panInsideBounds: function (bounds, options) {
3487                 this._enforcingBounds = true;
3488                 var center = this.getCenter(),
3489                     newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
3490
3491                 if (!center.equals(newCenter)) {
3492                         this.panTo(newCenter, options);
3493                 }
3494
3495                 this._enforcingBounds = false;
3496                 return this;
3497         },
3498
3499         // @method invalidateSize(options: Zoom/pan options): this
3500         // Checks if the map container size changed and updates the map if so —
3501         // call it after you've changed the map size dynamically, also animating
3502         // pan by default. If `options.pan` is `false`, panning will not occur.
3503         // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
3504         // that it doesn't happen often even if the method is called many
3505         // times in a row.
3506
3507         // @alternative
3508         // @method invalidateSize(animate: Boolean): this
3509         // Checks if the map container size changed and updates the map if so —
3510         // call it after you've changed the map size dynamically, also animating
3511         // pan by default.
3512         invalidateSize: function (options) {
3513                 if (!this._loaded) { return this; }
3514
3515                 options = extend({
3516                         animate: false,
3517                         pan: true
3518                 }, options === true ? {animate: true} : options);
3519
3520                 var oldSize = this.getSize();
3521                 this._sizeChanged = true;
3522                 this._lastCenter = null;
3523
3524                 var newSize = this.getSize(),
3525                     oldCenter = oldSize.divideBy(2).round(),
3526                     newCenter = newSize.divideBy(2).round(),
3527                     offset = oldCenter.subtract(newCenter);
3528
3529                 if (!offset.x && !offset.y) { return this; }
3530
3531                 if (options.animate && options.pan) {
3532                         this.panBy(offset);
3533
3534                 } else {
3535                         if (options.pan) {
3536                                 this._rawPanBy(offset);
3537                         }
3538
3539                         this.fire('move');
3540
3541                         if (options.debounceMoveend) {
3542                                 clearTimeout(this._sizeTimer);
3543                                 this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
3544                         } else {
3545                                 this.fire('moveend');
3546                         }
3547                 }
3548
3549                 // @section Map state change events
3550                 // @event resize: ResizeEvent
3551                 // Fired when the map is resized.
3552                 return this.fire('resize', {
3553                         oldSize: oldSize,
3554                         newSize: newSize
3555                 });
3556         },
3557
3558         // @section Methods for modifying map state
3559         // @method stop(): this
3560         // Stops the currently running `panTo` or `flyTo` animation, if any.
3561         stop: function () {
3562                 this.setZoom(this._limitZoom(this._zoom));
3563                 if (!this.options.zoomSnap) {
3564                         this.fire('viewreset');
3565                 }
3566                 return this._stop();
3567         },
3568
3569         // @section Geolocation methods
3570         // @method locate(options?: Locate options): this
3571         // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
3572         // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
3573         // and optionally sets the map view to the user's location with respect to
3574         // detection accuracy (or to the world view if geolocation failed).
3575         // Note that, if your page doesn't use HTTPS, this method will fail in
3576         // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
3577         // See `Locate options` for more details.
3578         locate: function (options) {
3579
3580                 options = this._locateOptions = extend({
3581                         timeout: 10000,
3582                         watch: false
3583                         // setView: false
3584                         // maxZoom: <Number>
3585                         // maximumAge: 0
3586                         // enableHighAccuracy: false
3587                 }, options);
3588
3589                 if (!('geolocation' in navigator)) {
3590                         this._handleGeolocationError({
3591                                 code: 0,
3592                                 message: 'Geolocation not supported.'
3593                         });
3594                         return this;
3595                 }
3596
3597                 var onResponse = bind(this._handleGeolocationResponse, this),
3598                     onError = bind(this._handleGeolocationError, this);
3599
3600                 if (options.watch) {
3601                         this._locationWatchId =
3602                                 navigator.geolocation.watchPosition(onResponse, onError, options);
3603                 } else {
3604                         navigator.geolocation.getCurrentPosition(onResponse, onError, options);
3605                 }
3606                 return this;
3607         },
3608
3609         // @method stopLocate(): this
3610         // Stops watching location previously initiated by `map.locate({watch: true})`
3611         // and aborts resetting the map view if map.locate was called with
3612         // `{setView: true}`.
3613         stopLocate: function () {
3614                 if (navigator.geolocation && navigator.geolocation.clearWatch) {
3615                         navigator.geolocation.clearWatch(this._locationWatchId);
3616                 }
3617                 if (this._locateOptions) {
3618                         this._locateOptions.setView = false;
3619                 }
3620                 return this;
3621         },
3622
3623         _handleGeolocationError: function (error) {
3624                 var c = error.code,
3625                     message = error.message ||
3626                             (c === 1 ? 'permission denied' :
3627                             (c === 2 ? 'position unavailable' : 'timeout'));
3628
3629                 if (this._locateOptions.setView && !this._loaded) {
3630                         this.fitWorld();
3631                 }
3632
3633                 // @section Location events
3634                 // @event locationerror: ErrorEvent
3635                 // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
3636                 this.fire('locationerror', {
3637                         code: c,
3638                         message: 'Geolocation error: ' + message + '.'
3639                 });
3640         },
3641
3642         _handleGeolocationResponse: function (pos) {
3643                 var lat = pos.coords.latitude,
3644                     lng = pos.coords.longitude,
3645                     latlng = new LatLng(lat, lng),
3646                     bounds = latlng.toBounds(pos.coords.accuracy * 2),
3647                     options = this._locateOptions;
3648
3649                 if (options.setView) {
3650                         var zoom = this.getBoundsZoom(bounds);
3651                         this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
3652                 }
3653
3654                 var data = {
3655                         latlng: latlng,
3656                         bounds: bounds,
3657                         timestamp: pos.timestamp
3658                 };
3659
3660                 for (var i in pos.coords) {
3661                         if (typeof pos.coords[i] === 'number') {
3662                                 data[i] = pos.coords[i];
3663                         }
3664                 }
3665
3666                 // @event locationfound: LocationEvent
3667                 // Fired when geolocation (using the [`locate`](#map-locate) method)
3668                 // went successfully.
3669                 this.fire('locationfound', data);
3670         },
3671
3672         // TODO Appropriate docs section?
3673         // @section Other Methods
3674         // @method addHandler(name: String, HandlerClass: Function): this
3675         // Adds a new `Handler` to the map, given its name and constructor function.
3676         addHandler: function (name, HandlerClass) {
3677                 if (!HandlerClass) { return this; }
3678
3679                 var handler = this[name] = new HandlerClass(this);
3680
3681                 this._handlers.push(handler);
3682
3683                 if (this.options[name]) {
3684                         handler.enable();
3685                 }
3686
3687                 return this;
3688         },
3689
3690         // @method remove(): this
3691         // Destroys the map and clears all related event listeners.
3692         remove: function () {
3693
3694                 this._initEvents(true);
3695
3696                 if (this._containerId !== this._container._leaflet_id) {
3697                         throw new Error('Map container is being reused by another instance');
3698                 }
3699
3700                 try {
3701                         // throws error in IE6-8
3702                         delete this._container._leaflet_id;
3703                         delete this._containerId;
3704                 } catch (e) {
3705                         /*eslint-disable */
3706                         this._container._leaflet_id = undefined;
3707                         /* eslint-enable */
3708                         this._containerId = undefined;
3709                 }
3710
3711                 if (this._locationWatchId !== undefined) {
3712                         this.stopLocate();
3713                 }
3714
3715                 this._stop();
3716
3717                 remove(this._mapPane);
3718
3719                 if (this._clearControlPos) {
3720                         this._clearControlPos();
3721                 }
3722                 if (this._resizeRequest) {
3723                         cancelAnimFrame(this._resizeRequest);
3724                         this._resizeRequest = null;
3725                 }
3726
3727                 this._clearHandlers();
3728
3729                 if (this._loaded) {
3730                         // @section Map state change events
3731                         // @event unload: Event
3732                         // Fired when the map is destroyed with [remove](#map-remove) method.
3733                         this.fire('unload');
3734                 }
3735
3736                 var i;
3737                 for (i in this._layers) {
3738                         this._layers[i].remove();
3739                 }
3740                 for (i in this._panes) {
3741                         remove(this._panes[i]);
3742                 }
3743
3744                 this._layers = [];
3745                 this._panes = [];
3746                 delete this._mapPane;
3747                 delete this._renderer;
3748
3749                 return this;
3750         },
3751
3752         // @section Other Methods
3753         // @method createPane(name: String, container?: HTMLElement): HTMLElement
3754         // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
3755         // then returns it. The pane is created as a child of `container`, or
3756         // as a child of the main map pane if not set.
3757         createPane: function (name, container) {
3758                 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
3759                     pane = create$1('div', className, container || this._mapPane);
3760
3761                 if (name) {
3762                         this._panes[name] = pane;
3763                 }
3764                 return pane;
3765         },
3766
3767         // @section Methods for Getting Map State
3768
3769         // @method getCenter(): LatLng
3770         // Returns the geographical center of the map view
3771         getCenter: function () {
3772                 this._checkIfLoaded();
3773
3774                 if (this._lastCenter && !this._moved()) {
3775                         return this._lastCenter;
3776                 }
3777                 return this.layerPointToLatLng(this._getCenterLayerPoint());
3778         },
3779
3780         // @method getZoom(): Number
3781         // Returns the current zoom level of the map view
3782         getZoom: function () {
3783                 return this._zoom;
3784         },
3785
3786         // @method getBounds(): LatLngBounds
3787         // Returns the geographical bounds visible in the current map view
3788         getBounds: function () {
3789                 var bounds = this.getPixelBounds(),
3790                     sw = this.unproject(bounds.getBottomLeft()),
3791                     ne = this.unproject(bounds.getTopRight());
3792
3793                 return new LatLngBounds(sw, ne);
3794         },
3795
3796         // @method getMinZoom(): Number
3797         // 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.
3798         getMinZoom: function () {
3799                 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
3800         },
3801
3802         // @method getMaxZoom(): Number
3803         // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
3804         getMaxZoom: function () {
3805                 return this.options.maxZoom === undefined ?
3806                         (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
3807                         this.options.maxZoom;
3808         },
3809
3810         // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number
3811         // Returns the maximum zoom level on which the given bounds fit to the map
3812         // view in its entirety. If `inside` (optional) is set to `true`, the method
3813         // instead returns the minimum zoom level on which the map view fits into
3814         // the given bounds in its entirety.
3815         getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
3816                 bounds = toLatLngBounds(bounds);
3817                 padding = toPoint(padding || [0, 0]);
3818
3819                 var zoom = this.getZoom() || 0,
3820                     min = this.getMinZoom(),
3821                     max = this.getMaxZoom(),
3822                     nw = bounds.getNorthWest(),
3823                     se = bounds.getSouthEast(),
3824                     size = this.getSize().subtract(padding),
3825                     boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
3826                     snap = any3d ? this.options.zoomSnap : 1,
3827                     scalex = size.x / boundsSize.x,
3828                     scaley = size.y / boundsSize.y,
3829                     scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
3830
3831                 zoom = this.getScaleZoom(scale, zoom);
3832
3833                 if (snap) {
3834                         zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
3835                         zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
3836                 }
3837
3838                 return Math.max(min, Math.min(max, zoom));
3839         },
3840
3841         // @method getSize(): Point
3842         // Returns the current size of the map container (in pixels).
3843         getSize: function () {
3844                 if (!this._size || this._sizeChanged) {
3845                         this._size = new Point(
3846                                 this._container.clientWidth || 0,
3847                                 this._container.clientHeight || 0);
3848
3849                         this._sizeChanged = false;
3850                 }
3851                 return this._size.clone();
3852         },
3853
3854         // @method getPixelBounds(): Bounds
3855         // Returns the bounds of the current map view in projected pixel
3856         // coordinates (sometimes useful in layer and overlay implementations).
3857         getPixelBounds: function (center, zoom) {
3858                 var topLeftPoint = this._getTopLeftPoint(center, zoom);
3859                 return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
3860         },
3861
3862         // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
3863         // the map pane? "left point of the map layer" can be confusing, specially
3864         // since there can be negative offsets.
3865         // @method getPixelOrigin(): Point
3866         // Returns the projected pixel coordinates of the top left point of
3867         // the map layer (useful in custom layer and overlay implementations).
3868         getPixelOrigin: function () {
3869                 this._checkIfLoaded();
3870                 return this._pixelOrigin;
3871         },
3872
3873         // @method getPixelWorldBounds(zoom?: Number): Bounds
3874         // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
3875         // If `zoom` is omitted, the map's current zoom level is used.
3876         getPixelWorldBounds: function (zoom) {
3877                 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
3878         },
3879
3880         // @section Other Methods
3881
3882         // @method getPane(pane: String|HTMLElement): HTMLElement
3883         // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
3884         getPane: function (pane) {
3885                 return typeof pane === 'string' ? this._panes[pane] : pane;
3886         },
3887
3888         // @method getPanes(): Object
3889         // Returns a plain object containing the names of all [panes](#map-pane) as keys and
3890         // the panes as values.
3891         getPanes: function () {
3892                 return this._panes;
3893         },
3894
3895         // @method getContainer: HTMLElement
3896         // Returns the HTML element that contains the map.
3897         getContainer: function () {
3898                 return this._container;
3899         },
3900
3901
3902         // @section Conversion Methods
3903
3904         // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
3905         // Returns the scale factor to be applied to a map transition from zoom level
3906         // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
3907         getZoomScale: function (toZoom, fromZoom) {
3908                 // TODO replace with universal implementation after refactoring projections
3909                 var crs = this.options.crs;
3910                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3911                 return crs.scale(toZoom) / crs.scale(fromZoom);
3912         },
3913
3914         // @method getScaleZoom(scale: Number, fromZoom: Number): Number
3915         // Returns the zoom level that the map would end up at, if it is at `fromZoom`
3916         // level and everything is scaled by a factor of `scale`. Inverse of
3917         // [`getZoomScale`](#map-getZoomScale).
3918         getScaleZoom: function (scale, fromZoom) {
3919                 var crs = this.options.crs;
3920                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3921                 var zoom = crs.zoom(scale * crs.scale(fromZoom));
3922                 return isNaN(zoom) ? Infinity : zoom;
3923         },
3924
3925         // @method project(latlng: LatLng, zoom: Number): Point
3926         // Projects a geographical coordinate `LatLng` according to the projection
3927         // of the map's CRS, then scales it according to `zoom` and the CRS's
3928         // `Transformation`. The result is pixel coordinate relative to
3929         // the CRS origin.
3930         project: function (latlng, zoom) {
3931                 zoom = zoom === undefined ? this._zoom : zoom;
3932                 return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
3933         },
3934
3935         // @method unproject(point: Point, zoom: Number): LatLng
3936         // Inverse of [`project`](#map-project).
3937         unproject: function (point, zoom) {
3938                 zoom = zoom === undefined ? this._zoom : zoom;
3939                 return this.options.crs.pointToLatLng(toPoint(point), zoom);
3940         },
3941
3942         // @method layerPointToLatLng(point: Point): LatLng
3943         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3944         // returns the corresponding geographical coordinate (for the current zoom level).
3945         layerPointToLatLng: function (point) {
3946                 var projectedPoint = toPoint(point).add(this.getPixelOrigin());
3947                 return this.unproject(projectedPoint);
3948         },
3949
3950         // @method latLngToLayerPoint(latlng: LatLng): Point
3951         // Given a geographical coordinate, returns the corresponding pixel coordinate
3952         // relative to the [origin pixel](#map-getpixelorigin).
3953         latLngToLayerPoint: function (latlng) {
3954                 var projectedPoint = this.project(toLatLng(latlng))._round();
3955                 return projectedPoint._subtract(this.getPixelOrigin());
3956         },
3957
3958         // @method wrapLatLng(latlng: LatLng): LatLng
3959         // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
3960         // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
3961         // CRS's bounds.
3962         // By default this means longitude is wrapped around the dateline so its
3963         // value is between -180 and +180 degrees.
3964         wrapLatLng: function (latlng) {
3965                 return this.options.crs.wrapLatLng(toLatLng(latlng));
3966         },
3967
3968         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
3969         // Returns a `LatLngBounds` with the same size as the given one, ensuring that
3970         // its center is within the CRS's bounds.
3971         // By default this means the center longitude is wrapped around the dateline so its
3972         // value is between -180 and +180 degrees, and the majority of the bounds
3973         // overlaps the CRS's bounds.
3974         wrapLatLngBounds: function (latlng) {
3975                 return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
3976         },
3977
3978         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
3979         // Returns the distance between two geographical coordinates according to
3980         // the map's CRS. By default this measures distance in meters.
3981         distance: function (latlng1, latlng2) {
3982                 return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
3983         },
3984
3985         // @method containerPointToLayerPoint(point: Point): Point
3986         // Given a pixel coordinate relative to the map container, returns the corresponding
3987         // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
3988         containerPointToLayerPoint: function (point) { // (Point)
3989                 return toPoint(point).subtract(this._getMapPanePos());
3990         },
3991
3992         // @method layerPointToContainerPoint(point: Point): Point
3993         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3994         // returns the corresponding pixel coordinate relative to the map container.
3995         layerPointToContainerPoint: function (point) { // (Point)
3996                 return toPoint(point).add(this._getMapPanePos());
3997         },
3998
3999         // @method containerPointToLatLng(point: Point): LatLng
4000         // Given a pixel coordinate relative to the map container, returns
4001         // the corresponding geographical coordinate (for the current zoom level).
4002         containerPointToLatLng: function (point) {
4003                 var layerPoint = this.containerPointToLayerPoint(toPoint(point));
4004                 return this.layerPointToLatLng(layerPoint);
4005         },
4006
4007         // @method latLngToContainerPoint(latlng: LatLng): Point
4008         // Given a geographical coordinate, returns the corresponding pixel coordinate
4009         // relative to the map container.
4010         latLngToContainerPoint: function (latlng) {
4011                 return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
4012         },
4013
4014         // @method mouseEventToContainerPoint(ev: MouseEvent): Point
4015         // Given a MouseEvent object, returns the pixel coordinate relative to the
4016         // map container where the event took place.
4017         mouseEventToContainerPoint: function (e) {
4018                 return getMousePosition(e, this._container);
4019         },
4020
4021         // @method mouseEventToLayerPoint(ev: MouseEvent): Point
4022         // Given a MouseEvent object, returns the pixel coordinate relative to
4023         // the [origin pixel](#map-getpixelorigin) where the event took place.
4024         mouseEventToLayerPoint: function (e) {
4025                 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
4026         },
4027
4028         // @method mouseEventToLatLng(ev: MouseEvent): LatLng
4029         // Given a MouseEvent object, returns geographical coordinate where the
4030         // event took place.
4031         mouseEventToLatLng: function (e) { // (MouseEvent)
4032                 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
4033         },
4034
4035
4036         // map initialization methods
4037
4038         _initContainer: function (id) {
4039                 var container = this._container = get(id);
4040
4041                 if (!container) {
4042                         throw new Error('Map container not found.');
4043                 } else if (container._leaflet_id) {
4044                         throw new Error('Map container is already initialized.');
4045                 }
4046
4047                 on(container, 'scroll', this._onScroll, this);
4048                 this._containerId = stamp(container);
4049         },
4050
4051         _initLayout: function () {
4052                 var container = this._container;
4053
4054                 this._fadeAnimated = this.options.fadeAnimation && any3d;
4055
4056                 addClass(container, 'leaflet-container' +
4057                         (touch ? ' leaflet-touch' : '') +
4058                         (retina ? ' leaflet-retina' : '') +
4059                         (ielt9 ? ' leaflet-oldie' : '') +
4060                         (safari ? ' leaflet-safari' : '') +
4061                         (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
4062
4063                 var position = getStyle(container, 'position');
4064
4065                 if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
4066                         container.style.position = 'relative';
4067                 }
4068
4069                 this._initPanes();
4070
4071                 if (this._initControlPos) {
4072                         this._initControlPos();
4073                 }
4074         },
4075
4076         _initPanes: function () {
4077                 var panes = this._panes = {};
4078                 this._paneRenderers = {};
4079
4080                 // @section
4081                 //
4082                 // Panes are DOM elements used to control the ordering of layers on the map. You
4083                 // can access panes with [`map.getPane`](#map-getpane) or
4084                 // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
4085                 // [`map.createPane`](#map-createpane) method.
4086                 //
4087                 // Every map has the following default panes that differ only in zIndex.
4088                 //
4089                 // @pane mapPane: HTMLElement = 'auto'
4090                 // Pane that contains all other map panes
4091
4092                 this._mapPane = this.createPane('mapPane', this._container);
4093                 setPosition(this._mapPane, new Point(0, 0));
4094
4095                 // @pane tilePane: HTMLElement = 200
4096                 // Pane for `GridLayer`s and `TileLayer`s
4097                 this.createPane('tilePane');
4098                 // @pane overlayPane: HTMLElement = 400
4099                 // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s
4100                 this.createPane('shadowPane');
4101                 // @pane shadowPane: HTMLElement = 500
4102                 // Pane for overlay shadows (e.g. `Marker` shadows)
4103                 this.createPane('overlayPane');
4104                 // @pane markerPane: HTMLElement = 600
4105                 // Pane for `Icon`s of `Marker`s
4106                 this.createPane('markerPane');
4107                 // @pane tooltipPane: HTMLElement = 650
4108                 // Pane for `Tooltip`s.
4109                 this.createPane('tooltipPane');
4110                 // @pane popupPane: HTMLElement = 700
4111                 // Pane for `Popup`s.
4112                 this.createPane('popupPane');
4113
4114                 if (!this.options.markerZoomAnimation) {
4115                         addClass(panes.markerPane, 'leaflet-zoom-hide');
4116                         addClass(panes.shadowPane, 'leaflet-zoom-hide');
4117                 }
4118         },
4119
4120
4121         // private methods that modify map state
4122
4123         // @section Map state change events
4124         _resetView: function (center, zoom) {
4125                 setPosition(this._mapPane, new Point(0, 0));
4126
4127                 var loading = !this._loaded;
4128                 this._loaded = true;
4129                 zoom = this._limitZoom(zoom);
4130
4131                 this.fire('viewprereset');
4132
4133                 var zoomChanged = this._zoom !== zoom;
4134                 this
4135                         ._moveStart(zoomChanged, false)
4136                         ._move(center, zoom)
4137                         ._moveEnd(zoomChanged);
4138
4139                 // @event viewreset: Event
4140                 // Fired when the map needs to redraw its content (this usually happens
4141                 // on map zoom or load). Very useful for creating custom overlays.
4142                 this.fire('viewreset');
4143
4144                 // @event load: Event
4145                 // Fired when the map is initialized (when its center and zoom are set
4146                 // for the first time).
4147                 if (loading) {
4148                         this.fire('load');
4149                 }
4150         },
4151
4152         _moveStart: function (zoomChanged, noMoveStart) {
4153                 // @event zoomstart: Event
4154                 // Fired when the map zoom is about to change (e.g. before zoom animation).
4155                 // @event movestart: Event
4156                 // Fired when the view of the map starts changing (e.g. user starts dragging the map).
4157                 if (zoomChanged) {
4158                         this.fire('zoomstart');
4159                 }
4160                 if (!noMoveStart) {
4161                         this.fire('movestart');
4162                 }
4163                 return this;
4164         },
4165
4166         _move: function (center, zoom, data) {
4167                 if (zoom === undefined) {
4168                         zoom = this._zoom;
4169                 }
4170                 var zoomChanged = this._zoom !== zoom;
4171
4172                 this._zoom = zoom;
4173                 this._lastCenter = center;
4174                 this._pixelOrigin = this._getNewPixelOrigin(center);
4175
4176                 // @event zoom: Event
4177                 // Fired repeatedly during any change in zoom level, including zoom
4178                 // and fly animations.
4179                 if (zoomChanged || (data && data.pinch)) {      // Always fire 'zoom' if pinching because #3530
4180                         this.fire('zoom', data);
4181                 }
4182
4183                 // @event move: Event
4184                 // Fired repeatedly during any movement of the map, including pan and
4185                 // fly animations.
4186                 return this.fire('move', data);
4187         },
4188
4189         _moveEnd: function (zoomChanged) {
4190                 // @event zoomend: Event
4191                 // Fired when the map has changed, after any animations.
4192                 if (zoomChanged) {
4193                         this.fire('zoomend');
4194                 }
4195
4196                 // @event moveend: Event
4197                 // Fired when the center of the map stops changing (e.g. user stopped
4198                 // dragging the map).
4199                 return this.fire('moveend');
4200         },
4201
4202         _stop: function () {
4203                 cancelAnimFrame(this._flyToFrame);
4204                 if (this._panAnim) {
4205                         this._panAnim.stop();
4206                 }
4207                 return this;
4208         },
4209
4210         _rawPanBy: function (offset) {
4211                 setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
4212         },
4213
4214         _getZoomSpan: function () {
4215                 return this.getMaxZoom() - this.getMinZoom();
4216         },
4217
4218         _panInsideMaxBounds: function () {
4219                 if (!this._enforcingBounds) {
4220                         this.panInsideBounds(this.options.maxBounds);
4221                 }
4222         },
4223
4224         _checkIfLoaded: function () {
4225                 if (!this._loaded) {
4226                         throw new Error('Set map center and zoom first.');
4227                 }
4228         },
4229
4230         // DOM event handling
4231
4232         // @section Interaction events
4233         _initEvents: function (remove$$1) {
4234                 this._targets = {};
4235                 this._targets[stamp(this._container)] = this;
4236
4237                 var onOff = remove$$1 ? off : on;
4238
4239                 // @event click: MouseEvent
4240                 // Fired when the user clicks (or taps) the map.
4241                 // @event dblclick: MouseEvent
4242                 // Fired when the user double-clicks (or double-taps) the map.
4243                 // @event mousedown: MouseEvent
4244                 // Fired when the user pushes the mouse button on the map.
4245                 // @event mouseup: MouseEvent
4246                 // Fired when the user releases the mouse button on the map.
4247                 // @event mouseover: MouseEvent
4248                 // Fired when the mouse enters the map.
4249                 // @event mouseout: MouseEvent
4250                 // Fired when the mouse leaves the map.
4251                 // @event mousemove: MouseEvent
4252                 // Fired while the mouse moves over the map.
4253                 // @event contextmenu: MouseEvent
4254                 // Fired when the user pushes the right mouse button on the map, prevents
4255                 // default browser context menu from showing if there are listeners on
4256                 // this event. Also fired on mobile when the user holds a single touch
4257                 // for a second (also called long press).
4258                 // @event keypress: KeyboardEvent
4259                 // Fired when the user presses a key from the keyboard while the map is focused.
4260                 onOff(this._container, 'click dblclick mousedown mouseup ' +
4261                         'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
4262
4263                 if (this.options.trackResize) {
4264                         onOff(window, 'resize', this._onResize, this);
4265                 }
4266
4267                 if (any3d && this.options.transform3DLimit) {
4268                         (remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
4269                 }
4270         },
4271
4272         _onResize: function () {
4273                 cancelAnimFrame(this._resizeRequest);
4274                 this._resizeRequest = requestAnimFrame(
4275                         function () { this.invalidateSize({debounceMoveend: true}); }, this);
4276         },
4277
4278         _onScroll: function () {
4279                 this._container.scrollTop  = 0;
4280                 this._container.scrollLeft = 0;
4281         },
4282
4283         _onMoveEnd: function () {
4284                 var pos = this._getMapPanePos();
4285                 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
4286                         // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
4287                         // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
4288                         this._resetView(this.getCenter(), this.getZoom());
4289                 }
4290         },
4291
4292         _findEventTargets: function (e, type) {
4293                 var targets = [],
4294                     target,
4295                     isHover = type === 'mouseout' || type === 'mouseover',
4296                     src = e.target || e.srcElement,
4297                     dragging = false;
4298
4299                 while (src) {
4300                         target = this._targets[stamp(src)];
4301                         if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
4302                                 // Prevent firing click after you just dragged an object.
4303                                 dragging = true;
4304                                 break;
4305                         }
4306                         if (target && target.listens(type, true)) {
4307                                 if (isHover && !isExternalTarget(src, e)) { break; }
4308                                 targets.push(target);
4309                                 if (isHover) { break; }
4310                         }
4311                         if (src === this._container) { break; }
4312                         src = src.parentNode;
4313                 }
4314                 if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) {
4315                         targets = [this];
4316                 }
4317                 return targets;
4318         },
4319
4320         _handleDOMEvent: function (e) {
4321                 if (!this._loaded || skipped(e)) { return; }
4322
4323                 var type = e.type;
4324
4325                 if (type === 'mousedown' || type === 'keypress') {
4326                         // prevents outline when clicking on keyboard-focusable element
4327                         preventOutline(e.target || e.srcElement);
4328                 }
4329
4330                 this._fireDOMEvent(e, type);
4331         },
4332
4333         _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
4334
4335         _fireDOMEvent: function (e, type, targets) {
4336
4337                 if (e.type === 'click') {
4338                         // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
4339                         // @event preclick: MouseEvent
4340                         // Fired before mouse click on the map (sometimes useful when you
4341                         // want something to happen on click before any existing click
4342                         // handlers start running).
4343                         var synth = extend({}, e);
4344                         synth.type = 'preclick';
4345                         this._fireDOMEvent(synth, synth.type, targets);
4346                 }
4347
4348                 if (e._stopped) { return; }
4349
4350                 // Find the layer the event is propagating from and its parents.
4351                 targets = (targets || []).concat(this._findEventTargets(e, type));
4352
4353                 if (!targets.length) { return; }
4354
4355                 var target = targets[0];
4356                 if (type === 'contextmenu' && target.listens(type, true)) {
4357                         preventDefault(e);
4358                 }
4359
4360                 var data = {
4361                         originalEvent: e
4362                 };
4363
4364                 if (e.type !== 'keypress') {
4365                         var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
4366                         data.containerPoint = isMarker ?
4367                                 this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
4368                         data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
4369                         data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
4370                 }
4371
4372                 for (var i = 0; i < targets.length; i++) {
4373                         targets[i].fire(type, data, true);
4374                         if (data.originalEvent._stopped ||
4375                                 (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
4376                 }
4377         },
4378
4379         _draggableMoved: function (obj) {
4380                 obj = obj.dragging && obj.dragging.enabled() ? obj : this;
4381                 return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
4382         },
4383
4384         _clearHandlers: function () {
4385                 for (var i = 0, len = this._handlers.length; i < len; i++) {
4386                         this._handlers[i].disable();
4387                 }
4388         },
4389
4390         // @section Other Methods
4391
4392         // @method whenReady(fn: Function, context?: Object): this
4393         // Runs the given function `fn` when the map gets initialized with
4394         // a view (center and zoom) and at least one layer, or immediately
4395         // if it's already initialized, optionally passing a function context.
4396         whenReady: function (callback, context) {
4397                 if (this._loaded) {
4398                         callback.call(context || this, {target: this});
4399                 } else {
4400                         this.on('load', callback, context);
4401                 }
4402                 return this;
4403         },
4404
4405
4406         // private methods for getting map state
4407
4408         _getMapPanePos: function () {
4409                 return getPosition(this._mapPane) || new Point(0, 0);
4410         },
4411
4412         _moved: function () {
4413                 var pos = this._getMapPanePos();
4414                 return pos && !pos.equals([0, 0]);
4415         },
4416
4417         _getTopLeftPoint: function (center, zoom) {
4418                 var pixelOrigin = center && zoom !== undefined ?
4419                         this._getNewPixelOrigin(center, zoom) :
4420                         this.getPixelOrigin();
4421                 return pixelOrigin.subtract(this._getMapPanePos());
4422         },
4423
4424         _getNewPixelOrigin: function (center, zoom) {
4425                 var viewHalf = this.getSize()._divideBy(2);
4426                 return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
4427         },
4428
4429         _latLngToNewLayerPoint: function (latlng, zoom, center) {
4430                 var topLeft = this._getNewPixelOrigin(center, zoom);
4431                 return this.project(latlng, zoom)._subtract(topLeft);
4432         },
4433
4434         _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
4435                 var topLeft = this._getNewPixelOrigin(center, zoom);
4436                 return toBounds([
4437                         this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
4438                         this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
4439                         this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
4440                         this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
4441                 ]);
4442         },
4443
4444         // layer point of the current center
4445         _getCenterLayerPoint: function () {
4446                 return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
4447         },
4448
4449         // offset of the specified place to the current center in pixels
4450         _getCenterOffset: function (latlng) {
4451                 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
4452         },
4453
4454         // adjust center for view to get inside bounds
4455         _limitCenter: function (center, zoom, bounds) {
4456
4457                 if (!bounds) { return center; }
4458
4459                 var centerPoint = this.project(center, zoom),
4460                     viewHalf = this.getSize().divideBy(2),
4461                     viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
4462                     offset = this._getBoundsOffset(viewBounds, bounds, zoom);
4463
4464                 // If offset is less than a pixel, ignore.
4465                 // This prevents unstable projections from getting into
4466                 // an infinite loop of tiny offsets.
4467                 if (offset.round().equals([0, 0])) {
4468                         return center;
4469                 }
4470
4471                 return this.unproject(centerPoint.add(offset), zoom);
4472         },
4473
4474         // adjust offset for view to get inside bounds
4475         _limitOffset: function (offset, bounds) {
4476                 if (!bounds) { return offset; }
4477
4478                 var viewBounds = this.getPixelBounds(),
4479                     newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
4480
4481                 return offset.add(this._getBoundsOffset(newBounds, bounds));
4482         },
4483
4484         // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
4485         _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
4486                 var projectedMaxBounds = toBounds(
4487                         this.project(maxBounds.getNorthEast(), zoom),
4488                         this.project(maxBounds.getSouthWest(), zoom)
4489                     ),
4490                     minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
4491                     maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
4492
4493                     dx = this._rebound(minOffset.x, -maxOffset.x),
4494                     dy = this._rebound(minOffset.y, -maxOffset.y);
4495
4496                 return new Point(dx, dy);
4497         },
4498
4499         _rebound: function (left, right) {
4500                 return left + right > 0 ?
4501                         Math.round(left - right) / 2 :
4502                         Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
4503         },
4504
4505         _limitZoom: function (zoom) {
4506                 var min = this.getMinZoom(),
4507                     max = this.getMaxZoom(),
4508                     snap = any3d ? this.options.zoomSnap : 1;
4509                 if (snap) {
4510                         zoom = Math.round(zoom / snap) * snap;
4511                 }
4512                 return Math.max(min, Math.min(max, zoom));
4513         },
4514
4515         _onPanTransitionStep: function () {
4516                 this.fire('move');
4517         },
4518
4519         _onPanTransitionEnd: function () {
4520                 removeClass(this._mapPane, 'leaflet-pan-anim');
4521                 this.fire('moveend');
4522         },
4523
4524         _tryAnimatedPan: function (center, options) {
4525                 // difference between the new and current centers in pixels
4526                 var offset = this._getCenterOffset(center)._trunc();
4527
4528                 // don't animate too far unless animate: true specified in options
4529                 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
4530
4531                 this.panBy(offset, options);
4532
4533                 return true;
4534         },
4535
4536         _createAnimProxy: function () {
4537
4538                 var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
4539                 this._panes.mapPane.appendChild(proxy);
4540
4541                 this.on('zoomanim', function (e) {
4542                         var prop = TRANSFORM,
4543                             transform = this._proxy.style[prop];
4544
4545                         setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
4546
4547                         // workaround for case when transform is the same and so transitionend event is not fired
4548                         if (transform === this._proxy.style[prop] && this._animatingZoom) {
4549                                 this._onZoomTransitionEnd();
4550                         }
4551                 }, this);
4552
4553                 this.on('load moveend', function () {
4554                         var c = this.getCenter(),
4555                             z = this.getZoom();
4556                         setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
4557                 }, this);
4558
4559                 this._on('unload', this._destroyAnimProxy, this);
4560         },
4561
4562         _destroyAnimProxy: function () {
4563                 remove(this._proxy);
4564                 delete this._proxy;
4565         },
4566
4567         _catchTransitionEnd: function (e) {
4568                 if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
4569                         this._onZoomTransitionEnd();
4570                 }
4571         },
4572
4573         _nothingToAnimate: function () {
4574                 return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
4575         },
4576
4577         _tryAnimatedZoom: function (center, zoom, options) {
4578
4579                 if (this._animatingZoom) { return true; }
4580
4581                 options = options || {};
4582
4583                 // don't animate if disabled, not supported or zoom difference is too large
4584                 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
4585                         Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
4586
4587                 // offset is the pixel coords of the zoom origin relative to the current center
4588                 var scale = this.getZoomScale(zoom),
4589                     offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
4590
4591                 // don't animate if the zoom origin isn't within one screen from the current center, unless forced
4592                 if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
4593
4594                 requestAnimFrame(function () {
4595                         this
4596                             ._moveStart(true, false)
4597                             ._animateZoom(center, zoom, true);
4598                 }, this);
4599
4600                 return true;
4601         },
4602
4603         _animateZoom: function (center, zoom, startAnim, noUpdate) {
4604                 if (!this._mapPane) { return; }
4605
4606                 if (startAnim) {
4607                         this._animatingZoom = true;
4608
4609                         // remember what center/zoom to set after animation
4610                         this._animateToCenter = center;
4611                         this._animateToZoom = zoom;
4612
4613                         addClass(this._mapPane, 'leaflet-zoom-anim');
4614                 }
4615
4616                 // @event zoomanim: ZoomAnimEvent
4617                 // Fired on every frame of a zoom animation
4618                 this.fire('zoomanim', {
4619                         center: center,
4620                         zoom: zoom,
4621                         noUpdate: noUpdate
4622                 });
4623
4624                 // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
4625                 setTimeout(bind(this._onZoomTransitionEnd, this), 250);
4626         },
4627
4628         _onZoomTransitionEnd: function () {
4629                 if (!this._animatingZoom) { return; }
4630
4631                 if (this._mapPane) {
4632                         removeClass(this._mapPane, 'leaflet-zoom-anim');
4633                 }
4634
4635                 this._animatingZoom = false;
4636
4637                 this._move(this._animateToCenter, this._animateToZoom);
4638
4639                 // This anim frame should prevent an obscure iOS webkit tile loading race condition.
4640                 requestAnimFrame(function () {
4641                         this._moveEnd(true);
4642                 }, this);
4643         }
4644 });
4645
4646 // @section
4647
4648 // @factory L.map(id: String, options?: Map options)
4649 // Instantiates a map object given the DOM ID of a `<div>` element
4650 // and optionally an object literal with `Map options`.
4651 //
4652 // @alternative
4653 // @factory L.map(el: HTMLElement, options?: Map options)
4654 // Instantiates a map object given an instance of a `<div>` HTML element
4655 // and optionally an object literal with `Map options`.
4656 function createMap(id, options) {
4657         return new Map(id, options);
4658 }
4659
4660 /*
4661  * @class Control
4662  * @aka L.Control
4663  * @inherits Class
4664  *
4665  * L.Control is a base class for implementing map controls. Handles positioning.
4666  * All other controls extend from this class.
4667  */
4668
4669 var Control = Class.extend({
4670         // @section
4671         // @aka Control options
4672         options: {
4673                 // @option position: String = 'topright'
4674                 // The position of the control (one of the map corners). Possible values are `'topleft'`,
4675                 // `'topright'`, `'bottomleft'` or `'bottomright'`
4676                 position: 'topright'
4677         },
4678
4679         initialize: function (options) {
4680                 setOptions(this, options);
4681         },
4682
4683         /* @section
4684          * Classes extending L.Control will inherit the following methods:
4685          *
4686          * @method getPosition: string
4687          * Returns the position of the control.
4688          */
4689         getPosition: function () {
4690                 return this.options.position;
4691         },
4692
4693         // @method setPosition(position: string): this
4694         // Sets the position of the control.
4695         setPosition: function (position) {
4696                 var map = this._map;
4697
4698                 if (map) {
4699                         map.removeControl(this);
4700                 }
4701
4702                 this.options.position = position;
4703
4704                 if (map) {
4705                         map.addControl(this);
4706                 }
4707
4708                 return this;
4709         },
4710
4711         // @method getContainer: HTMLElement
4712         // Returns the HTMLElement that contains the control.
4713         getContainer: function () {
4714                 return this._container;
4715         },
4716
4717         // @method addTo(map: Map): this
4718         // Adds the control to the given map.
4719         addTo: function (map) {
4720                 this.remove();
4721                 this._map = map;
4722
4723                 var container = this._container = this.onAdd(map),
4724                     pos = this.getPosition(),
4725                     corner = map._controlCorners[pos];
4726
4727                 addClass(container, 'leaflet-control');
4728
4729                 if (pos.indexOf('bottom') !== -1) {
4730                         corner.insertBefore(container, corner.firstChild);
4731                 } else {
4732                         corner.appendChild(container);
4733                 }
4734
4735                 return this;
4736         },
4737
4738         // @method remove: this
4739         // Removes the control from the map it is currently active on.
4740         remove: function () {
4741                 if (!this._map) {
4742                         return this;
4743                 }
4744
4745                 remove(this._container);
4746
4747                 if (this.onRemove) {
4748                         this.onRemove(this._map);
4749                 }
4750
4751                 this._map = null;
4752
4753                 return this;
4754         },
4755
4756         _refocusOnMap: function (e) {
4757                 // if map exists and event is not a keyboard event
4758                 if (this._map && e && e.screenX > 0 && e.screenY > 0) {
4759                         this._map.getContainer().focus();
4760                 }
4761         }
4762 });
4763
4764 var control = function (options) {
4765         return new Control(options);
4766 };
4767
4768 /* @section Extension methods
4769  * @uninheritable
4770  *
4771  * Every control should extend from `L.Control` and (re-)implement the following methods.
4772  *
4773  * @method onAdd(map: Map): HTMLElement
4774  * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
4775  *
4776  * @method onRemove(map: Map)
4777  * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
4778  */
4779
4780 /* @namespace Map
4781  * @section Methods for Layers and Controls
4782  */
4783 Map.include({
4784         // @method addControl(control: Control): this
4785         // Adds the given control to the map
4786         addControl: function (control) {
4787                 control.addTo(this);
4788                 return this;
4789         },
4790
4791         // @method removeControl(control: Control): this
4792         // Removes the given control from the map
4793         removeControl: function (control) {
4794                 control.remove();
4795                 return this;
4796         },
4797
4798         _initControlPos: function () {
4799                 var corners = this._controlCorners = {},
4800                     l = 'leaflet-',
4801                     container = this._controlContainer =
4802                             create$1('div', l + 'control-container', this._container);
4803
4804                 function createCorner(vSide, hSide) {
4805                         var className = l + vSide + ' ' + l + hSide;
4806
4807                         corners[vSide + hSide] = create$1('div', className, container);
4808                 }
4809
4810                 createCorner('top', 'left');
4811                 createCorner('top', 'right');
4812                 createCorner('bottom', 'left');
4813                 createCorner('bottom', 'right');
4814         },
4815
4816         _clearControlPos: function () {
4817                 for (var i in this._controlCorners) {
4818                         remove(this._controlCorners[i]);
4819                 }
4820                 remove(this._controlContainer);
4821                 delete this._controlCorners;
4822                 delete this._controlContainer;
4823         }
4824 });
4825
4826 /*
4827  * @class Control.Layers
4828  * @aka L.Control.Layers
4829  * @inherits Control
4830  *
4831  * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](http://leafletjs.com/examples/layers-control/)). Extends `Control`.
4832  *
4833  * @example
4834  *
4835  * ```js
4836  * var baseLayers = {
4837  *      "Mapbox": mapbox,
4838  *      "OpenStreetMap": osm
4839  * };
4840  *
4841  * var overlays = {
4842  *      "Marker": marker,
4843  *      "Roads": roadsLayer
4844  * };
4845  *
4846  * L.control.layers(baseLayers, overlays).addTo(map);
4847  * ```
4848  *
4849  * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
4850  *
4851  * ```js
4852  * {
4853  *     "<someName1>": layer1,
4854  *     "<someName2>": layer2
4855  * }
4856  * ```
4857  *
4858  * The layer names can contain HTML, which allows you to add additional styling to the items:
4859  *
4860  * ```js
4861  * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
4862  * ```
4863  */
4864
4865 var Layers = Control.extend({
4866         // @section
4867         // @aka Control.Layers options
4868         options: {
4869                 // @option collapsed: Boolean = true
4870                 // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
4871                 collapsed: true,
4872                 position: 'topright',
4873
4874                 // @option autoZIndex: Boolean = true
4875                 // If `true`, the control will assign zIndexes in increasing order to all of its layers so that the order is preserved when switching them on/off.
4876                 autoZIndex: true,
4877
4878                 // @option hideSingleBase: Boolean = false
4879                 // If `true`, the base layers in the control will be hidden when there is only one.
4880                 hideSingleBase: false,
4881
4882                 // @option sortLayers: Boolean = false
4883                 // Whether to sort the layers. When `false`, layers will keep the order
4884                 // in which they were added to the control.
4885                 sortLayers: false,
4886
4887                 // @option sortFunction: Function = *
4888                 // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
4889                 // that will be used for sorting the layers, when `sortLayers` is `true`.
4890                 // The function receives both the `L.Layer` instances and their names, as in
4891                 // `sortFunction(layerA, layerB, nameA, nameB)`.
4892                 // By default, it sorts layers alphabetically by their name.
4893                 sortFunction: function (layerA, layerB, nameA, nameB) {
4894                         return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
4895                 }
4896         },
4897
4898         initialize: function (baseLayers, overlays, options) {
4899                 setOptions(this, options);
4900
4901                 this._layerControlInputs = [];
4902                 this._layers = [];
4903                 this._lastZIndex = 0;
4904                 this._handlingClick = false;
4905
4906                 for (var i in baseLayers) {
4907                         this._addLayer(baseLayers[i], i);
4908                 }
4909
4910                 for (i in overlays) {
4911                         this._addLayer(overlays[i], i, true);
4912                 }
4913         },
4914
4915         onAdd: function (map) {
4916                 this._initLayout();
4917                 this._update();
4918
4919                 this._map = map;
4920                 map.on('zoomend', this._checkDisabledLayers, this);
4921
4922                 for (var i = 0; i < this._layers.length; i++) {
4923                         this._layers[i].layer.on('add remove', this._onLayerChange, this);
4924                 }
4925
4926                 return this._container;
4927         },
4928
4929         addTo: function (map) {
4930                 Control.prototype.addTo.call(this, map);
4931                 // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
4932                 return this._expandIfNotCollapsed();
4933         },
4934
4935         onRemove: function () {
4936                 this._map.off('zoomend', this._checkDisabledLayers, this);
4937
4938                 for (var i = 0; i < this._layers.length; i++) {
4939                         this._layers[i].layer.off('add remove', this._onLayerChange, this);
4940                 }
4941         },
4942
4943         // @method addBaseLayer(layer: Layer, name: String): this
4944         // Adds a base layer (radio button entry) with the given name to the control.
4945         addBaseLayer: function (layer, name) {
4946                 this._addLayer(layer, name);
4947                 return (this._map) ? this._update() : this;
4948         },
4949
4950         // @method addOverlay(layer: Layer, name: String): this
4951         // Adds an overlay (checkbox entry) with the given name to the control.
4952         addOverlay: function (layer, name) {
4953                 this._addLayer(layer, name, true);
4954                 return (this._map) ? this._update() : this;
4955         },
4956
4957         // @method removeLayer(layer: Layer): this
4958         // Remove the given layer from the control.
4959         removeLayer: function (layer) {
4960                 layer.off('add remove', this._onLayerChange, this);
4961
4962                 var obj = this._getLayer(stamp(layer));
4963                 if (obj) {
4964                         this._layers.splice(this._layers.indexOf(obj), 1);
4965                 }
4966                 return (this._map) ? this._update() : this;
4967         },
4968
4969         // @method expand(): this
4970         // Expand the control container if collapsed.
4971         expand: function () {
4972                 addClass(this._container, 'leaflet-control-layers-expanded');
4973                 this._form.style.height = null;
4974                 var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
4975                 if (acceptableHeight < this._form.clientHeight) {
4976                         addClass(this._form, 'leaflet-control-layers-scrollbar');
4977                         this._form.style.height = acceptableHeight + 'px';
4978                 } else {
4979                         removeClass(this._form, 'leaflet-control-layers-scrollbar');
4980                 }
4981                 this._checkDisabledLayers();
4982                 return this;
4983         },
4984
4985         // @method collapse(): this
4986         // Collapse the control container if expanded.
4987         collapse: function () {
4988                 removeClass(this._container, 'leaflet-control-layers-expanded');
4989                 return this;
4990         },
4991
4992         _initLayout: function () {
4993                 var className = 'leaflet-control-layers',
4994                     container = this._container = create$1('div', className),
4995                     collapsed = this.options.collapsed;
4996
4997                 // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
4998                 container.setAttribute('aria-haspopup', true);
4999
5000                 disableClickPropagation(container);
5001                 disableScrollPropagation(container);
5002
5003                 var form = this._form = create$1('form', className + '-list');
5004
5005                 if (collapsed) {
5006                         this._map.on('click', this.collapse, this);
5007
5008                         if (!android) {
5009                                 on(container, {
5010                                         mouseenter: this.expand,
5011                                         mouseleave: this.collapse
5012                                 }, this);
5013                         }
5014                 }
5015
5016                 var link = this._layersLink = create$1('a', className + '-toggle', container);
5017                 link.href = '#';
5018                 link.title = 'Layers';
5019
5020                 if (touch) {
5021                         on(link, 'click', stop);
5022                         on(link, 'click', this.expand, this);
5023                 } else {
5024                         on(link, 'focus', this.expand, this);
5025                 }
5026
5027                 if (!collapsed) {
5028                         this.expand();
5029                 }
5030
5031                 this._baseLayersList = create$1('div', className + '-base', form);
5032                 this._separator = create$1('div', className + '-separator', form);
5033                 this._overlaysList = create$1('div', className + '-overlays', form);
5034
5035                 container.appendChild(form);
5036         },
5037
5038         _getLayer: function (id) {
5039                 for (var i = 0; i < this._layers.length; i++) {
5040
5041                         if (this._layers[i] && stamp(this._layers[i].layer) === id) {
5042                                 return this._layers[i];
5043                         }
5044                 }
5045         },
5046
5047         _addLayer: function (layer, name, overlay) {
5048                 if (this._map) {
5049                         layer.on('add remove', this._onLayerChange, this);
5050                 }
5051
5052                 this._layers.push({
5053                         layer: layer,
5054                         name: name,
5055                         overlay: overlay
5056                 });
5057
5058                 if (this.options.sortLayers) {
5059                         this._layers.sort(bind(function (a, b) {
5060                                 return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
5061                         }, this));
5062                 }
5063
5064                 if (this.options.autoZIndex && layer.setZIndex) {
5065                         this._lastZIndex++;
5066                         layer.setZIndex(this._lastZIndex);
5067                 }
5068
5069                 this._expandIfNotCollapsed();
5070         },
5071
5072         _update: function () {
5073                 if (!this._container) { return this; }
5074
5075                 empty(this._baseLayersList);
5076                 empty(this._overlaysList);
5077
5078                 this._layerControlInputs = [];
5079                 var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
5080
5081                 for (i = 0; i < this._layers.length; i++) {
5082                         obj = this._layers[i];
5083                         this._addItem(obj);
5084                         overlaysPresent = overlaysPresent || obj.overlay;
5085                         baseLayersPresent = baseLayersPresent || !obj.overlay;
5086                         baseLayersCount += !obj.overlay ? 1 : 0;
5087                 }
5088
5089                 // Hide base layers section if there's only one layer.
5090                 if (this.options.hideSingleBase) {
5091                         baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
5092                         this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
5093                 }
5094
5095                 this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
5096
5097                 return this;
5098         },
5099
5100         _onLayerChange: function (e) {
5101                 if (!this._handlingClick) {
5102                         this._update();
5103                 }
5104
5105                 var obj = this._getLayer(stamp(e.target));
5106
5107                 // @namespace Map
5108                 // @section Layer events
5109                 // @event baselayerchange: LayersControlEvent
5110                 // Fired when the base layer is changed through the [layer control](#control-layers).
5111                 // @event overlayadd: LayersControlEvent
5112                 // Fired when an overlay is selected through the [layer control](#control-layers).
5113                 // @event overlayremove: LayersControlEvent
5114                 // Fired when an overlay is deselected through the [layer control](#control-layers).
5115                 // @namespace Control.Layers
5116                 var type = obj.overlay ?
5117                         (e.type === 'add' ? 'overlayadd' : 'overlayremove') :
5118                         (e.type === 'add' ? 'baselayerchange' : null);
5119
5120                 if (type) {
5121                         this._map.fire(type, obj);
5122                 }
5123         },
5124
5125         // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
5126         _createRadioElement: function (name, checked) {
5127
5128                 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
5129                                 name + '"' + (checked ? ' checked="checked"' : '') + '/>';
5130
5131                 var radioFragment = document.createElement('div');
5132                 radioFragment.innerHTML = radioHtml;
5133
5134                 return radioFragment.firstChild;
5135         },
5136
5137         _addItem: function (obj) {
5138                 var label = document.createElement('label'),
5139                     checked = this._map.hasLayer(obj.layer),
5140                     input;
5141
5142                 if (obj.overlay) {
5143                         input = document.createElement('input');
5144                         input.type = 'checkbox';
5145                         input.className = 'leaflet-control-layers-selector';
5146                         input.defaultChecked = checked;
5147                 } else {
5148                         input = this._createRadioElement('leaflet-base-layers', checked);
5149                 }
5150
5151                 this._layerControlInputs.push(input);
5152                 input.layerId = stamp(obj.layer);
5153
5154                 on(input, 'click', this._onInputClick, this);
5155
5156                 var name = document.createElement('span');
5157                 name.innerHTML = ' ' + obj.name;
5158
5159                 // Helps from preventing layer control flicker when checkboxes are disabled
5160                 // https://github.com/Leaflet/Leaflet/issues/2771
5161                 var holder = document.createElement('div');
5162
5163                 label.appendChild(holder);
5164                 holder.appendChild(input);
5165                 holder.appendChild(name);
5166
5167                 var container = obj.overlay ? this._overlaysList : this._baseLayersList;
5168                 container.appendChild(label);
5169
5170                 this._checkDisabledLayers();
5171                 return label;
5172         },
5173
5174         _onInputClick: function () {
5175                 var inputs = this._layerControlInputs,
5176                     input, layer;
5177                 var addedLayers = [],
5178                     removedLayers = [];
5179
5180                 this._handlingClick = true;
5181
5182                 for (var i = inputs.length - 1; i >= 0; i--) {
5183                         input = inputs[i];
5184                         layer = this._getLayer(input.layerId).layer;
5185
5186                         if (input.checked) {
5187                                 addedLayers.push(layer);
5188                         } else if (!input.checked) {
5189                                 removedLayers.push(layer);
5190                         }
5191                 }
5192
5193                 // Bugfix issue 2318: Should remove all old layers before readding new ones
5194                 for (i = 0; i < removedLayers.length; i++) {
5195                         if (this._map.hasLayer(removedLayers[i])) {
5196                                 this._map.removeLayer(removedLayers[i]);
5197                         }
5198                 }
5199                 for (i = 0; i < addedLayers.length; i++) {
5200                         if (!this._map.hasLayer(addedLayers[i])) {
5201                                 this._map.addLayer(addedLayers[i]);
5202                         }
5203                 }
5204
5205                 this._handlingClick = false;
5206
5207                 this._refocusOnMap();
5208         },
5209
5210         _checkDisabledLayers: function () {
5211                 var inputs = this._layerControlInputs,
5212                     input,
5213                     layer,
5214                     zoom = this._map.getZoom();
5215
5216                 for (var i = inputs.length - 1; i >= 0; i--) {
5217                         input = inputs[i];
5218                         layer = this._getLayer(input.layerId).layer;
5219                         input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
5220                                          (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
5221
5222                 }
5223         },
5224
5225         _expandIfNotCollapsed: function () {
5226                 if (this._map && !this.options.collapsed) {
5227                         this.expand();
5228                 }
5229                 return this;
5230         },
5231
5232         _expand: function () {
5233                 // Backward compatibility, remove me in 1.1.
5234                 return this.expand();
5235         },
5236
5237         _collapse: function () {
5238                 // Backward compatibility, remove me in 1.1.
5239                 return this.collapse();
5240         }
5241
5242 });
5243
5244
5245 // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
5246 // Creates an attribution control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation.
5247 var layers = function (baseLayers, overlays, options) {
5248         return new Layers(baseLayers, overlays, options);
5249 };
5250
5251 /*
5252  * @class Control.Zoom
5253  * @aka L.Control.Zoom
5254  * @inherits Control
5255  *
5256  * A basic zoom control with two buttons (zoom in and zoom out). It is put on the map by default unless you set its [`zoomControl` option](#map-zoomcontrol) to `false`. Extends `Control`.
5257  */
5258
5259 var Zoom = Control.extend({
5260         // @section
5261         // @aka Control.Zoom options
5262         options: {
5263                 position: 'topleft',
5264
5265                 // @option zoomInText: String = '+'
5266                 // The text set on the 'zoom in' button.
5267                 zoomInText: '+',
5268
5269                 // @option zoomInTitle: String = 'Zoom in'
5270                 // The title set on the 'zoom in' button.
5271                 zoomInTitle: 'Zoom in',
5272
5273                 // @option zoomOutText: String = '&#x2212;'
5274                 // The text set on the 'zoom out' button.
5275                 zoomOutText: '&#x2212;',
5276
5277                 // @option zoomOutTitle: String = 'Zoom out'
5278                 // The title set on the 'zoom out' button.
5279                 zoomOutTitle: 'Zoom out'
5280         },
5281
5282         onAdd: function (map) {
5283                 var zoomName = 'leaflet-control-zoom',
5284                     container = create$1('div', zoomName + ' leaflet-bar'),
5285                     options = this.options;
5286
5287                 this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
5288                         zoomName + '-in',  container, this._zoomIn);
5289                 this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
5290                         zoomName + '-out', container, this._zoomOut);
5291
5292                 this._updateDisabled();
5293                 map.on('zoomend zoomlevelschange', this._updateDisabled, this);
5294
5295                 return container;
5296         },
5297
5298         onRemove: function (map) {
5299                 map.off('zoomend zoomlevelschange', this._updateDisabled, this);
5300         },
5301
5302         disable: function () {
5303                 this._disabled = true;
5304                 this._updateDisabled();
5305                 return this;
5306         },
5307
5308         enable: function () {
5309                 this._disabled = false;
5310                 this._updateDisabled();
5311                 return this;
5312         },
5313
5314         _zoomIn: function (e) {
5315                 if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
5316                         this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5317                 }
5318         },
5319
5320         _zoomOut: function (e) {
5321                 if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
5322                         this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5323                 }
5324         },
5325
5326         _createButton: function (html, title, className, container, fn) {
5327                 var link = create$1('a', className, container);
5328                 link.innerHTML = html;
5329                 link.href = '#';
5330                 link.title = title;
5331
5332                 /*
5333                  * Will force screen readers like VoiceOver to read this as "Zoom in - button"
5334                  */
5335                 link.setAttribute('role', 'button');
5336                 link.setAttribute('aria-label', title);
5337
5338                 disableClickPropagation(link);
5339                 on(link, 'click', stop);
5340                 on(link, 'click', fn, this);
5341                 on(link, 'click', this._refocusOnMap, this);
5342
5343                 return link;
5344         },
5345
5346         _updateDisabled: function () {
5347                 var map = this._map,
5348                     className = 'leaflet-disabled';
5349
5350                 removeClass(this._zoomInButton, className);
5351                 removeClass(this._zoomOutButton, className);
5352
5353                 if (this._disabled || map._zoom === map.getMinZoom()) {
5354                         addClass(this._zoomOutButton, className);
5355                 }
5356                 if (this._disabled || map._zoom === map.getMaxZoom()) {
5357                         addClass(this._zoomInButton, className);
5358                 }
5359         }
5360 });
5361
5362 // @namespace Map
5363 // @section Control options
5364 // @option zoomControl: Boolean = true
5365 // Whether a [zoom control](#control-zoom) is added to the map by default.
5366 Map.mergeOptions({
5367         zoomControl: true
5368 });
5369
5370 Map.addInitHook(function () {
5371         if (this.options.zoomControl) {
5372                 this.zoomControl = new Zoom();
5373                 this.addControl(this.zoomControl);
5374         }
5375 });
5376
5377 // @namespace Control.Zoom
5378 // @factory L.control.zoom(options: Control.Zoom options)
5379 // Creates a zoom control
5380 var zoom = function (options) {
5381         return new Zoom(options);
5382 };
5383
5384 /*
5385  * @class Control.Scale
5386  * @aka L.Control.Scale
5387  * @inherits Control
5388  *
5389  * A simple scale control that shows the scale of the current center of screen in metric (m/km) and imperial (mi/ft) systems. Extends `Control`.
5390  *
5391  * @example
5392  *
5393  * ```js
5394  * L.control.scale().addTo(map);
5395  * ```
5396  */
5397
5398 var Scale = Control.extend({
5399         // @section
5400         // @aka Control.Scale options
5401         options: {
5402                 position: 'bottomleft',
5403
5404                 // @option maxWidth: Number = 100
5405                 // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
5406                 maxWidth: 100,
5407
5408                 // @option metric: Boolean = True
5409                 // Whether to show the metric scale line (m/km).
5410                 metric: true,
5411
5412                 // @option imperial: Boolean = True
5413                 // Whether to show the imperial scale line (mi/ft).
5414                 imperial: true
5415
5416                 // @option updateWhenIdle: Boolean = false
5417                 // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
5418         },
5419
5420         onAdd: function (map) {
5421                 var className = 'leaflet-control-scale',
5422                     container = create$1('div', className),
5423                     options = this.options;
5424
5425                 this._addScales(options, className + '-line', container);
5426
5427                 map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5428                 map.whenReady(this._update, this);
5429
5430                 return container;
5431         },
5432
5433         onRemove: function (map) {
5434                 map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5435         },
5436
5437         _addScales: function (options, className, container) {
5438                 if (options.metric) {
5439                         this._mScale = create$1('div', className, container);
5440                 }
5441                 if (options.imperial) {
5442                         this._iScale = create$1('div', className, container);
5443                 }
5444         },
5445
5446         _update: function () {
5447                 var map = this._map,
5448                     y = map.getSize().y / 2;
5449
5450                 var maxMeters = map.distance(
5451                         map.containerPointToLatLng([0, y]),
5452                         map.containerPointToLatLng([this.options.maxWidth, y]));
5453
5454                 this._updateScales(maxMeters);
5455         },
5456
5457         _updateScales: function (maxMeters) {
5458                 if (this.options.metric && maxMeters) {
5459                         this._updateMetric(maxMeters);
5460                 }
5461                 if (this.options.imperial && maxMeters) {
5462                         this._updateImperial(maxMeters);
5463                 }
5464         },
5465
5466         _updateMetric: function (maxMeters) {
5467                 var meters = this._getRoundNum(maxMeters),
5468                     label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
5469
5470                 this._updateScale(this._mScale, label, meters / maxMeters);
5471         },
5472
5473         _updateImperial: function (maxMeters) {
5474                 var maxFeet = maxMeters * 3.2808399,
5475                     maxMiles, miles, feet;
5476
5477                 if (maxFeet > 5280) {
5478                         maxMiles = maxFeet / 5280;
5479                         miles = this._getRoundNum(maxMiles);
5480                         this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
5481
5482                 } else {
5483                         feet = this._getRoundNum(maxFeet);
5484                         this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
5485                 }
5486         },
5487
5488         _updateScale: function (scale, text, ratio) {
5489                 scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
5490                 scale.innerHTML = text;
5491         },
5492
5493         _getRoundNum: function (num) {
5494                 var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
5495                     d = num / pow10;
5496
5497                 d = d >= 10 ? 10 :
5498                     d >= 5 ? 5 :
5499                     d >= 3 ? 3 :
5500                     d >= 2 ? 2 : 1;
5501
5502                 return pow10 * d;
5503         }
5504 });
5505
5506
5507 // @factory L.control.scale(options?: Control.Scale options)
5508 // Creates an scale control with the given options.
5509 var scale = function (options) {
5510         return new Scale(options);
5511 };
5512
5513 /*
5514  * @class Control.Attribution
5515  * @aka L.Control.Attribution
5516  * @inherits Control
5517  *
5518  * The attribution control allows you to display attribution data in a small text box on a map. It is put on the map by default unless you set its [`attributionControl` option](#map-attributioncontrol) to `false`, and it fetches attribution texts from layers with the [`getAttribution` method](#layer-getattribution) automatically. Extends Control.
5519  */
5520
5521 var Attribution = Control.extend({
5522         // @section
5523         // @aka Control.Attribution options
5524         options: {
5525                 position: 'bottomright',
5526
5527                 // @option prefix: String = 'Leaflet'
5528                 // The HTML text shown before the attributions. Pass `false` to disable.
5529                 prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
5530         },
5531
5532         initialize: function (options) {
5533                 setOptions(this, options);
5534
5535                 this._attributions = {};
5536         },
5537
5538         onAdd: function (map) {
5539                 map.attributionControl = this;
5540                 this._container = create$1('div', 'leaflet-control-attribution');
5541                 disableClickPropagation(this._container);
5542
5543                 // TODO ugly, refactor
5544                 for (var i in map._layers) {
5545                         if (map._layers[i].getAttribution) {
5546                                 this.addAttribution(map._layers[i].getAttribution());
5547                         }
5548                 }
5549
5550                 this._update();
5551
5552                 return this._container;
5553         },
5554
5555         // @method setPrefix(prefix: String): this
5556         // Sets the text before the attributions.
5557         setPrefix: function (prefix) {
5558                 this.options.prefix = prefix;
5559                 this._update();
5560                 return this;
5561         },
5562
5563         // @method addAttribution(text: String): this
5564         // Adds an attribution text (e.g. `'Vector data &copy; Mapbox'`).
5565         addAttribution: function (text) {
5566                 if (!text) { return this; }
5567
5568                 if (!this._attributions[text]) {
5569                         this._attributions[text] = 0;
5570                 }
5571                 this._attributions[text]++;
5572
5573                 this._update();
5574
5575                 return this;
5576         },
5577
5578         // @method removeAttribution(text: String): this
5579         // Removes an attribution text.
5580         removeAttribution: function (text) {
5581                 if (!text) { return this; }
5582
5583                 if (this._attributions[text]) {
5584                         this._attributions[text]--;
5585                         this._update();
5586                 }
5587
5588                 return this;
5589         },
5590
5591         _update: function () {
5592                 if (!this._map) { return; }
5593
5594                 var attribs = [];
5595
5596                 for (var i in this._attributions) {
5597                         if (this._attributions[i]) {
5598                                 attribs.push(i);
5599                         }
5600                 }
5601
5602                 var prefixAndAttribs = [];
5603
5604                 if (this.options.prefix) {
5605                         prefixAndAttribs.push(this.options.prefix);
5606                 }
5607                 if (attribs.length) {
5608                         prefixAndAttribs.push(attribs.join(', '));
5609                 }
5610
5611                 this._container.innerHTML = prefixAndAttribs.join(' | ');
5612         }
5613 });
5614
5615 // @namespace Map
5616 // @section Control options
5617 // @option attributionControl: Boolean = true
5618 // Whether a [attribution control](#control-attribution) is added to the map by default.
5619 Map.mergeOptions({
5620         attributionControl: true
5621 });
5622
5623 Map.addInitHook(function () {
5624         if (this.options.attributionControl) {
5625                 new Attribution().addTo(this);
5626         }
5627 });
5628
5629 // @namespace Control.Attribution
5630 // @factory L.control.attribution(options: Control.Attribution options)
5631 // Creates an attribution control.
5632 var attribution = function (options) {
5633         return new Attribution(options);
5634 };
5635
5636 Control.Layers = Layers;
5637 Control.Zoom = Zoom;
5638 Control.Scale = Scale;
5639 Control.Attribution = Attribution;
5640
5641 control.layers = layers;
5642 control.zoom = zoom;
5643 control.scale = scale;
5644 control.attribution = attribution;
5645
5646 /*
5647         L.Handler is a base class for handler classes that are used internally to inject
5648         interaction features like dragging to classes like Map and Marker.
5649 */
5650
5651 // @class Handler
5652 // @aka L.Handler
5653 // Abstract class for map interaction handlers
5654
5655 var Handler = Class.extend({
5656         initialize: function (map) {
5657                 this._map = map;
5658         },
5659
5660         // @method enable(): this
5661         // Enables the handler
5662         enable: function () {
5663                 if (this._enabled) { return this; }
5664
5665                 this._enabled = true;
5666                 this.addHooks();
5667                 return this;
5668         },
5669
5670         // @method disable(): this
5671         // Disables the handler
5672         disable: function () {
5673                 if (!this._enabled) { return this; }
5674
5675                 this._enabled = false;
5676                 this.removeHooks();
5677                 return this;
5678         },
5679
5680         // @method enabled(): Boolean
5681         // Returns `true` if the handler is enabled
5682         enabled: function () {
5683                 return !!this._enabled;
5684         }
5685
5686         // @section Extension methods
5687         // Classes inheriting from `Handler` must implement the two following methods:
5688         // @method addHooks()
5689         // Called when the handler is enabled, should add event hooks.
5690         // @method removeHooks()
5691         // Called when the handler is disabled, should remove the event hooks added previously.
5692 });
5693
5694 // @section There is static function which can be called without instantiating L.Handler:
5695 // @function addTo(map: Map, name: String): this
5696 // Adds a new Handler to the given map with the given name.
5697 Handler.addTo = function (map, name) {
5698         map.addHandler(name, this);
5699         return this;
5700 };
5701
5702 var Mixin = {Events: Events};
5703
5704 /*
5705  * @class Draggable
5706  * @aka L.Draggable
5707  * @inherits Evented
5708  *
5709  * A class for making DOM elements draggable (including touch support).
5710  * Used internally for map and marker dragging. Only works for elements
5711  * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
5712  *
5713  * @example
5714  * ```js
5715  * var draggable = new L.Draggable(elementToDrag);
5716  * draggable.enable();
5717  * ```
5718  */
5719
5720 var START = touch ? 'touchstart mousedown' : 'mousedown';
5721 var END = {
5722         mousedown: 'mouseup',
5723         touchstart: 'touchend',
5724         pointerdown: 'touchend',
5725         MSPointerDown: 'touchend'
5726 };
5727 var MOVE = {
5728         mousedown: 'mousemove',
5729         touchstart: 'touchmove',
5730         pointerdown: 'touchmove',
5731         MSPointerDown: 'touchmove'
5732 };
5733
5734
5735 var Draggable = Evented.extend({
5736
5737         options: {
5738                 // @section
5739                 // @aka Draggable options
5740                 // @option clickTolerance: Number = 3
5741                 // The max number of pixels a user can shift the mouse pointer during a click
5742                 // for it to be considered a valid click (as opposed to a mouse drag).
5743                 clickTolerance: 3
5744         },
5745
5746         // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
5747         // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
5748         initialize: function (element, dragStartTarget, preventOutline$$1, options) {
5749                 setOptions(this, options);
5750
5751                 this._element = element;
5752                 this._dragStartTarget = dragStartTarget || element;
5753                 this._preventOutline = preventOutline$$1;
5754         },
5755
5756         // @method enable()
5757         // Enables the dragging ability
5758         enable: function () {
5759                 if (this._enabled) { return; }
5760
5761                 on(this._dragStartTarget, START, this._onDown, this);
5762
5763                 this._enabled = true;
5764         },
5765
5766         // @method disable()
5767         // Disables the dragging ability
5768         disable: function () {
5769                 if (!this._enabled) { return; }
5770
5771                 // If we're currently dragging this draggable,
5772                 // disabling it counts as first ending the drag.
5773                 if (Draggable._dragging === this) {
5774                         this.finishDrag();
5775                 }
5776
5777                 off(this._dragStartTarget, START, this._onDown, this);
5778
5779                 this._enabled = false;
5780                 this._moved = false;
5781         },
5782
5783         _onDown: function (e) {
5784                 // Ignore simulated events, since we handle both touch and
5785                 // mouse explicitly; otherwise we risk getting duplicates of
5786                 // touch events, see #4315.
5787                 // Also ignore the event if disabled; this happens in IE11
5788                 // under some circumstances, see #3666.
5789                 if (e._simulated || !this._enabled) { return; }
5790
5791                 this._moved = false;
5792
5793                 if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
5794
5795                 if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
5796                 Draggable._dragging = this;  // Prevent dragging multiple objects at once.
5797
5798                 if (this._preventOutline) {
5799                         preventOutline(this._element);
5800                 }
5801
5802                 disableImageDrag();
5803                 disableTextSelection();
5804
5805                 if (this._moving) { return; }
5806
5807                 // @event down: Event
5808                 // Fired when a drag is about to start.
5809                 this.fire('down');
5810
5811                 var first = e.touches ? e.touches[0] : e,
5812                     sizedParent = getSizedParentNode(this._element);
5813
5814                 this._startPoint = new Point(first.clientX, first.clientY);
5815
5816                 // Cache the scale, so that we can continuously compensate for it during drag (_onMove).
5817                 this._parentScale = getScale(sizedParent);
5818
5819                 on(document, MOVE[e.type], this._onMove, this);
5820                 on(document, END[e.type], this._onUp, this);
5821         },
5822
5823         _onMove: function (e) {
5824                 // Ignore simulated events, since we handle both touch and
5825                 // mouse explicitly; otherwise we risk getting duplicates of
5826                 // touch events, see #4315.
5827                 // Also ignore the event if disabled; this happens in IE11
5828                 // under some circumstances, see #3666.
5829                 if (e._simulated || !this._enabled) { return; }
5830
5831                 if (e.touches && e.touches.length > 1) {
5832                         this._moved = true;
5833                         return;
5834                 }
5835
5836                 var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
5837                     offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint);
5838
5839                 if (!offset.x && !offset.y) { return; }
5840                 if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
5841
5842                 // We assume that the parent container's position, border and scale do not change for the duration of the drag.
5843                 // Therefore there is no need to account for the position and border (they are eliminated by the subtraction)
5844                 // and we can use the cached value for the scale.
5845                 offset.x /= this._parentScale.x;
5846                 offset.y /= this._parentScale.y;
5847
5848                 preventDefault(e);
5849
5850                 if (!this._moved) {
5851                         // @event dragstart: Event
5852                         // Fired when a drag starts
5853                         this.fire('dragstart');
5854
5855                         this._moved = true;
5856                         this._startPos = getPosition(this._element).subtract(offset);
5857
5858                         addClass(document.body, 'leaflet-dragging');
5859
5860                         this._lastTarget = e.target || e.srcElement;
5861                         // IE and Edge do not give the <use> element, so fetch it
5862                         // if necessary
5863                         if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
5864                                 this._lastTarget = this._lastTarget.correspondingUseElement;
5865                         }
5866                         addClass(this._lastTarget, 'leaflet-drag-target');
5867                 }
5868
5869                 this._newPos = this._startPos.add(offset);
5870                 this._moving = true;
5871
5872                 cancelAnimFrame(this._animRequest);
5873                 this._lastEvent = e;
5874                 this._animRequest = requestAnimFrame(this._updatePosition, this, true);
5875         },
5876
5877         _updatePosition: function () {
5878                 var e = {originalEvent: this._lastEvent};
5879
5880                 // @event predrag: Event
5881                 // Fired continuously during dragging *before* each corresponding
5882                 // update of the element's position.
5883                 this.fire('predrag', e);
5884                 setPosition(this._element, this._newPos);
5885
5886                 // @event drag: Event
5887                 // Fired continuously during dragging.
5888                 this.fire('drag', e);
5889         },
5890
5891         _onUp: function (e) {
5892                 // Ignore simulated events, since we handle both touch and
5893                 // mouse explicitly; otherwise we risk getting duplicates of
5894                 // touch events, see #4315.
5895                 // Also ignore the event if disabled; this happens in IE11
5896                 // under some circumstances, see #3666.
5897                 if (e._simulated || !this._enabled) { return; }
5898                 this.finishDrag();
5899         },
5900
5901         finishDrag: function () {
5902                 removeClass(document.body, 'leaflet-dragging');
5903
5904                 if (this._lastTarget) {
5905                         removeClass(this._lastTarget, 'leaflet-drag-target');
5906                         this._lastTarget = null;
5907                 }
5908
5909                 for (var i in MOVE) {
5910                         off(document, MOVE[i], this._onMove, this);
5911                         off(document, END[i], this._onUp, this);
5912                 }
5913
5914                 enableImageDrag();
5915                 enableTextSelection();
5916
5917                 if (this._moved && this._moving) {
5918                         // ensure drag is not fired after dragend
5919                         cancelAnimFrame(this._animRequest);
5920
5921                         // @event dragend: DragEndEvent
5922                         // Fired when the drag ends.
5923                         this.fire('dragend', {
5924                                 distance: this._newPos.distanceTo(this._startPos)
5925                         });
5926                 }
5927
5928                 this._moving = false;
5929                 Draggable._dragging = false;
5930         }
5931
5932 });
5933
5934 /*
5935  * @namespace LineUtil
5936  *
5937  * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast.
5938  */
5939
5940 // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
5941 // Improves rendering performance dramatically by lessening the number of points to draw.
5942
5943 // @function simplify(points: Point[], tolerance: Number): Point[]
5944 // Dramatically reduces the number of points in a polyline while retaining
5945 // its shape and returns a new array of simplified points, using the
5946 // [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
5947 // Used for a huge performance boost when processing/displaying Leaflet polylines for
5948 // each zoom level and also reducing visual noise. tolerance affects the amount of
5949 // simplification (lesser value means higher quality but slower and with more points).
5950 // Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
5951 function simplify(points, tolerance) {
5952         if (!tolerance || !points.length) {
5953                 return points.slice();
5954         }
5955
5956         var sqTolerance = tolerance * tolerance;
5957
5958             // stage 1: vertex reduction
5959             points = _reducePoints(points, sqTolerance);
5960
5961             // stage 2: Douglas-Peucker simplification
5962             points = _simplifyDP(points, sqTolerance);
5963
5964         return points;
5965 }
5966
5967 // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
5968 // Returns the distance between point `p` and segment `p1` to `p2`.
5969 function pointToSegmentDistance(p, p1, p2) {
5970         return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
5971 }
5972
5973 // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
5974 // Returns the closest point from a point `p` on a segment `p1` to `p2`.
5975 function closestPointOnSegment(p, p1, p2) {
5976         return _sqClosestPointOnSegment(p, p1, p2);
5977 }
5978
5979 // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
5980 function _simplifyDP(points, sqTolerance) {
5981
5982         var len = points.length,
5983             ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
5984             markers = new ArrayConstructor(len);
5985
5986             markers[0] = markers[len - 1] = 1;
5987
5988         _simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
5989
5990         var i,
5991             newPoints = [];
5992
5993         for (i = 0; i < len; i++) {
5994                 if (markers[i]) {
5995                         newPoints.push(points[i]);
5996                 }
5997         }
5998
5999         return newPoints;
6000 }
6001
6002 function _simplifyDPStep(points, markers, sqTolerance, first, last) {
6003
6004         var maxSqDist = 0,
6005         index, i, sqDist;
6006
6007         for (i = first + 1; i <= last - 1; i++) {
6008                 sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
6009
6010                 if (sqDist > maxSqDist) {
6011                         index = i;
6012                         maxSqDist = sqDist;
6013                 }
6014         }
6015
6016         if (maxSqDist > sqTolerance) {
6017                 markers[index] = 1;
6018
6019                 _simplifyDPStep(points, markers, sqTolerance, first, index);
6020                 _simplifyDPStep(points, markers, sqTolerance, index, last);
6021         }
6022 }
6023
6024 // reduce points that are too close to each other to a single point
6025 function _reducePoints(points, sqTolerance) {
6026         var reducedPoints = [points[0]];
6027
6028         for (var i = 1, prev = 0, len = points.length; i < len; i++) {
6029                 if (_sqDist(points[i], points[prev]) > sqTolerance) {
6030                         reducedPoints.push(points[i]);
6031                         prev = i;
6032                 }
6033         }
6034         if (prev < len - 1) {
6035                 reducedPoints.push(points[len - 1]);
6036         }
6037         return reducedPoints;
6038 }
6039
6040 var _lastCode;
6041
6042 // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
6043 // Clips the segment a to b by rectangular bounds with the
6044 // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
6045 // (modifying the segment points directly!). Used by Leaflet to only show polyline
6046 // points that are on the screen or near, increasing performance.
6047 function clipSegment(a, b, bounds, useLastCode, round) {
6048         var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
6049             codeB = _getBitCode(b, bounds),
6050
6051             codeOut, p, newCode;
6052
6053             // save 2nd code to avoid calculating it on the next segment
6054             _lastCode = codeB;
6055
6056         while (true) {
6057                 // if a,b is inside the clip window (trivial accept)
6058                 if (!(codeA | codeB)) {
6059                         return [a, b];
6060                 }
6061
6062                 // if a,b is outside the clip window (trivial reject)
6063                 if (codeA & codeB) {
6064                         return false;
6065                 }
6066
6067                 // other cases
6068                 codeOut = codeA || codeB;
6069                 p = _getEdgeIntersection(a, b, codeOut, bounds, round);
6070                 newCode = _getBitCode(p, bounds);
6071
6072                 if (codeOut === codeA) {
6073                         a = p;
6074                         codeA = newCode;
6075                 } else {
6076                         b = p;
6077                         codeB = newCode;
6078                 }
6079         }
6080 }
6081
6082 function _getEdgeIntersection(a, b, code, bounds, round) {
6083         var dx = b.x - a.x,
6084             dy = b.y - a.y,
6085             min = bounds.min,
6086             max = bounds.max,
6087             x, y;
6088
6089         if (code & 8) { // top
6090                 x = a.x + dx * (max.y - a.y) / dy;
6091                 y = max.y;
6092
6093         } else if (code & 4) { // bottom
6094                 x = a.x + dx * (min.y - a.y) / dy;
6095                 y = min.y;
6096
6097         } else if (code & 2) { // right
6098                 x = max.x;
6099                 y = a.y + dy * (max.x - a.x) / dx;
6100
6101         } else if (code & 1) { // left
6102                 x = min.x;
6103                 y = a.y + dy * (min.x - a.x) / dx;
6104         }
6105
6106         return new Point(x, y, round);
6107 }
6108
6109 function _getBitCode(p, bounds) {
6110         var code = 0;
6111
6112         if (p.x < bounds.min.x) { // left
6113                 code |= 1;
6114         } else if (p.x > bounds.max.x) { // right
6115                 code |= 2;
6116         }
6117
6118         if (p.y < bounds.min.y) { // bottom
6119                 code |= 4;
6120         } else if (p.y > bounds.max.y) { // top
6121                 code |= 8;
6122         }
6123
6124         return code;
6125 }
6126
6127 // square distance (to avoid unnecessary Math.sqrt calls)
6128 function _sqDist(p1, p2) {
6129         var dx = p2.x - p1.x,
6130             dy = p2.y - p1.y;
6131         return dx * dx + dy * dy;
6132 }
6133
6134 // return closest point on segment or distance to that point
6135 function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
6136         var x = p1.x,
6137             y = p1.y,
6138             dx = p2.x - x,
6139             dy = p2.y - y,
6140             dot = dx * dx + dy * dy,
6141             t;
6142
6143         if (dot > 0) {
6144                 t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
6145
6146                 if (t > 1) {
6147                         x = p2.x;
6148                         y = p2.y;
6149                 } else if (t > 0) {
6150                         x += dx * t;
6151                         y += dy * t;
6152                 }
6153         }
6154
6155         dx = p.x - x;
6156         dy = p.y - y;
6157
6158         return sqDist ? dx * dx + dy * dy : new Point(x, y);
6159 }
6160
6161
6162 // @function isFlat(latlngs: LatLng[]): Boolean
6163 // Returns true if `latlngs` is a flat array, false is nested.
6164 function isFlat(latlngs) {
6165         return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
6166 }
6167
6168 function _flat(latlngs) {
6169         console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
6170         return isFlat(latlngs);
6171 }
6172
6173
6174 var LineUtil = (Object.freeze || Object)({
6175         simplify: simplify,
6176         pointToSegmentDistance: pointToSegmentDistance,
6177         closestPointOnSegment: closestPointOnSegment,
6178         clipSegment: clipSegment,
6179         _getEdgeIntersection: _getEdgeIntersection,
6180         _getBitCode: _getBitCode,
6181         _sqClosestPointOnSegment: _sqClosestPointOnSegment,
6182         isFlat: isFlat,
6183         _flat: _flat
6184 });
6185
6186 /*
6187  * @namespace PolyUtil
6188  * Various utility functions for polygon geometries.
6189  */
6190
6191 /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
6192  * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)).
6193  * Used by Leaflet to only show polygon points that are on the screen or near, increasing
6194  * performance. Note that polygon points needs different algorithm for clipping
6195  * than polyline, so there's a separate method for it.
6196  */
6197 function clipPolygon(points, bounds, round) {
6198         var clippedPoints,
6199             edges = [1, 4, 2, 8],
6200             i, j, k,
6201             a, b,
6202             len, edge, p;
6203
6204         for (i = 0, len = points.length; i < len; i++) {
6205                 points[i]._code = _getBitCode(points[i], bounds);
6206         }
6207
6208         // for each edge (left, bottom, right, top)
6209         for (k = 0; k < 4; k++) {
6210                 edge = edges[k];
6211                 clippedPoints = [];
6212
6213                 for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
6214                         a = points[i];
6215                         b = points[j];
6216
6217                         // if a is inside the clip window
6218                         if (!(a._code & edge)) {
6219                                 // if b is outside the clip window (a->b goes out of screen)
6220                                 if (b._code & edge) {
6221                                         p = _getEdgeIntersection(b, a, edge, bounds, round);
6222                                         p._code = _getBitCode(p, bounds);
6223                                         clippedPoints.push(p);
6224                                 }
6225                                 clippedPoints.push(a);
6226
6227                         // else if b is inside the clip window (a->b enters the screen)
6228                         } else if (!(b._code & edge)) {
6229                                 p = _getEdgeIntersection(b, a, edge, bounds, round);
6230                                 p._code = _getBitCode(p, bounds);
6231                                 clippedPoints.push(p);
6232                         }
6233                 }
6234                 points = clippedPoints;
6235         }
6236
6237         return points;
6238 }
6239
6240
6241 var PolyUtil = (Object.freeze || Object)({
6242         clipPolygon: clipPolygon
6243 });
6244
6245 /*
6246  * @namespace Projection
6247  * @section
6248  * Leaflet comes with a set of already defined Projections out of the box:
6249  *
6250  * @projection L.Projection.LonLat
6251  *
6252  * Equirectangular, or Plate Carree projection — the most simple projection,
6253  * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
6254  * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
6255  * `EPSG:4326` and `Simple` CRS.
6256  */
6257
6258 var LonLat = {
6259         project: function (latlng) {
6260                 return new Point(latlng.lng, latlng.lat);
6261         },
6262
6263         unproject: function (point) {
6264                 return new LatLng(point.y, point.x);
6265         },
6266
6267         bounds: new Bounds([-180, -90], [180, 90])
6268 };
6269
6270 /*
6271  * @namespace Projection
6272  * @projection L.Projection.Mercator
6273  *
6274  * Elliptical Mercator projection — more complex than Spherical Mercator. Takes into account that Earth is a geoid, not a perfect sphere. Used by the EPSG:3395 CRS.
6275  */
6276
6277 var Mercator = {
6278         R: 6378137,
6279         R_MINOR: 6356752.314245179,
6280
6281         bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
6282
6283         project: function (latlng) {
6284                 var d = Math.PI / 180,
6285                     r = this.R,
6286                     y = latlng.lat * d,
6287                     tmp = this.R_MINOR / r,
6288                     e = Math.sqrt(1 - tmp * tmp),
6289                     con = e * Math.sin(y);
6290
6291                 var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
6292                 y = -r * Math.log(Math.max(ts, 1E-10));
6293
6294                 return new Point(latlng.lng * d * r, y);
6295         },
6296
6297         unproject: function (point) {
6298                 var d = 180 / Math.PI,
6299                     r = this.R,
6300                     tmp = this.R_MINOR / r,
6301                     e = Math.sqrt(1 - tmp * tmp),
6302                     ts = Math.exp(-point.y / r),
6303                     phi = Math.PI / 2 - 2 * Math.atan(ts);
6304
6305                 for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
6306                         con = e * Math.sin(phi);
6307                         con = Math.pow((1 - con) / (1 + con), e / 2);
6308                         dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
6309                         phi += dphi;
6310                 }
6311
6312                 return new LatLng(phi * d, point.x * d / r);
6313         }
6314 };
6315
6316 /*
6317  * @class Projection
6318
6319  * An object with methods for projecting geographical coordinates of the world onto
6320  * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection).
6321
6322  * @property bounds: Bounds
6323  * The bounds (specified in CRS units) where the projection is valid
6324
6325  * @method project(latlng: LatLng): Point
6326  * Projects geographical coordinates into a 2D point.
6327  * Only accepts actual `L.LatLng` instances, not arrays.
6328
6329  * @method unproject(point: Point): LatLng
6330  * The inverse of `project`. Projects a 2D point into a geographical location.
6331  * Only accepts actual `L.Point` instances, not arrays.
6332
6333  * Note that the projection instances do not inherit from Leafet's `Class` object,
6334  * and can't be instantiated. Also, new classes can't inherit from them,
6335  * and methods can't be added to them with the `include` function.
6336
6337  */
6338
6339
6340
6341
6342 var index = (Object.freeze || Object)({
6343         LonLat: LonLat,
6344         Mercator: Mercator,
6345         SphericalMercator: SphericalMercator
6346 });
6347
6348 /*
6349  * @namespace CRS
6350  * @crs L.CRS.EPSG3395
6351  *
6352  * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
6353  */
6354 var EPSG3395 = extend({}, Earth, {
6355         code: 'EPSG:3395',
6356         projection: Mercator,
6357
6358         transformation: (function () {
6359                 var scale = 0.5 / (Math.PI * Mercator.R);
6360                 return toTransformation(scale, 0.5, -scale, 0.5);
6361         }())
6362 });
6363
6364 /*
6365  * @namespace CRS
6366  * @crs L.CRS.EPSG4326
6367  *
6368  * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
6369  *
6370  * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
6371  * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
6372  * with this CRS, ensure that there are two 256x256 pixel tiles covering the
6373  * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
6374  * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
6375  */
6376
6377 var EPSG4326 = extend({}, Earth, {
6378         code: 'EPSG:4326',
6379         projection: LonLat,
6380         transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
6381 });
6382
6383 /*
6384  * @namespace CRS
6385  * @crs L.CRS.Simple
6386  *
6387  * A simple CRS that maps longitude and latitude into `x` and `y` directly.
6388  * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
6389  * axis should still be inverted (going from bottom to top). `distance()` returns
6390  * simple euclidean distance.
6391  */
6392
6393 var Simple = extend({}, CRS, {
6394         projection: LonLat,
6395         transformation: toTransformation(1, 0, -1, 0),
6396
6397         scale: function (zoom) {
6398                 return Math.pow(2, zoom);
6399         },
6400
6401         zoom: function (scale) {
6402                 return Math.log(scale) / Math.LN2;
6403         },
6404
6405         distance: function (latlng1, latlng2) {
6406                 var dx = latlng2.lng - latlng1.lng,
6407                     dy = latlng2.lat - latlng1.lat;
6408
6409                 return Math.sqrt(dx * dx + dy * dy);
6410         },
6411
6412         infinite: true
6413 });
6414
6415 CRS.Earth = Earth;
6416 CRS.EPSG3395 = EPSG3395;
6417 CRS.EPSG3857 = EPSG3857;
6418 CRS.EPSG900913 = EPSG900913;
6419 CRS.EPSG4326 = EPSG4326;
6420 CRS.Simple = Simple;
6421
6422 /*
6423  * @class Layer
6424  * @inherits Evented
6425  * @aka L.Layer
6426  * @aka ILayer
6427  *
6428  * A set of methods from the Layer base class that all Leaflet layers use.
6429  * Inherits all methods, options and events from `L.Evented`.
6430  *
6431  * @example
6432  *
6433  * ```js
6434  * var layer = L.Marker(latlng).addTo(map);
6435  * layer.addTo(map);
6436  * layer.remove();
6437  * ```
6438  *
6439  * @event add: Event
6440  * Fired after the layer is added to a map
6441  *
6442  * @event remove: Event
6443  * Fired after the layer is removed from a map
6444  */
6445
6446
6447 var Layer = Evented.extend({
6448
6449         // Classes extending `L.Layer` will inherit the following options:
6450         options: {
6451                 // @option pane: String = 'overlayPane'
6452                 // By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default.
6453                 pane: 'overlayPane',
6454
6455                 // @option attribution: String = null
6456                 // String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox".
6457                 attribution: null,
6458
6459                 bubblingMouseEvents: true
6460         },
6461
6462         /* @section
6463          * Classes extending `L.Layer` will inherit the following methods:
6464          *
6465          * @method addTo(map: Map|LayerGroup): this
6466          * Adds the layer to the given map or layer group.
6467          */
6468         addTo: function (map) {
6469                 map.addLayer(this);
6470                 return this;
6471         },
6472
6473         // @method remove: this
6474         // Removes the layer from the map it is currently active on.
6475         remove: function () {
6476                 return this.removeFrom(this._map || this._mapToAdd);
6477         },
6478
6479         // @method removeFrom(map: Map): this
6480         // Removes the layer from the given map
6481         removeFrom: function (obj) {
6482                 if (obj) {
6483                         obj.removeLayer(this);
6484                 }
6485                 return this;
6486         },
6487
6488         // @method getPane(name? : String): HTMLElement
6489         // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
6490         getPane: function (name) {
6491                 return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
6492         },
6493
6494         addInteractiveTarget: function (targetEl) {
6495                 this._map._targets[stamp(targetEl)] = this;
6496                 return this;
6497         },
6498
6499         removeInteractiveTarget: function (targetEl) {
6500                 delete this._map._targets[stamp(targetEl)];
6501                 return this;
6502         },
6503
6504         // @method getAttribution: String
6505         // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
6506         getAttribution: function () {
6507                 return this.options.attribution;
6508         },
6509
6510         _layerAdd: function (e) {
6511                 var map = e.target;
6512
6513                 // check in case layer gets added and then removed before the map is ready
6514                 if (!map.hasLayer(this)) { return; }
6515
6516                 this._map = map;
6517                 this._zoomAnimated = map._zoomAnimated;
6518
6519                 if (this.getEvents) {
6520                         var events = this.getEvents();
6521                         map.on(events, this);
6522                         this.once('remove', function () {
6523                                 map.off(events, this);
6524                         }, this);
6525                 }
6526
6527                 this.onAdd(map);
6528
6529                 if (this.getAttribution && map.attributionControl) {
6530                         map.attributionControl.addAttribution(this.getAttribution());
6531                 }
6532
6533                 this.fire('add');
6534                 map.fire('layeradd', {layer: this});
6535         }
6536 });
6537
6538 /* @section Extension methods
6539  * @uninheritable
6540  *
6541  * Every layer should extend from `L.Layer` and (re-)implement the following methods.
6542  *
6543  * @method onAdd(map: Map): this
6544  * Should contain code that creates DOM elements for the layer, adds them to `map panes` where they should belong and puts listeners on relevant map events. Called on [`map.addLayer(layer)`](#map-addlayer).
6545  *
6546  * @method onRemove(map: Map): this
6547  * Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer).
6548  *
6549  * @method getEvents(): Object
6550  * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer.
6551  *
6552  * @method getAttribution(): String
6553  * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
6554  *
6555  * @method beforeAdd(map: Map): this
6556  * Optional method. Called on [`map.addLayer(layer)`](#map-addlayer), before the layer is added to the map, before events are initialized, without waiting until the map is in a usable state. Use for early initialization only.
6557  */
6558
6559
6560 /* @namespace Map
6561  * @section Layer events
6562  *
6563  * @event layeradd: LayerEvent
6564  * Fired when a new layer is added to the map.
6565  *
6566  * @event layerremove: LayerEvent
6567  * Fired when some layer is removed from the map
6568  *
6569  * @section Methods for Layers and Controls
6570  */
6571 Map.include({
6572         // @method addLayer(layer: Layer): this
6573         // Adds the given layer to the map
6574         addLayer: function (layer) {
6575                 if (!layer._layerAdd) {
6576                         throw new Error('The provided object is not a Layer.');
6577                 }
6578
6579                 var id = stamp(layer);
6580                 if (this._layers[id]) { return this; }
6581                 this._layers[id] = layer;
6582
6583                 layer._mapToAdd = this;
6584
6585                 if (layer.beforeAdd) {
6586                         layer.beforeAdd(this);
6587                 }
6588
6589                 this.whenReady(layer._layerAdd, layer);
6590
6591                 return this;
6592         },
6593
6594         // @method removeLayer(layer: Layer): this
6595         // Removes the given layer from the map.
6596         removeLayer: function (layer) {
6597                 var id = stamp(layer);
6598
6599                 if (!this._layers[id]) { return this; }
6600
6601                 if (this._loaded) {
6602                         layer.onRemove(this);
6603                 }
6604
6605                 if (layer.getAttribution && this.attributionControl) {
6606                         this.attributionControl.removeAttribution(layer.getAttribution());
6607                 }
6608
6609                 delete this._layers[id];
6610
6611                 if (this._loaded) {
6612                         this.fire('layerremove', {layer: layer});
6613                         layer.fire('remove');
6614                 }
6615
6616                 layer._map = layer._mapToAdd = null;
6617
6618                 return this;
6619         },
6620
6621         // @method hasLayer(layer: Layer): Boolean
6622         // Returns `true` if the given layer is currently added to the map
6623         hasLayer: function (layer) {
6624                 return !!layer && (stamp(layer) in this._layers);
6625         },
6626
6627         /* @method eachLayer(fn: Function, context?: Object): this
6628          * Iterates over the layers of the map, optionally specifying context of the iterator function.
6629          * ```
6630          * map.eachLayer(function(layer){
6631          *     layer.bindPopup('Hello');
6632          * });
6633          * ```
6634          */
6635         eachLayer: function (method, context) {
6636                 for (var i in this._layers) {
6637                         method.call(context, this._layers[i]);
6638                 }
6639                 return this;
6640         },
6641
6642         _addLayers: function (layers) {
6643                 layers = layers ? (isArray(layers) ? layers : [layers]) : [];
6644
6645                 for (var i = 0, len = layers.length; i < len; i++) {
6646                         this.addLayer(layers[i]);
6647                 }
6648         },
6649
6650         _addZoomLimit: function (layer) {
6651                 if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
6652                         this._zoomBoundLayers[stamp(layer)] = layer;
6653                         this._updateZoomLevels();
6654                 }
6655         },
6656
6657         _removeZoomLimit: function (layer) {
6658                 var id = stamp(layer);
6659
6660                 if (this._zoomBoundLayers[id]) {
6661                         delete this._zoomBoundLayers[id];
6662                         this._updateZoomLevels();
6663                 }
6664         },
6665
6666         _updateZoomLevels: function () {
6667                 var minZoom = Infinity,
6668                     maxZoom = -Infinity,
6669                     oldZoomSpan = this._getZoomSpan();
6670
6671                 for (var i in this._zoomBoundLayers) {
6672                         var options = this._zoomBoundLayers[i].options;
6673
6674                         minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
6675                         maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
6676                 }
6677
6678                 this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
6679                 this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
6680
6681                 // @section Map state change events
6682                 // @event zoomlevelschange: Event
6683                 // Fired when the number of zoomlevels on the map is changed due
6684                 // to adding or removing a layer.
6685                 if (oldZoomSpan !== this._getZoomSpan()) {
6686                         this.fire('zoomlevelschange');
6687                 }
6688
6689                 if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
6690                         this.setZoom(this._layersMaxZoom);
6691                 }
6692                 if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
6693                         this.setZoom(this._layersMinZoom);
6694                 }
6695         }
6696 });
6697
6698 /*
6699  * @class LayerGroup
6700  * @aka L.LayerGroup
6701  * @inherits Layer
6702  *
6703  * Used to group several layers and handle them as one. If you add it to the map,
6704  * any layers added or removed from the group will be added/removed on the map as
6705  * well. Extends `Layer`.
6706  *
6707  * @example
6708  *
6709  * ```js
6710  * L.layerGroup([marker1, marker2])
6711  *      .addLayer(polyline)
6712  *      .addTo(map);
6713  * ```
6714  */
6715
6716 var LayerGroup = Layer.extend({
6717
6718         initialize: function (layers, options) {
6719                 setOptions(this, options);
6720
6721                 this._layers = {};
6722
6723                 var i, len;
6724
6725                 if (layers) {
6726                         for (i = 0, len = layers.length; i < len; i++) {
6727                                 this.addLayer(layers[i]);
6728                         }
6729                 }
6730         },
6731
6732         // @method addLayer(layer: Layer): this
6733         // Adds the given layer to the group.
6734         addLayer: function (layer) {
6735                 var id = this.getLayerId(layer);
6736
6737                 this._layers[id] = layer;
6738
6739                 if (this._map) {
6740                         this._map.addLayer(layer);
6741                 }
6742
6743                 return this;
6744         },
6745
6746         // @method removeLayer(layer: Layer): this
6747         // Removes the given layer from the group.
6748         // @alternative
6749         // @method removeLayer(id: Number): this
6750         // Removes the layer with the given internal ID from the group.
6751         removeLayer: function (layer) {
6752                 var id = layer in this._layers ? layer : this.getLayerId(layer);
6753
6754                 if (this._map && this._layers[id]) {
6755                         this._map.removeLayer(this._layers[id]);
6756                 }
6757
6758                 delete this._layers[id];
6759
6760                 return this;
6761         },
6762
6763         // @method hasLayer(layer: Layer): Boolean
6764         // Returns `true` if the given layer is currently added to the group.
6765         // @alternative
6766         // @method hasLayer(id: Number): Boolean
6767         // Returns `true` if the given internal ID is currently added to the group.
6768         hasLayer: function (layer) {
6769                 return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
6770         },
6771
6772         // @method clearLayers(): this
6773         // Removes all the layers from the group.
6774         clearLayers: function () {
6775                 return this.eachLayer(this.removeLayer, this);
6776         },
6777
6778         // @method invoke(methodName: String, …): this
6779         // Calls `methodName` on every layer contained in this group, passing any
6780         // additional parameters. Has no effect if the layers contained do not
6781         // implement `methodName`.
6782         invoke: function (methodName) {
6783                 var args = Array.prototype.slice.call(arguments, 1),
6784                     i, layer;
6785
6786                 for (i in this._layers) {
6787                         layer = this._layers[i];
6788
6789                         if (layer[methodName]) {
6790                                 layer[methodName].apply(layer, args);
6791                         }
6792                 }
6793
6794                 return this;
6795         },
6796
6797         onAdd: function (map) {
6798                 this.eachLayer(map.addLayer, map);
6799         },
6800
6801         onRemove: function (map) {
6802                 this.eachLayer(map.removeLayer, map);
6803         },
6804
6805         // @method eachLayer(fn: Function, context?: Object): this
6806         // Iterates over the layers of the group, optionally specifying context of the iterator function.
6807         // ```js
6808         // group.eachLayer(function (layer) {
6809         //      layer.bindPopup('Hello');
6810         // });
6811         // ```
6812         eachLayer: function (method, context) {
6813                 for (var i in this._layers) {
6814                         method.call(context, this._layers[i]);
6815                 }
6816                 return this;
6817         },
6818
6819         // @method getLayer(id: Number): Layer
6820         // Returns the layer with the given internal ID.
6821         getLayer: function (id) {
6822                 return this._layers[id];
6823         },
6824
6825         // @method getLayers(): Layer[]
6826         // Returns an array of all the layers added to the group.
6827         getLayers: function () {
6828                 var layers = [];
6829                 this.eachLayer(layers.push, layers);
6830                 return layers;
6831         },
6832
6833         // @method setZIndex(zIndex: Number): this
6834         // Calls `setZIndex` on every layer contained in this group, passing the z-index.
6835         setZIndex: function (zIndex) {
6836                 return this.invoke('setZIndex', zIndex);
6837         },
6838
6839         // @method getLayerId(layer: Layer): Number
6840         // Returns the internal ID for a layer
6841         getLayerId: function (layer) {
6842                 return stamp(layer);
6843         }
6844 });
6845
6846
6847 // @factory L.layerGroup(layers?: Layer[], options?: Object)
6848 // Create a layer group, optionally given an initial set of layers and an `options` object.
6849 var layerGroup = function (layers, options) {
6850         return new LayerGroup(layers, options);
6851 };
6852
6853 /*
6854  * @class FeatureGroup
6855  * @aka L.FeatureGroup
6856  * @inherits LayerGroup
6857  *
6858  * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
6859  *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
6860  *  * Events are propagated to the `FeatureGroup`, so if the group has an event
6861  * handler, it will handle events from any of the layers. This includes mouse events
6862  * and custom events.
6863  *  * Has `layeradd` and `layerremove` events
6864  *
6865  * @example
6866  *
6867  * ```js
6868  * L.featureGroup([marker1, marker2, polyline])
6869  *      .bindPopup('Hello world!')
6870  *      .on('click', function() { alert('Clicked on a member of the group!'); })
6871  *      .addTo(map);
6872  * ```
6873  */
6874
6875 var FeatureGroup = LayerGroup.extend({
6876
6877         addLayer: function (layer) {
6878                 if (this.hasLayer(layer)) {
6879                         return this;
6880                 }
6881
6882                 layer.addEventParent(this);
6883
6884                 LayerGroup.prototype.addLayer.call(this, layer);
6885
6886                 // @event layeradd: LayerEvent
6887                 // Fired when a layer is added to this `FeatureGroup`
6888                 return this.fire('layeradd', {layer: layer});
6889         },
6890
6891         removeLayer: function (layer) {
6892                 if (!this.hasLayer(layer)) {
6893                         return this;
6894                 }
6895                 if (layer in this._layers) {
6896                         layer = this._layers[layer];
6897                 }
6898
6899                 layer.removeEventParent(this);
6900
6901                 LayerGroup.prototype.removeLayer.call(this, layer);
6902
6903                 // @event layerremove: LayerEvent
6904                 // Fired when a layer is removed from this `FeatureGroup`
6905                 return this.fire('layerremove', {layer: layer});
6906         },
6907
6908         // @method setStyle(style: Path options): this
6909         // Sets the given path options to each layer of the group that has a `setStyle` method.
6910         setStyle: function (style) {
6911                 return this.invoke('setStyle', style);
6912         },
6913
6914         // @method bringToFront(): this
6915         // Brings the layer group to the top of all other layers
6916         bringToFront: function () {
6917                 return this.invoke('bringToFront');
6918         },
6919
6920         // @method bringToBack(): this
6921         // Brings the layer group to the back of all other layers
6922         bringToBack: function () {
6923                 return this.invoke('bringToBack');
6924         },
6925
6926         // @method getBounds(): LatLngBounds
6927         // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
6928         getBounds: function () {
6929                 var bounds = new LatLngBounds();
6930
6931                 for (var id in this._layers) {
6932                         var layer = this._layers[id];
6933                         bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
6934                 }
6935                 return bounds;
6936         }
6937 });
6938
6939 // @factory L.featureGroup(layers: Layer[])
6940 // Create a feature group, optionally given an initial set of layers.
6941 var featureGroup = function (layers) {
6942         return new FeatureGroup(layers);
6943 };
6944
6945 /*
6946  * @class Icon
6947  * @aka L.Icon
6948  *
6949  * Represents an icon to provide when creating a marker.
6950  *
6951  * @example
6952  *
6953  * ```js
6954  * var myIcon = L.icon({
6955  *     iconUrl: 'my-icon.png',
6956  *     iconRetinaUrl: 'my-icon@2x.png',
6957  *     iconSize: [38, 95],
6958  *     iconAnchor: [22, 94],
6959  *     popupAnchor: [-3, -76],
6960  *     shadowUrl: 'my-icon-shadow.png',
6961  *     shadowRetinaUrl: 'my-icon-shadow@2x.png',
6962  *     shadowSize: [68, 95],
6963  *     shadowAnchor: [22, 94]
6964  * });
6965  *
6966  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
6967  * ```
6968  *
6969  * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
6970  *
6971  */
6972
6973 var Icon = Class.extend({
6974
6975         /* @section
6976          * @aka Icon options
6977          *
6978          * @option iconUrl: String = null
6979          * **(required)** The URL to the icon image (absolute or relative to your script path).
6980          *
6981          * @option iconRetinaUrl: String = null
6982          * The URL to a retina sized version of the icon image (absolute or relative to your
6983          * script path). Used for Retina screen devices.
6984          *
6985          * @option iconSize: Point = null
6986          * Size of the icon image in pixels.
6987          *
6988          * @option iconAnchor: Point = null
6989          * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
6990          * will be aligned so that this point is at the marker's geographical location. Centered
6991          * by default if size is specified, also can be set in CSS with negative margins.
6992          *
6993          * @option popupAnchor: Point = [0, 0]
6994          * The coordinates of the point from which popups will "open", relative to the icon anchor.
6995          *
6996          * @option tooltipAnchor: Point = [0, 0]
6997          * The coordinates of the point from which tooltips will "open", relative to the icon anchor.
6998          *
6999          * @option shadowUrl: String = null
7000          * The URL to the icon shadow image. If not specified, no shadow image will be created.
7001          *
7002          * @option shadowRetinaUrl: String = null
7003          *
7004          * @option shadowSize: Point = null
7005          * Size of the shadow image in pixels.
7006          *
7007          * @option shadowAnchor: Point = null
7008          * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
7009          * as iconAnchor if not specified).
7010          *
7011          * @option className: String = ''
7012          * A custom class name to assign to both icon and shadow images. Empty by default.
7013          */
7014
7015         options: {
7016                 popupAnchor: [0, 0],
7017                 tooltipAnchor: [0, 0],
7018         },
7019
7020         initialize: function (options) {
7021                 setOptions(this, options);
7022         },
7023
7024         // @method createIcon(oldIcon?: HTMLElement): HTMLElement
7025         // Called internally when the icon has to be shown, returns a `<img>` HTML element
7026         // styled according to the options.
7027         createIcon: function (oldIcon) {
7028                 return this._createIcon('icon', oldIcon);
7029         },
7030
7031         // @method createShadow(oldIcon?: HTMLElement): HTMLElement
7032         // As `createIcon`, but for the shadow beneath it.
7033         createShadow: function (oldIcon) {
7034                 return this._createIcon('shadow', oldIcon);
7035         },
7036
7037         _createIcon: function (name, oldIcon) {
7038                 var src = this._getIconUrl(name);
7039
7040                 if (!src) {
7041                         if (name === 'icon') {
7042                                 throw new Error('iconUrl not set in Icon options (see the docs).');
7043                         }
7044                         return null;
7045                 }
7046
7047                 var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
7048                 this._setIconStyles(img, name);
7049
7050                 return img;
7051         },
7052
7053         _setIconStyles: function (img, name) {
7054                 var options = this.options;
7055                 var sizeOption = options[name + 'Size'];
7056
7057                 if (typeof sizeOption === 'number') {
7058                         sizeOption = [sizeOption, sizeOption];
7059                 }
7060
7061                 var size = toPoint(sizeOption),
7062                     anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
7063                             size && size.divideBy(2, true));
7064
7065                 img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
7066
7067                 if (anchor) {
7068                         img.style.marginLeft = (-anchor.x) + 'px';
7069                         img.style.marginTop  = (-anchor.y) + 'px';
7070                 }
7071
7072                 if (size) {
7073                         img.style.width  = size.x + 'px';
7074                         img.style.height = size.y + 'px';
7075                 }
7076         },
7077
7078         _createImg: function (src, el) {
7079                 el = el || document.createElement('img');
7080                 el.src = src;
7081                 return el;
7082         },
7083
7084         _getIconUrl: function (name) {
7085                 return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
7086         }
7087 });
7088
7089
7090 // @factory L.icon(options: Icon options)
7091 // Creates an icon instance with the given options.
7092 function icon(options) {
7093         return new Icon(options);
7094 }
7095
7096 /*
7097  * @miniclass Icon.Default (Icon)
7098  * @aka L.Icon.Default
7099  * @section
7100  *
7101  * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
7102  * no icon is specified. Points to the blue marker image distributed with Leaflet
7103  * releases.
7104  *
7105  * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
7106  * (which is a set of `Icon options`).
7107  *
7108  * If you want to _completely_ replace the default icon, override the
7109  * `L.Marker.prototype.options.icon` with your own icon instead.
7110  */
7111
7112 var IconDefault = Icon.extend({
7113
7114         options: {
7115                 iconUrl:       'marker-icon.png',
7116                 iconRetinaUrl: 'marker-icon-2x.png',
7117                 shadowUrl:     'marker-shadow.png',
7118                 iconSize:    [25, 41],
7119                 iconAnchor:  [12, 41],
7120                 popupAnchor: [1, -34],
7121                 tooltipAnchor: [16, -28],
7122                 shadowSize:  [41, 41]
7123         },
7124
7125         _getIconUrl: function (name) {
7126                 if (!IconDefault.imagePath) {   // Deprecated, backwards-compatibility only
7127                         IconDefault.imagePath = this._detectIconPath();
7128                 }
7129
7130                 // @option imagePath: String
7131                 // `Icon.Default` will try to auto-detect the location of the
7132                 // blue icon images. If you are placing these images in a non-standard
7133                 // way, set this option to point to the right path.
7134                 return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
7135         },
7136
7137         _detectIconPath: function () {
7138                 var el = create$1('div',  'leaflet-default-icon-path', document.body);
7139                 var path = getStyle(el, 'background-image') ||
7140                            getStyle(el, 'backgroundImage');     // IE8
7141
7142                 document.body.removeChild(el);
7143
7144                 if (path === null || path.indexOf('url') !== 0) {
7145                         path = '';
7146                 } else {
7147                         path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, '');
7148                 }
7149
7150                 return path;
7151         }
7152 });
7153
7154 /*
7155  * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
7156  */
7157
7158
7159 /* @namespace Marker
7160  * @section Interaction handlers
7161  *
7162  * Interaction handlers are properties of a marker instance that allow you to control interaction behavior in runtime, enabling or disabling certain features such as dragging (see `Handler` methods). Example:
7163  *
7164  * ```js
7165  * marker.dragging.disable();
7166  * ```
7167  *
7168  * @property dragging: Handler
7169  * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
7170  */
7171
7172 var MarkerDrag = Handler.extend({
7173         initialize: function (marker) {
7174                 this._marker = marker;
7175         },
7176
7177         addHooks: function () {
7178                 var icon = this._marker._icon;
7179
7180                 if (!this._draggable) {
7181                         this._draggable = new Draggable(icon, icon, true);
7182                 }
7183
7184                 this._draggable.on({
7185                         dragstart: this._onDragStart,
7186                         predrag: this._onPreDrag,
7187                         drag: this._onDrag,
7188                         dragend: this._onDragEnd
7189                 }, this).enable();
7190
7191                 addClass(icon, 'leaflet-marker-draggable');
7192         },
7193
7194         removeHooks: function () {
7195                 this._draggable.off({
7196                         dragstart: this._onDragStart,
7197                         predrag: this._onPreDrag,
7198                         drag: this._onDrag,
7199                         dragend: this._onDragEnd
7200                 }, this).disable();
7201
7202                 if (this._marker._icon) {
7203                         removeClass(this._marker._icon, 'leaflet-marker-draggable');
7204                 }
7205         },
7206
7207         moved: function () {
7208                 return this._draggable && this._draggable._moved;
7209         },
7210
7211         _adjustPan: function (e) {
7212                 var marker = this._marker,
7213                     map = marker._map,
7214                     speed = this._marker.options.autoPanSpeed,
7215                     padding = this._marker.options.autoPanPadding,
7216                     iconPos = getPosition(marker._icon),
7217                     bounds = map.getPixelBounds(),
7218                     origin = map.getPixelOrigin();
7219
7220                 var panBounds = toBounds(
7221                         bounds.min._subtract(origin).add(padding),
7222                         bounds.max._subtract(origin).subtract(padding)
7223                 );
7224
7225                 if (!panBounds.contains(iconPos)) {
7226                         // Compute incremental movement
7227                         var movement = toPoint(
7228                                 (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) -
7229                                 (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x),
7230
7231                                 (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) -
7232                                 (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y)
7233                         ).multiplyBy(speed);
7234
7235                         map.panBy(movement, {animate: false});
7236
7237                         this._draggable._newPos._add(movement);
7238                         this._draggable._startPos._add(movement);
7239
7240                         setPosition(marker._icon, this._draggable._newPos);
7241                         this._onDrag(e);
7242
7243                         this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7244                 }
7245         },
7246
7247         _onDragStart: function () {
7248                 // @section Dragging events
7249                 // @event dragstart: Event
7250                 // Fired when the user starts dragging the marker.
7251
7252                 // @event movestart: Event
7253                 // Fired when the marker starts moving (because of dragging).
7254
7255                 this._oldLatLng = this._marker.getLatLng();
7256                 this._marker
7257                     .closePopup()
7258                     .fire('movestart')
7259                     .fire('dragstart');
7260         },
7261
7262         _onPreDrag: function (e) {
7263                 if (this._marker.options.autoPan) {
7264                         cancelAnimFrame(this._panRequest);
7265                         this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7266                 }
7267         },
7268
7269         _onDrag: function (e) {
7270                 var marker = this._marker,
7271                     shadow = marker._shadow,
7272                     iconPos = getPosition(marker._icon),
7273                     latlng = marker._map.layerPointToLatLng(iconPos);
7274
7275                 // update shadow position
7276                 if (shadow) {
7277                         setPosition(shadow, iconPos);
7278                 }
7279
7280                 marker._latlng = latlng;
7281                 e.latlng = latlng;
7282                 e.oldLatLng = this._oldLatLng;
7283
7284                 // @event drag: Event
7285                 // Fired repeatedly while the user drags the marker.
7286                 marker
7287                     .fire('move', e)
7288                     .fire('drag', e);
7289         },
7290
7291         _onDragEnd: function (e) {
7292                 // @event dragend: DragEndEvent
7293                 // Fired when the user stops dragging the marker.
7294
7295                  cancelAnimFrame(this._panRequest);
7296
7297                 // @event moveend: Event
7298                 // Fired when the marker stops moving (because of dragging).
7299                 delete this._oldLatLng;
7300                 this._marker
7301                     .fire('moveend')
7302                     .fire('dragend', e);
7303         }
7304 });
7305
7306 /*
7307  * @class Marker
7308  * @inherits Interactive layer
7309  * @aka L.Marker
7310  * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
7311  *
7312  * @example
7313  *
7314  * ```js
7315  * L.marker([50.5, 30.5]).addTo(map);
7316  * ```
7317  */
7318
7319 var Marker = Layer.extend({
7320
7321         // @section
7322         // @aka Marker options
7323         options: {
7324                 // @option icon: Icon = *
7325                 // Icon instance to use for rendering the marker.
7326                 // See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
7327                 // If not specified, a common instance of `L.Icon.Default` is used.
7328                 icon: new IconDefault(),
7329
7330                 // Option inherited from "Interactive layer" abstract class
7331                 interactive: true,
7332
7333                 // @option draggable: Boolean = false
7334                 // Whether the marker is draggable with mouse/touch or not.
7335                 draggable: false,
7336
7337                 // @option autoPan: Boolean = false
7338                 // Set it to `true` if you want the map to do panning animation when marker hits the edges.
7339                 autoPan: false,
7340
7341                 // @option autoPanPadding: Point = Point(50, 50)
7342                 // Equivalent of setting both top left and bottom right autopan padding to the same value.
7343                 autoPanPadding: [50, 50],
7344
7345                 // @option autoPanSpeed: Number = 10
7346                 // Number of pixels the map should move by.
7347                 autoPanSpeed: 10,
7348
7349                 // @option keyboard: Boolean = true
7350                 // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
7351                 keyboard: true,
7352
7353                 // @option title: String = ''
7354                 // Text for the browser tooltip that appear on marker hover (no tooltip by default).
7355                 title: '',
7356
7357                 // @option alt: String = ''
7358                 // Text for the `alt` attribute of the icon image (useful for accessibility).
7359                 alt: '',
7360
7361                 // @option zIndexOffset: Number = 0
7362                 // By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively).
7363                 zIndexOffset: 0,
7364
7365                 // @option opacity: Number = 1.0
7366                 // The opacity of the marker.
7367                 opacity: 1,
7368
7369                 // @option riseOnHover: Boolean = false
7370                 // If `true`, the marker will get on top of others when you hover the mouse over it.
7371                 riseOnHover: false,
7372
7373                 // @option riseOffset: Number = 250
7374                 // The z-index offset used for the `riseOnHover` feature.
7375                 riseOffset: 250,
7376
7377                 // @option pane: String = 'markerPane'
7378                 // `Map pane` where the markers icon will be added.
7379                 pane: 'markerPane',
7380
7381                 // @option bubblingMouseEvents: Boolean = false
7382                 // When `true`, a mouse event on this marker will trigger the same event on the map
7383                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7384                 bubblingMouseEvents: false
7385         },
7386
7387         /* @section
7388          *
7389          * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
7390          */
7391
7392         initialize: function (latlng, options) {
7393                 setOptions(this, options);
7394                 this._latlng = toLatLng(latlng);
7395         },
7396
7397         onAdd: function (map) {
7398                 this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
7399
7400                 if (this._zoomAnimated) {
7401                         map.on('zoomanim', this._animateZoom, this);
7402                 }
7403
7404                 this._initIcon();
7405                 this.update();
7406         },
7407
7408         onRemove: function (map) {
7409                 if (this.dragging && this.dragging.enabled()) {
7410                         this.options.draggable = true;
7411                         this.dragging.removeHooks();
7412                 }
7413                 delete this.dragging;
7414
7415                 if (this._zoomAnimated) {
7416                         map.off('zoomanim', this._animateZoom, this);
7417                 }
7418
7419                 this._removeIcon();
7420                 this._removeShadow();
7421         },
7422
7423         getEvents: function () {
7424                 return {
7425                         zoom: this.update,
7426                         viewreset: this.update
7427                 };
7428         },
7429
7430         // @method getLatLng: LatLng
7431         // Returns the current geographical position of the marker.
7432         getLatLng: function () {
7433                 return this._latlng;
7434         },
7435
7436         // @method setLatLng(latlng: LatLng): this
7437         // Changes the marker position to the given point.
7438         setLatLng: function (latlng) {
7439                 var oldLatLng = this._latlng;
7440                 this._latlng = toLatLng(latlng);
7441                 this.update();
7442
7443                 // @event move: Event
7444                 // Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
7445                 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
7446         },
7447
7448         // @method setZIndexOffset(offset: Number): this
7449         // Changes the [zIndex offset](#marker-zindexoffset) of the marker.
7450         setZIndexOffset: function (offset) {
7451                 this.options.zIndexOffset = offset;
7452                 return this.update();
7453         },
7454
7455         // @method setIcon(icon: Icon): this
7456         // Changes the marker icon.
7457         setIcon: function (icon) {
7458
7459                 this.options.icon = icon;
7460
7461                 if (this._map) {
7462                         this._initIcon();
7463                         this.update();
7464                 }
7465
7466                 if (this._popup) {
7467                         this.bindPopup(this._popup, this._popup.options);
7468                 }
7469
7470                 return this;
7471         },
7472
7473         getElement: function () {
7474                 return this._icon;
7475         },
7476
7477         update: function () {
7478
7479                 if (this._icon && this._map) {
7480                         var pos = this._map.latLngToLayerPoint(this._latlng).round();
7481                         this._setPos(pos);
7482                 }
7483
7484                 return this;
7485         },
7486
7487         _initIcon: function () {
7488                 var options = this.options,
7489                     classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
7490
7491                 var icon = options.icon.createIcon(this._icon),
7492                     addIcon = false;
7493
7494                 // if we're not reusing the icon, remove the old one and init new one
7495                 if (icon !== this._icon) {
7496                         if (this._icon) {
7497                                 this._removeIcon();
7498                         }
7499                         addIcon = true;
7500
7501                         if (options.title) {
7502                                 icon.title = options.title;
7503                         }
7504
7505                         if (icon.tagName === 'IMG') {
7506                                 icon.alt = options.alt || '';
7507                         }
7508                 }
7509
7510                 addClass(icon, classToAdd);
7511
7512                 if (options.keyboard) {
7513                         icon.tabIndex = '0';
7514                 }
7515
7516                 this._icon = icon;
7517
7518                 if (options.riseOnHover) {
7519                         this.on({
7520                                 mouseover: this._bringToFront,
7521                                 mouseout: this._resetZIndex
7522                         });
7523                 }
7524
7525                 var newShadow = options.icon.createShadow(this._shadow),
7526                     addShadow = false;
7527
7528                 if (newShadow !== this._shadow) {
7529                         this._removeShadow();
7530                         addShadow = true;
7531                 }
7532
7533                 if (newShadow) {
7534                         addClass(newShadow, classToAdd);
7535                         newShadow.alt = '';
7536                 }
7537                 this._shadow = newShadow;
7538
7539
7540                 if (options.opacity < 1) {
7541                         this._updateOpacity();
7542                 }
7543
7544
7545                 if (addIcon) {
7546                         this.getPane().appendChild(this._icon);
7547                 }
7548                 this._initInteraction();
7549                 if (newShadow && addShadow) {
7550                         this.getPane('shadowPane').appendChild(this._shadow);
7551                 }
7552         },
7553
7554         _removeIcon: function () {
7555                 if (this.options.riseOnHover) {
7556                         this.off({
7557                                 mouseover: this._bringToFront,
7558                                 mouseout: this._resetZIndex
7559                         });
7560                 }
7561
7562                 remove(this._icon);
7563                 this.removeInteractiveTarget(this._icon);
7564
7565                 this._icon = null;
7566         },
7567
7568         _removeShadow: function () {
7569                 if (this._shadow) {
7570                         remove(this._shadow);
7571                 }
7572                 this._shadow = null;
7573         },
7574
7575         _setPos: function (pos) {
7576                 setPosition(this._icon, pos);
7577
7578                 if (this._shadow) {
7579                         setPosition(this._shadow, pos);
7580                 }
7581
7582                 this._zIndex = pos.y + this.options.zIndexOffset;
7583
7584                 this._resetZIndex();
7585         },
7586
7587         _updateZIndex: function (offset) {
7588                 this._icon.style.zIndex = this._zIndex + offset;
7589         },
7590
7591         _animateZoom: function (opt) {
7592                 var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
7593
7594                 this._setPos(pos);
7595         },
7596
7597         _initInteraction: function () {
7598
7599                 if (!this.options.interactive) { return; }
7600
7601                 addClass(this._icon, 'leaflet-interactive');
7602
7603                 this.addInteractiveTarget(this._icon);
7604
7605                 if (MarkerDrag) {
7606                         var draggable = this.options.draggable;
7607                         if (this.dragging) {
7608                                 draggable = this.dragging.enabled();
7609                                 this.dragging.disable();
7610                         }
7611
7612                         this.dragging = new MarkerDrag(this);
7613
7614                         if (draggable) {
7615                                 this.dragging.enable();
7616                         }
7617                 }
7618         },
7619
7620         // @method setOpacity(opacity: Number): this
7621         // Changes the opacity of the marker.
7622         setOpacity: function (opacity) {
7623                 this.options.opacity = opacity;
7624                 if (this._map) {
7625                         this._updateOpacity();
7626                 }
7627
7628                 return this;
7629         },
7630
7631         _updateOpacity: function () {
7632                 var opacity = this.options.opacity;
7633
7634                 setOpacity(this._icon, opacity);
7635
7636                 if (this._shadow) {
7637                         setOpacity(this._shadow, opacity);
7638                 }
7639         },
7640
7641         _bringToFront: function () {
7642                 this._updateZIndex(this.options.riseOffset);
7643         },
7644
7645         _resetZIndex: function () {
7646                 this._updateZIndex(0);
7647         },
7648
7649         _getPopupAnchor: function () {
7650                 return this.options.icon.options.popupAnchor;
7651         },
7652
7653         _getTooltipAnchor: function () {
7654                 return this.options.icon.options.tooltipAnchor;
7655         }
7656 });
7657
7658
7659 // factory L.marker(latlng: LatLng, options? : Marker options)
7660
7661 // @factory L.marker(latlng: LatLng, options? : Marker options)
7662 // Instantiates a Marker object given a geographical point and optionally an options object.
7663 function marker(latlng, options) {
7664         return new Marker(latlng, options);
7665 }
7666
7667 /*
7668  * @class Path
7669  * @aka L.Path
7670  * @inherits Interactive layer
7671  *
7672  * An abstract class that contains options and constants shared between vector
7673  * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
7674  */
7675
7676 var Path = Layer.extend({
7677
7678         // @section
7679         // @aka Path options
7680         options: {
7681                 // @option stroke: Boolean = true
7682                 // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
7683                 stroke: true,
7684
7685                 // @option color: String = '#3388ff'
7686                 // Stroke color
7687                 color: '#3388ff',
7688
7689                 // @option weight: Number = 3
7690                 // Stroke width in pixels
7691                 weight: 3,
7692
7693                 // @option opacity: Number = 1.0
7694                 // Stroke opacity
7695                 opacity: 1,
7696
7697                 // @option lineCap: String= 'round'
7698                 // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
7699                 lineCap: 'round',
7700
7701                 // @option lineJoin: String = 'round'
7702                 // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
7703                 lineJoin: 'round',
7704
7705                 // @option dashArray: String = null
7706                 // A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
7707                 dashArray: null,
7708
7709                 // @option dashOffset: String = null
7710                 // A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
7711                 dashOffset: null,
7712
7713                 // @option fill: Boolean = depends
7714                 // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
7715                 fill: false,
7716
7717                 // @option fillColor: String = *
7718                 // Fill color. Defaults to the value of the [`color`](#path-color) option
7719                 fillColor: null,
7720
7721                 // @option fillOpacity: Number = 0.2
7722                 // Fill opacity.
7723                 fillOpacity: 0.2,
7724
7725                 // @option fillRule: String = 'evenodd'
7726                 // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
7727                 fillRule: 'evenodd',
7728
7729                 // className: '',
7730
7731                 // Option inherited from "Interactive layer" abstract class
7732                 interactive: true,
7733
7734                 // @option bubblingMouseEvents: Boolean = true
7735                 // When `true`, a mouse event on this path will trigger the same event on the map
7736                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7737                 bubblingMouseEvents: true
7738         },
7739
7740         beforeAdd: function (map) {
7741                 // Renderer is set here because we need to call renderer.getEvents
7742                 // before this.getEvents.
7743                 this._renderer = map.getRenderer(this);
7744         },
7745
7746         onAdd: function () {
7747                 this._renderer._initPath(this);
7748                 this._reset();
7749                 this._renderer._addPath(this);
7750         },
7751
7752         onRemove: function () {
7753                 this._renderer._removePath(this);
7754         },
7755
7756         // @method redraw(): this
7757         // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
7758         redraw: function () {
7759                 if (this._map) {
7760                         this._renderer._updatePath(this);
7761                 }
7762                 return this;
7763         },
7764
7765         // @method setStyle(style: Path options): this
7766         // Changes the appearance of a Path based on the options in the `Path options` object.
7767         setStyle: function (style) {
7768                 setOptions(this, style);
7769                 if (this._renderer) {
7770                         this._renderer._updateStyle(this);
7771                 }
7772                 return this;
7773         },
7774
7775         // @method bringToFront(): this
7776         // Brings the layer to the top of all path layers.
7777         bringToFront: function () {
7778                 if (this._renderer) {
7779                         this._renderer._bringToFront(this);
7780                 }
7781                 return this;
7782         },
7783
7784         // @method bringToBack(): this
7785         // Brings the layer to the bottom of all path layers.
7786         bringToBack: function () {
7787                 if (this._renderer) {
7788                         this._renderer._bringToBack(this);
7789                 }
7790                 return this;
7791         },
7792
7793         getElement: function () {
7794                 return this._path;
7795         },
7796
7797         _reset: function () {
7798                 // defined in child classes
7799                 this._project();
7800                 this._update();
7801         },
7802
7803         _clickTolerance: function () {
7804                 // used when doing hit detection for Canvas layers
7805                 return (this.options.stroke ? this.options.weight / 2 : 0) + this._renderer.options.tolerance;
7806         }
7807 });
7808
7809 /*
7810  * @class CircleMarker
7811  * @aka L.CircleMarker
7812  * @inherits Path
7813  *
7814  * A circle of a fixed size with radius specified in pixels. Extends `Path`.
7815  */
7816
7817 var CircleMarker = Path.extend({
7818
7819         // @section
7820         // @aka CircleMarker options
7821         options: {
7822                 fill: true,
7823
7824                 // @option radius: Number = 10
7825                 // Radius of the circle marker, in pixels
7826                 radius: 10
7827         },
7828
7829         initialize: function (latlng, options) {
7830                 setOptions(this, options);
7831                 this._latlng = toLatLng(latlng);
7832                 this._radius = this.options.radius;
7833         },
7834
7835         // @method setLatLng(latLng: LatLng): this
7836         // Sets the position of a circle marker to a new location.
7837         setLatLng: function (latlng) {
7838                 this._latlng = toLatLng(latlng);
7839                 this.redraw();
7840                 return this.fire('move', {latlng: this._latlng});
7841         },
7842
7843         // @method getLatLng(): LatLng
7844         // Returns the current geographical position of the circle marker
7845         getLatLng: function () {
7846                 return this._latlng;
7847         },
7848
7849         // @method setRadius(radius: Number): this
7850         // Sets the radius of a circle marker. Units are in pixels.
7851         setRadius: function (radius) {
7852                 this.options.radius = this._radius = radius;
7853                 return this.redraw();
7854         },
7855
7856         // @method getRadius(): Number
7857         // Returns the current radius of the circle
7858         getRadius: function () {
7859                 return this._radius;
7860         },
7861
7862         setStyle : function (options) {
7863                 var radius = options && options.radius || this._radius;
7864                 Path.prototype.setStyle.call(this, options);
7865                 this.setRadius(radius);
7866                 return this;
7867         },
7868
7869         _project: function () {
7870                 this._point = this._map.latLngToLayerPoint(this._latlng);
7871                 this._updateBounds();
7872         },
7873
7874         _updateBounds: function () {
7875                 var r = this._radius,
7876                     r2 = this._radiusY || r,
7877                     w = this._clickTolerance(),
7878                     p = [r + w, r2 + w];
7879                 this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
7880         },
7881
7882         _update: function () {
7883                 if (this._map) {
7884                         this._updatePath();
7885                 }
7886         },
7887
7888         _updatePath: function () {
7889                 this._renderer._updateCircle(this);
7890         },
7891
7892         _empty: function () {
7893                 return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
7894         },
7895
7896         // Needed by the `Canvas` renderer for interactivity
7897         _containsPoint: function (p) {
7898                 return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
7899         }
7900 });
7901
7902
7903 // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
7904 // Instantiates a circle marker object given a geographical point, and an optional options object.
7905 function circleMarker(latlng, options) {
7906         return new CircleMarker(latlng, options);
7907 }
7908
7909 /*
7910  * @class Circle
7911  * @aka L.Circle
7912  * @inherits CircleMarker
7913  *
7914  * A class for drawing circle overlays on a map. Extends `CircleMarker`.
7915  *
7916  * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
7917  *
7918  * @example
7919  *
7920  * ```js
7921  * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
7922  * ```
7923  */
7924
7925 var Circle = CircleMarker.extend({
7926
7927         initialize: function (latlng, options, legacyOptions) {
7928                 if (typeof options === 'number') {
7929                         // Backwards compatibility with 0.7.x factory (latlng, radius, options?)
7930                         options = extend({}, legacyOptions, {radius: options});
7931                 }
7932                 setOptions(this, options);
7933                 this._latlng = toLatLng(latlng);
7934
7935                 if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
7936
7937                 // @section
7938                 // @aka Circle options
7939                 // @option radius: Number; Radius of the circle, in meters.
7940                 this._mRadius = this.options.radius;
7941         },
7942
7943         // @method setRadius(radius: Number): this
7944         // Sets the radius of a circle. Units are in meters.
7945         setRadius: function (radius) {
7946                 this._mRadius = radius;
7947                 return this.redraw();
7948         },
7949
7950         // @method getRadius(): Number
7951         // Returns the current radius of a circle. Units are in meters.
7952         getRadius: function () {
7953                 return this._mRadius;
7954         },
7955
7956         // @method getBounds(): LatLngBounds
7957         // Returns the `LatLngBounds` of the path.
7958         getBounds: function () {
7959                 var half = [this._radius, this._radiusY || this._radius];
7960
7961                 return new LatLngBounds(
7962                         this._map.layerPointToLatLng(this._point.subtract(half)),
7963                         this._map.layerPointToLatLng(this._point.add(half)));
7964         },
7965
7966         setStyle: Path.prototype.setStyle,
7967
7968         _project: function () {
7969
7970                 var lng = this._latlng.lng,
7971                     lat = this._latlng.lat,
7972                     map = this._map,
7973                     crs = map.options.crs;
7974
7975                 if (crs.distance === Earth.distance) {
7976                         var d = Math.PI / 180,
7977                             latR = (this._mRadius / Earth.R) / d,
7978                             top = map.project([lat + latR, lng]),
7979                             bottom = map.project([lat - latR, lng]),
7980                             p = top.add(bottom).divideBy(2),
7981                             lat2 = map.unproject(p).lat,
7982                             lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
7983                                     (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
7984
7985                         if (isNaN(lngR) || lngR === 0) {
7986                                 lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
7987                         }
7988
7989                         this._point = p.subtract(map.getPixelOrigin());
7990                         this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x;
7991                         this._radiusY = p.y - top.y;
7992
7993                 } else {
7994                         var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
7995
7996                         this._point = map.latLngToLayerPoint(this._latlng);
7997                         this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
7998                 }
7999
8000                 this._updateBounds();
8001         }
8002 });
8003
8004 // @factory L.circle(latlng: LatLng, options?: Circle options)
8005 // Instantiates a circle object given a geographical point, and an options object
8006 // which contains the circle radius.
8007 // @alternative
8008 // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
8009 // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
8010 // Do not use in new applications or plugins.
8011 function circle(latlng, options, legacyOptions) {
8012         return new Circle(latlng, options, legacyOptions);
8013 }
8014
8015 /*
8016  * @class Polyline
8017  * @aka L.Polyline
8018  * @inherits Path
8019  *
8020  * A class for drawing polyline overlays on a map. Extends `Path`.
8021  *
8022  * @example
8023  *
8024  * ```js
8025  * // create a red polyline from an array of LatLng points
8026  * var latlngs = [
8027  *      [45.51, -122.68],
8028  *      [37.77, -122.43],
8029  *      [34.04, -118.2]
8030  * ];
8031  *
8032  * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
8033  *
8034  * // zoom the map to the polyline
8035  * map.fitBounds(polyline.getBounds());
8036  * ```
8037  *
8038  * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
8039  *
8040  * ```js
8041  * // create a red polyline from an array of arrays of LatLng points
8042  * var latlngs = [
8043  *      [[45.51, -122.68],
8044  *       [37.77, -122.43],
8045  *       [34.04, -118.2]],
8046  *      [[40.78, -73.91],
8047  *       [41.83, -87.62],
8048  *       [32.76, -96.72]]
8049  * ];
8050  * ```
8051  */
8052
8053
8054 var Polyline = Path.extend({
8055
8056         // @section
8057         // @aka Polyline options
8058         options: {
8059                 // @option smoothFactor: Number = 1.0
8060                 // How much to simplify the polyline on each zoom level. More means
8061                 // better performance and smoother look, and less means more accurate representation.
8062                 smoothFactor: 1.0,
8063
8064                 // @option noClip: Boolean = false
8065                 // Disable polyline clipping.
8066                 noClip: false
8067         },
8068
8069         initialize: function (latlngs, options) {
8070                 setOptions(this, options);
8071                 this._setLatLngs(latlngs);
8072         },
8073
8074         // @method getLatLngs(): LatLng[]
8075         // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
8076         getLatLngs: function () {
8077                 return this._latlngs;
8078         },
8079
8080         // @method setLatLngs(latlngs: LatLng[]): this
8081         // Replaces all the points in the polyline with the given array of geographical points.
8082         setLatLngs: function (latlngs) {
8083                 this._setLatLngs(latlngs);
8084                 return this.redraw();
8085         },
8086
8087         // @method isEmpty(): Boolean
8088         // Returns `true` if the Polyline has no LatLngs.
8089         isEmpty: function () {
8090                 return !this._latlngs.length;
8091         },
8092
8093         // @method closestLayerPoint(p: Point): Point
8094         // Returns the point closest to `p` on the Polyline.
8095         closestLayerPoint: function (p) {
8096                 var minDistance = Infinity,
8097                     minPoint = null,
8098                     closest = _sqClosestPointOnSegment,
8099                     p1, p2;
8100
8101                 for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
8102                         var points = this._parts[j];
8103
8104                         for (var i = 1, len = points.length; i < len; i++) {
8105                                 p1 = points[i - 1];
8106                                 p2 = points[i];
8107
8108                                 var sqDist = closest(p, p1, p2, true);
8109
8110                                 if (sqDist < minDistance) {
8111                                         minDistance = sqDist;
8112                                         minPoint = closest(p, p1, p2);
8113                                 }
8114                         }
8115                 }
8116                 if (minPoint) {
8117                         minPoint.distance = Math.sqrt(minDistance);
8118                 }
8119                 return minPoint;
8120         },
8121
8122         // @method getCenter(): LatLng
8123         // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
8124         getCenter: function () {
8125                 // throws error when not yet added to map as this center calculation requires projected coordinates
8126                 if (!this._map) {
8127                         throw new Error('Must add layer to map before using getCenter()');
8128                 }
8129
8130                 var i, halfDist, segDist, dist, p1, p2, ratio,
8131                     points = this._rings[0],
8132                     len = points.length;
8133
8134                 if (!len) { return null; }
8135
8136                 // polyline centroid algorithm; only uses the first ring if there are multiple
8137
8138                 for (i = 0, halfDist = 0; i < len - 1; i++) {
8139                         halfDist += points[i].distanceTo(points[i + 1]) / 2;
8140                 }
8141
8142                 // The line is so small in the current view that all points are on the same pixel.
8143                 if (halfDist === 0) {
8144                         return this._map.layerPointToLatLng(points[0]);
8145                 }
8146
8147                 for (i = 0, dist = 0; i < len - 1; i++) {
8148                         p1 = points[i];
8149                         p2 = points[i + 1];
8150                         segDist = p1.distanceTo(p2);
8151                         dist += segDist;
8152
8153                         if (dist > halfDist) {
8154                                 ratio = (dist - halfDist) / segDist;
8155                                 return this._map.layerPointToLatLng([
8156                                         p2.x - ratio * (p2.x - p1.x),
8157                                         p2.y - ratio * (p2.y - p1.y)
8158                                 ]);
8159                         }
8160                 }
8161         },
8162
8163         // @method getBounds(): LatLngBounds
8164         // Returns the `LatLngBounds` of the path.
8165         getBounds: function () {
8166                 return this._bounds;
8167         },
8168
8169         // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
8170         // Adds a given point to the polyline. By default, adds to the first ring of
8171         // the polyline in case of a multi-polyline, but can be overridden by passing
8172         // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
8173         addLatLng: function (latlng, latlngs) {
8174                 latlngs = latlngs || this._defaultShape();
8175                 latlng = toLatLng(latlng);
8176                 latlngs.push(latlng);
8177                 this._bounds.extend(latlng);
8178                 return this.redraw();
8179         },
8180
8181         _setLatLngs: function (latlngs) {
8182                 this._bounds = new LatLngBounds();
8183                 this._latlngs = this._convertLatLngs(latlngs);
8184         },
8185
8186         _defaultShape: function () {
8187                 return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
8188         },
8189
8190         // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
8191         _convertLatLngs: function (latlngs) {
8192                 var result = [],
8193                     flat = isFlat(latlngs);
8194
8195                 for (var i = 0, len = latlngs.length; i < len; i++) {
8196                         if (flat) {
8197                                 result[i] = toLatLng(latlngs[i]);
8198                                 this._bounds.extend(result[i]);
8199                         } else {
8200                                 result[i] = this._convertLatLngs(latlngs[i]);
8201                         }
8202                 }
8203
8204                 return result;
8205         },
8206
8207         _project: function () {
8208                 var pxBounds = new Bounds();
8209                 this._rings = [];
8210                 this._projectLatlngs(this._latlngs, this._rings, pxBounds);
8211
8212                 var w = this._clickTolerance(),
8213                     p = new Point(w, w);
8214
8215                 if (this._bounds.isValid() && pxBounds.isValid()) {
8216                         pxBounds.min._subtract(p);
8217                         pxBounds.max._add(p);
8218                         this._pxBounds = pxBounds;
8219                 }
8220         },
8221
8222         // recursively turns latlngs into a set of rings with projected coordinates
8223         _projectLatlngs: function (latlngs, result, projectedBounds) {
8224                 var flat = latlngs[0] instanceof LatLng,
8225                     len = latlngs.length,
8226                     i, ring;
8227
8228                 if (flat) {
8229                         ring = [];
8230                         for (i = 0; i < len; i++) {
8231                                 ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
8232                                 projectedBounds.extend(ring[i]);
8233                         }
8234                         result.push(ring);
8235                 } else {
8236                         for (i = 0; i < len; i++) {
8237                                 this._projectLatlngs(latlngs[i], result, projectedBounds);
8238                         }
8239                 }
8240         },
8241
8242         // clip polyline by renderer bounds so that we have less to render for performance
8243         _clipPoints: function () {
8244                 var bounds = this._renderer._bounds;
8245
8246                 this._parts = [];
8247                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8248                         return;
8249                 }
8250
8251                 if (this.options.noClip) {
8252                         this._parts = this._rings;
8253                         return;
8254                 }
8255
8256                 var parts = this._parts,
8257                     i, j, k, len, len2, segment, points;
8258
8259                 for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
8260                         points = this._rings[i];
8261
8262                         for (j = 0, len2 = points.length; j < len2 - 1; j++) {
8263                                 segment = clipSegment(points[j], points[j + 1], bounds, j, true);
8264
8265                                 if (!segment) { continue; }
8266
8267                                 parts[k] = parts[k] || [];
8268                                 parts[k].push(segment[0]);
8269
8270                                 // if segment goes out of screen, or it's the last one, it's the end of the line part
8271                                 if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
8272                                         parts[k].push(segment[1]);
8273                                         k++;
8274                                 }
8275                         }
8276                 }
8277         },
8278
8279         // simplify each clipped part of the polyline for performance
8280         _simplifyPoints: function () {
8281                 var parts = this._parts,
8282                     tolerance = this.options.smoothFactor;
8283
8284                 for (var i = 0, len = parts.length; i < len; i++) {
8285                         parts[i] = simplify(parts[i], tolerance);
8286                 }
8287         },
8288
8289         _update: function () {
8290                 if (!this._map) { return; }
8291
8292                 this._clipPoints();
8293                 this._simplifyPoints();
8294                 this._updatePath();
8295         },
8296
8297         _updatePath: function () {
8298                 this._renderer._updatePoly(this);
8299         },
8300
8301         // Needed by the `Canvas` renderer for interactivity
8302         _containsPoint: function (p, closed) {
8303                 var i, j, k, len, len2, part,
8304                     w = this._clickTolerance();
8305
8306                 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8307
8308                 // hit detection for polylines
8309                 for (i = 0, len = this._parts.length; i < len; i++) {
8310                         part = this._parts[i];
8311
8312                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8313                                 if (!closed && (j === 0)) { continue; }
8314
8315                                 if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
8316                                         return true;
8317                                 }
8318                         }
8319                 }
8320                 return false;
8321         }
8322 });
8323
8324 // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
8325 // Instantiates a polyline object given an array of geographical points and
8326 // optionally an options object. You can create a `Polyline` object with
8327 // multiple separate lines (`MultiPolyline`) by passing an array of arrays
8328 // of geographic points.
8329 function polyline(latlngs, options) {
8330         return new Polyline(latlngs, options);
8331 }
8332
8333 // Retrocompat. Allow plugins to support Leaflet versions before and after 1.1.
8334 Polyline._flat = _flat;
8335
8336 /*
8337  * @class Polygon
8338  * @aka L.Polygon
8339  * @inherits Polyline
8340  *
8341  * A class for drawing polygon overlays on a map. Extends `Polyline`.
8342  *
8343  * Note that points you pass when creating a polygon shouldn't have an additional last point equal to the first one — it's better to filter out such points.
8344  *
8345  *
8346  * @example
8347  *
8348  * ```js
8349  * // create a red polygon from an array of LatLng points
8350  * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
8351  *
8352  * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
8353  *
8354  * // zoom the map to the polygon
8355  * map.fitBounds(polygon.getBounds());
8356  * ```
8357  *
8358  * You can also pass an array of arrays of latlngs, with the first array representing the outer shape and the other arrays representing holes in the outer shape:
8359  *
8360  * ```js
8361  * var latlngs = [
8362  *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8363  *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8364  * ];
8365  * ```
8366  *
8367  * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
8368  *
8369  * ```js
8370  * var latlngs = [
8371  *   [ // first polygon
8372  *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8373  *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8374  *   ],
8375  *   [ // second polygon
8376  *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
8377  *   ]
8378  * ];
8379  * ```
8380  */
8381
8382 var Polygon = Polyline.extend({
8383
8384         options: {
8385                 fill: true
8386         },
8387
8388         isEmpty: function () {
8389                 return !this._latlngs.length || !this._latlngs[0].length;
8390         },
8391
8392         getCenter: function () {
8393                 // throws error when not yet added to map as this center calculation requires projected coordinates
8394                 if (!this._map) {
8395                         throw new Error('Must add layer to map before using getCenter()');
8396                 }
8397
8398                 var i, j, p1, p2, f, area, x, y, center,
8399                     points = this._rings[0],
8400                     len = points.length;
8401
8402                 if (!len) { return null; }
8403
8404                 // polygon centroid algorithm; only uses the first ring if there are multiple
8405
8406                 area = x = y = 0;
8407
8408                 for (i = 0, j = len - 1; i < len; j = i++) {
8409                         p1 = points[i];
8410                         p2 = points[j];
8411
8412                         f = p1.y * p2.x - p2.y * p1.x;
8413                         x += (p1.x + p2.x) * f;
8414                         y += (p1.y + p2.y) * f;
8415                         area += f * 3;
8416                 }
8417
8418                 if (area === 0) {
8419                         // Polygon is so small that all points are on same pixel.
8420                         center = points[0];
8421                 } else {
8422                         center = [x / area, y / area];
8423                 }
8424                 return this._map.layerPointToLatLng(center);
8425         },
8426
8427         _convertLatLngs: function (latlngs) {
8428                 var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
8429                     len = result.length;
8430
8431                 // remove last point if it equals first one
8432                 if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
8433                         result.pop();
8434                 }
8435                 return result;
8436         },
8437
8438         _setLatLngs: function (latlngs) {
8439                 Polyline.prototype._setLatLngs.call(this, latlngs);
8440                 if (isFlat(this._latlngs)) {
8441                         this._latlngs = [this._latlngs];
8442                 }
8443         },
8444
8445         _defaultShape: function () {
8446                 return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
8447         },
8448
8449         _clipPoints: function () {
8450                 // polygons need a different clipping algorithm so we redefine that
8451
8452                 var bounds = this._renderer._bounds,
8453                     w = this.options.weight,
8454                     p = new Point(w, w);
8455
8456                 // increase clip padding by stroke width to avoid stroke on clip edges
8457                 bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
8458
8459                 this._parts = [];
8460                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8461                         return;
8462                 }
8463
8464                 if (this.options.noClip) {
8465                         this._parts = this._rings;
8466                         return;
8467                 }
8468
8469                 for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
8470                         clipped = clipPolygon(this._rings[i], bounds, true);
8471                         if (clipped.length) {
8472                                 this._parts.push(clipped);
8473                         }
8474                 }
8475         },
8476
8477         _updatePath: function () {
8478                 this._renderer._updatePoly(this, true);
8479         },
8480
8481         // Needed by the `Canvas` renderer for interactivity
8482         _containsPoint: function (p) {
8483                 var inside = false,
8484                     part, p1, p2, i, j, k, len, len2;
8485
8486                 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8487
8488                 // ray casting algorithm for detecting if point is in polygon
8489                 for (i = 0, len = this._parts.length; i < len; i++) {
8490                         part = this._parts[i];
8491
8492                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8493                                 p1 = part[j];
8494                                 p2 = part[k];
8495
8496                                 if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) {
8497                                         inside = !inside;
8498                                 }
8499                         }
8500                 }
8501
8502                 // also check if it's on polygon stroke
8503                 return inside || Polyline.prototype._containsPoint.call(this, p, true);
8504         }
8505
8506 });
8507
8508
8509 // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
8510 function polygon(latlngs, options) {
8511         return new Polygon(latlngs, options);
8512 }
8513
8514 /*
8515  * @class GeoJSON
8516  * @aka L.GeoJSON
8517  * @inherits FeatureGroup
8518  *
8519  * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
8520  * GeoJSON data and display it on the map. Extends `FeatureGroup`.
8521  *
8522  * @example
8523  *
8524  * ```js
8525  * L.geoJSON(data, {
8526  *      style: function (feature) {
8527  *              return {color: feature.properties.color};
8528  *      }
8529  * }).bindPopup(function (layer) {
8530  *      return layer.feature.properties.description;
8531  * }).addTo(map);
8532  * ```
8533  */
8534
8535 var GeoJSON = FeatureGroup.extend({
8536
8537         /* @section
8538          * @aka GeoJSON options
8539          *
8540          * @option pointToLayer: Function = *
8541          * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
8542          * called when data is added, passing the GeoJSON point feature and its `LatLng`.
8543          * The default is to spawn a default `Marker`:
8544          * ```js
8545          * function(geoJsonPoint, latlng) {
8546          *      return L.marker(latlng);
8547          * }
8548          * ```
8549          *
8550          * @option style: Function = *
8551          * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
8552          * called internally when data is added.
8553          * The default value is to not override any defaults:
8554          * ```js
8555          * function (geoJsonFeature) {
8556          *      return {}
8557          * }
8558          * ```
8559          *
8560          * @option onEachFeature: Function = *
8561          * A `Function` that will be called once for each created `Feature`, after it has
8562          * been created and styled. Useful for attaching events and popups to features.
8563          * The default is to do nothing with the newly created layers:
8564          * ```js
8565          * function (feature, layer) {}
8566          * ```
8567          *
8568          * @option filter: Function = *
8569          * A `Function` that will be used to decide whether to include a feature or not.
8570          * The default is to include all features:
8571          * ```js
8572          * function (geoJsonFeature) {
8573          *      return true;
8574          * }
8575          * ```
8576          * Note: dynamically changing the `filter` option will have effect only on newly
8577          * added data. It will _not_ re-evaluate already included features.
8578          *
8579          * @option coordsToLatLng: Function = *
8580          * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
8581          * The default is the `coordsToLatLng` static method.
8582          */
8583
8584         initialize: function (geojson, options) {
8585                 setOptions(this, options);
8586
8587                 this._layers = {};
8588
8589                 if (geojson) {
8590                         this.addData(geojson);
8591                 }
8592         },
8593
8594         // @method addData( <GeoJSON> data ): this
8595         // Adds a GeoJSON object to the layer.
8596         addData: function (geojson) {
8597                 var features = isArray(geojson) ? geojson : geojson.features,
8598                     i, len, feature;
8599
8600                 if (features) {
8601                         for (i = 0, len = features.length; i < len; i++) {
8602                                 // only add this if geometry or geometries are set and not null
8603                                 feature = features[i];
8604                                 if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
8605                                         this.addData(feature);
8606                                 }
8607                         }
8608                         return this;
8609                 }
8610
8611                 var options = this.options;
8612
8613                 if (options.filter && !options.filter(geojson)) { return this; }
8614
8615                 var layer = geometryToLayer(geojson, options);
8616                 if (!layer) {
8617                         return this;
8618                 }
8619                 layer.feature = asFeature(geojson);
8620
8621                 layer.defaultOptions = layer.options;
8622                 this.resetStyle(layer);
8623
8624                 if (options.onEachFeature) {
8625                         options.onEachFeature(geojson, layer);
8626                 }
8627
8628                 return this.addLayer(layer);
8629         },
8630
8631         // @method resetStyle( <Path> layer ): this
8632         // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
8633         resetStyle: function (layer) {
8634                 // reset any custom styles
8635                 layer.options = extend({}, layer.defaultOptions);
8636                 this._setLayerStyle(layer, this.options.style);
8637                 return this;
8638         },
8639
8640         // @method setStyle( <Function> style ): this
8641         // Changes styles of GeoJSON vector layers with the given style function.
8642         setStyle: function (style) {
8643                 return this.eachLayer(function (layer) {
8644                         this._setLayerStyle(layer, style);
8645                 }, this);
8646         },
8647
8648         _setLayerStyle: function (layer, style) {
8649                 if (typeof style === 'function') {
8650                         style = style(layer.feature);
8651                 }
8652                 if (layer.setStyle) {
8653                         layer.setStyle(style);
8654                 }
8655         }
8656 });
8657
8658 // @section
8659 // There are several static functions which can be called without instantiating L.GeoJSON:
8660
8661 // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
8662 // Creates a `Layer` from a given GeoJSON feature. Can use a custom
8663 // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
8664 // functions if provided as options.
8665 function geometryToLayer(geojson, options) {
8666
8667         var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
8668             coords = geometry ? geometry.coordinates : null,
8669             layers = [],
8670             pointToLayer = options && options.pointToLayer,
8671             _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
8672             latlng, latlngs, i, len;
8673
8674         if (!coords && !geometry) {
8675                 return null;
8676         }
8677
8678         switch (geometry.type) {
8679         case 'Point':
8680                 latlng = _coordsToLatLng(coords);
8681                 return pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng);
8682
8683         case 'MultiPoint':
8684                 for (i = 0, len = coords.length; i < len; i++) {
8685                         latlng = _coordsToLatLng(coords[i]);
8686                         layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng));
8687                 }
8688                 return new FeatureGroup(layers);
8689
8690         case 'LineString':
8691         case 'MultiLineString':
8692                 latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
8693                 return new Polyline(latlngs, options);
8694
8695         case 'Polygon':
8696         case 'MultiPolygon':
8697                 latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
8698                 return new Polygon(latlngs, options);
8699
8700         case 'GeometryCollection':
8701                 for (i = 0, len = geometry.geometries.length; i < len; i++) {
8702                         var layer = geometryToLayer({
8703                                 geometry: geometry.geometries[i],
8704                                 type: 'Feature',
8705                                 properties: geojson.properties
8706                         }, options);
8707
8708                         if (layer) {
8709                                 layers.push(layer);
8710                         }
8711                 }
8712                 return new FeatureGroup(layers);
8713
8714         default:
8715                 throw new Error('Invalid GeoJSON object.');
8716         }
8717 }
8718
8719 // @function coordsToLatLng(coords: Array): LatLng
8720 // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
8721 // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
8722 function coordsToLatLng(coords) {
8723         return new LatLng(coords[1], coords[0], coords[2]);
8724 }
8725
8726 // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
8727 // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
8728 // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
8729 // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
8730 function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
8731         var latlngs = [];
8732
8733         for (var i = 0, len = coords.length, latlng; i < len; i++) {
8734                 latlng = levelsDeep ?
8735                         coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
8736                         (_coordsToLatLng || coordsToLatLng)(coords[i]);
8737
8738                 latlngs.push(latlng);
8739         }
8740
8741         return latlngs;
8742 }
8743
8744 // @function latLngToCoords(latlng: LatLng, precision?: Number): Array
8745 // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
8746 function latLngToCoords(latlng, precision) {
8747         precision = typeof precision === 'number' ? precision : 6;
8748         return latlng.alt !== undefined ?
8749                 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
8750                 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
8751 }
8752
8753 // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
8754 // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
8755 // `closed` determines whether the first point should be appended to the end of the array to close the feature, only used when `levelsDeep` is 0. False by default.
8756 function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
8757         var coords = [];
8758
8759         for (var i = 0, len = latlngs.length; i < len; i++) {
8760                 coords.push(levelsDeep ?
8761                         latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) :
8762                         latLngToCoords(latlngs[i], precision));
8763         }
8764
8765         if (!levelsDeep && closed) {
8766                 coords.push(coords[0]);
8767         }
8768
8769         return coords;
8770 }
8771
8772 function getFeature(layer, newGeometry) {
8773         return layer.feature ?
8774                 extend({}, layer.feature, {geometry: newGeometry}) :
8775                 asFeature(newGeometry);
8776 }
8777
8778 // @function asFeature(geojson: Object): Object
8779 // Normalize GeoJSON geometries/features into GeoJSON features.
8780 function asFeature(geojson) {
8781         if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
8782                 return geojson;
8783         }
8784
8785         return {
8786                 type: 'Feature',
8787                 properties: {},
8788                 geometry: geojson
8789         };
8790 }
8791
8792 var PointToGeoJSON = {
8793         toGeoJSON: function (precision) {
8794                 return getFeature(this, {
8795                         type: 'Point',
8796                         coordinates: latLngToCoords(this.getLatLng(), precision)
8797                 });
8798         }
8799 };
8800
8801 // @namespace Marker
8802 // @method toGeoJSON(): Object
8803 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
8804 Marker.include(PointToGeoJSON);
8805
8806 // @namespace CircleMarker
8807 // @method toGeoJSON(): Object
8808 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
8809 Circle.include(PointToGeoJSON);
8810 CircleMarker.include(PointToGeoJSON);
8811
8812
8813 // @namespace Polyline
8814 // @method toGeoJSON(): Object
8815 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
8816 Polyline.include({
8817         toGeoJSON: function (precision) {
8818                 var multi = !isFlat(this._latlngs);
8819
8820                 var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
8821
8822                 return getFeature(this, {
8823                         type: (multi ? 'Multi' : '') + 'LineString',
8824                         coordinates: coords
8825                 });
8826         }
8827 });
8828
8829 // @namespace Polygon
8830 // @method toGeoJSON(): Object
8831 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
8832 Polygon.include({
8833         toGeoJSON: function (precision) {
8834                 var holes = !isFlat(this._latlngs),
8835                     multi = holes && !isFlat(this._latlngs[0]);
8836
8837                 var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
8838
8839                 if (!holes) {
8840                         coords = [coords];
8841                 }
8842
8843                 return getFeature(this, {
8844                         type: (multi ? 'Multi' : '') + 'Polygon',
8845                         coordinates: coords
8846                 });
8847         }
8848 });
8849
8850
8851 // @namespace LayerGroup
8852 LayerGroup.include({
8853         toMultiPoint: function (precision) {
8854                 var coords = [];
8855
8856                 this.eachLayer(function (layer) {
8857                         coords.push(layer.toGeoJSON(precision).geometry.coordinates);
8858                 });
8859
8860                 return getFeature(this, {
8861                         type: 'MultiPoint',
8862                         coordinates: coords
8863                 });
8864         },
8865
8866         // @method toGeoJSON(): Object
8867         // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
8868         toGeoJSON: function (precision) {
8869
8870                 var type = this.feature && this.feature.geometry && this.feature.geometry.type;
8871
8872                 if (type === 'MultiPoint') {
8873                         return this.toMultiPoint(precision);
8874                 }
8875
8876                 var isGeometryCollection = type === 'GeometryCollection',
8877                     jsons = [];
8878
8879                 this.eachLayer(function (layer) {
8880                         if (layer.toGeoJSON) {
8881                                 var json = layer.toGeoJSON(precision);
8882                                 if (isGeometryCollection) {
8883                                         jsons.push(json.geometry);
8884                                 } else {
8885                                         var feature = asFeature(json);
8886                                         // Squash nested feature collections
8887                                         if (feature.type === 'FeatureCollection') {
8888                                                 jsons.push.apply(jsons, feature.features);
8889                                         } else {
8890                                                 jsons.push(feature);
8891                                         }
8892                                 }
8893                         }
8894                 });
8895
8896                 if (isGeometryCollection) {
8897                         return getFeature(this, {
8898                                 geometries: jsons,
8899                                 type: 'GeometryCollection'
8900                         });
8901                 }
8902
8903                 return {
8904                         type: 'FeatureCollection',
8905                         features: jsons
8906                 };
8907         }
8908 });
8909
8910 // @namespace GeoJSON
8911 // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
8912 // Creates a GeoJSON layer. Optionally accepts an object in
8913 // [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map
8914 // (you can alternatively add it later with `addData` method) and an `options` object.
8915 function geoJSON(geojson, options) {
8916         return new GeoJSON(geojson, options);
8917 }
8918
8919 // Backward compatibility.
8920 var geoJson = geoJSON;
8921
8922 /*
8923  * @class ImageOverlay
8924  * @aka L.ImageOverlay
8925  * @inherits Interactive layer
8926  *
8927  * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
8928  *
8929  * @example
8930  *
8931  * ```js
8932  * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
8933  *      imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
8934  * L.imageOverlay(imageUrl, imageBounds).addTo(map);
8935  * ```
8936  */
8937
8938 var ImageOverlay = Layer.extend({
8939
8940         // @section
8941         // @aka ImageOverlay options
8942         options: {
8943                 // @option opacity: Number = 1.0
8944                 // The opacity of the image overlay.
8945                 opacity: 1,
8946
8947                 // @option alt: String = ''
8948                 // Text for the `alt` attribute of the image (useful for accessibility).
8949                 alt: '',
8950
8951                 // @option interactive: Boolean = false
8952                 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
8953                 interactive: false,
8954
8955                 // @option crossOrigin: Boolean|String = false
8956                 // Whether the crossOrigin attribute will be added to the image.
8957                 // If a String is provided, the image will have its crossOrigin attribute set to the String provided. This is needed if you want to access image pixel data.
8958                 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
8959                 crossOrigin: false,
8960
8961                 // @option errorOverlayUrl: String = ''
8962                 // URL to the overlay image to show in place of the overlay that failed to load.
8963                 errorOverlayUrl: '',
8964
8965                 // @option zIndex: Number = 1
8966                 // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the tile layer.
8967                 zIndex: 1,
8968
8969                 // @option className: String = ''
8970                 // A custom class name to assign to the image. Empty by default.
8971                 className: '',
8972         },
8973
8974         initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
8975                 this._url = url;
8976                 this._bounds = toLatLngBounds(bounds);
8977
8978                 setOptions(this, options);
8979         },
8980
8981         onAdd: function () {
8982                 if (!this._image) {
8983                         this._initImage();
8984
8985                         if (this.options.opacity < 1) {
8986                                 this._updateOpacity();
8987                         }
8988                 }
8989
8990                 if (this.options.interactive) {
8991                         addClass(this._image, 'leaflet-interactive');
8992                         this.addInteractiveTarget(this._image);
8993                 }
8994
8995                 this.getPane().appendChild(this._image);
8996                 this._reset();
8997         },
8998
8999         onRemove: function () {
9000                 remove(this._image);
9001                 if (this.options.interactive) {
9002                         this.removeInteractiveTarget(this._image);
9003                 }
9004         },
9005
9006         // @method setOpacity(opacity: Number): this
9007         // Sets the opacity of the overlay.
9008         setOpacity: function (opacity) {
9009                 this.options.opacity = opacity;
9010
9011                 if (this._image) {
9012                         this._updateOpacity();
9013                 }
9014                 return this;
9015         },
9016
9017         setStyle: function (styleOpts) {
9018                 if (styleOpts.opacity) {
9019                         this.setOpacity(styleOpts.opacity);
9020                 }
9021                 return this;
9022         },
9023
9024         // @method bringToFront(): this
9025         // Brings the layer to the top of all overlays.
9026         bringToFront: function () {
9027                 if (this._map) {
9028                         toFront(this._image);
9029                 }
9030                 return this;
9031         },
9032
9033         // @method bringToBack(): this
9034         // Brings the layer to the bottom of all overlays.
9035         bringToBack: function () {
9036                 if (this._map) {
9037                         toBack(this._image);
9038                 }
9039                 return this;
9040         },
9041
9042         // @method setUrl(url: String): this
9043         // Changes the URL of the image.
9044         setUrl: function (url) {
9045                 this._url = url;
9046
9047                 if (this._image) {
9048                         this._image.src = url;
9049                 }
9050                 return this;
9051         },
9052
9053         // @method setBounds(bounds: LatLngBounds): this
9054         // Update the bounds that this ImageOverlay covers
9055         setBounds: function (bounds) {
9056                 this._bounds = toLatLngBounds(bounds);
9057
9058                 if (this._map) {
9059                         this._reset();
9060                 }
9061                 return this;
9062         },
9063
9064         getEvents: function () {
9065                 var events = {
9066                         zoom: this._reset,
9067                         viewreset: this._reset
9068                 };
9069
9070                 if (this._zoomAnimated) {
9071                         events.zoomanim = this._animateZoom;
9072                 }
9073
9074                 return events;
9075         },
9076
9077         // @method: setZIndex(value: Number) : this
9078         // Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
9079         setZIndex: function (value) {
9080                 this.options.zIndex = value;
9081                 this._updateZIndex();
9082                 return this;
9083         },
9084
9085         // @method getBounds(): LatLngBounds
9086         // Get the bounds that this ImageOverlay covers
9087         getBounds: function () {
9088                 return this._bounds;
9089         },
9090
9091         // @method getElement(): HTMLElement
9092         // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
9093         // used by this overlay.
9094         getElement: function () {
9095                 return this._image;
9096         },
9097
9098         _initImage: function () {
9099                 var wasElementSupplied = this._url.tagName === 'IMG';
9100                 var img = this._image = wasElementSupplied ? this._url : create$1('img');
9101
9102                 addClass(img, 'leaflet-image-layer');
9103                 if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); }
9104                 if (this.options.className) { addClass(img, this.options.className); }
9105
9106                 img.onselectstart = falseFn;
9107                 img.onmousemove = falseFn;
9108
9109                 // @event load: Event
9110                 // Fired when the ImageOverlay layer has loaded its image
9111                 img.onload = bind(this.fire, this, 'load');
9112                 img.onerror = bind(this._overlayOnError, this, 'error');
9113
9114                 if (this.options.crossOrigin || this.options.crossOrigin === '') {
9115                         img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
9116                 }
9117
9118                 if (this.options.zIndex) {
9119                         this._updateZIndex();
9120                 }
9121
9122                 if (wasElementSupplied) {
9123                         this._url = img.src;
9124                         return;
9125                 }
9126
9127                 img.src = this._url;
9128                 img.alt = this.options.alt;
9129         },
9130
9131         _animateZoom: function (e) {
9132                 var scale = this._map.getZoomScale(e.zoom),
9133                     offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
9134
9135                 setTransform(this._image, offset, scale);
9136         },
9137
9138         _reset: function () {
9139                 var image = this._image,
9140                     bounds = new Bounds(
9141                         this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
9142                         this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
9143                     size = bounds.getSize();
9144
9145                 setPosition(image, bounds.min);
9146
9147                 image.style.width  = size.x + 'px';
9148                 image.style.height = size.y + 'px';
9149         },
9150
9151         _updateOpacity: function () {
9152                 setOpacity(this._image, this.options.opacity);
9153         },
9154
9155         _updateZIndex: function () {
9156                 if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
9157                         this._image.style.zIndex = this.options.zIndex;
9158                 }
9159         },
9160
9161         _overlayOnError: function () {
9162                 // @event error: Event
9163                 // Fired when the ImageOverlay layer has loaded its image
9164                 this.fire('error');
9165
9166                 var errorUrl = this.options.errorOverlayUrl;
9167                 if (errorUrl && this._url !== errorUrl) {
9168                         this._url = errorUrl;
9169                         this._image.src = errorUrl;
9170                 }
9171         }
9172 });
9173
9174 // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
9175 // Instantiates an image overlay object given the URL of the image and the
9176 // geographical bounds it is tied to.
9177 var imageOverlay = function (url, bounds, options) {
9178         return new ImageOverlay(url, bounds, options);
9179 };
9180
9181 /*
9182  * @class VideoOverlay
9183  * @aka L.VideoOverlay
9184  * @inherits ImageOverlay
9185  *
9186  * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
9187  *
9188  * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
9189  * HTML5 element.
9190  *
9191  * @example
9192  *
9193  * ```js
9194  * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
9195  *      videoBounds = [[ 32, -130], [ 13, -100]];
9196  * L.videoOverlay(videoUrl, videoBounds ).addTo(map);
9197  * ```
9198  */
9199
9200 var VideoOverlay = ImageOverlay.extend({
9201
9202         // @section
9203         // @aka VideoOverlay options
9204         options: {
9205                 // @option autoplay: Boolean = true
9206                 // Whether the video starts playing automatically when loaded.
9207                 autoplay: true,
9208
9209                 // @option loop: Boolean = true
9210                 // Whether the video will loop back to the beginning when played.
9211                 loop: true
9212         },
9213
9214         _initImage: function () {
9215                 var wasElementSupplied = this._url.tagName === 'VIDEO';
9216                 var vid = this._image = wasElementSupplied ? this._url : create$1('video');
9217
9218                 addClass(vid, 'leaflet-image-layer');
9219                 if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); }
9220
9221                 vid.onselectstart = falseFn;
9222                 vid.onmousemove = falseFn;
9223
9224                 // @event load: Event
9225                 // Fired when the video has finished loading the first frame
9226                 vid.onloadeddata = bind(this.fire, this, 'load');
9227
9228                 if (wasElementSupplied) {
9229                         var sourceElements = vid.getElementsByTagName('source');
9230                         var sources = [];
9231                         for (var j = 0; j < sourceElements.length; j++) {
9232                                 sources.push(sourceElements[j].src);
9233                         }
9234
9235                         this._url = (sourceElements.length > 0) ? sources : [vid.src];
9236                         return;
9237                 }
9238
9239                 if (!isArray(this._url)) { this._url = [this._url]; }
9240
9241                 vid.autoplay = !!this.options.autoplay;
9242                 vid.loop = !!this.options.loop;
9243                 for (var i = 0; i < this._url.length; i++) {
9244                         var source = create$1('source');
9245                         source.src = this._url[i];
9246                         vid.appendChild(source);
9247                 }
9248         }
9249
9250         // @method getElement(): HTMLVideoElement
9251         // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
9252         // used by this overlay.
9253 });
9254
9255
9256 // @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options)
9257 // Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the
9258 // geographical bounds it is tied to.
9259
9260 function videoOverlay(video, bounds, options) {
9261         return new VideoOverlay(video, bounds, options);
9262 }
9263
9264 /*
9265  * @class DivOverlay
9266  * @inherits Layer
9267  * @aka L.DivOverlay
9268  * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
9269  */
9270
9271 // @namespace DivOverlay
9272 var DivOverlay = Layer.extend({
9273
9274         // @section
9275         // @aka DivOverlay options
9276         options: {
9277                 // @option offset: Point = Point(0, 7)
9278                 // The offset of the popup position. Useful to control the anchor
9279                 // of the popup when opening it on some overlays.
9280                 offset: [0, 7],
9281
9282                 // @option className: String = ''
9283                 // A custom CSS class name to assign to the popup.
9284                 className: '',
9285
9286                 // @option pane: String = 'popupPane'
9287                 // `Map pane` where the popup will be added.
9288                 pane: 'popupPane'
9289         },
9290
9291         initialize: function (options, source) {
9292                 setOptions(this, options);
9293
9294                 this._source = source;
9295         },
9296
9297         onAdd: function (map) {
9298                 this._zoomAnimated = map._zoomAnimated;
9299
9300                 if (!this._container) {
9301                         this._initLayout();
9302                 }
9303
9304                 if (map._fadeAnimated) {
9305                         setOpacity(this._container, 0);
9306                 }
9307
9308                 clearTimeout(this._removeTimeout);
9309                 this.getPane().appendChild(this._container);
9310                 this.update();
9311
9312                 if (map._fadeAnimated) {
9313                         setOpacity(this._container, 1);
9314                 }
9315
9316                 this.bringToFront();
9317         },
9318
9319         onRemove: function (map) {
9320                 if (map._fadeAnimated) {
9321                         setOpacity(this._container, 0);
9322                         this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200);
9323                 } else {
9324                         remove(this._container);
9325                 }
9326         },
9327
9328         // @namespace Popup
9329         // @method getLatLng: LatLng
9330         // Returns the geographical point of popup.
9331         getLatLng: function () {
9332                 return this._latlng;
9333         },
9334
9335         // @method setLatLng(latlng: LatLng): this
9336         // Sets the geographical point where the popup will open.
9337         setLatLng: function (latlng) {
9338                 this._latlng = toLatLng(latlng);
9339                 if (this._map) {
9340                         this._updatePosition();
9341                         this._adjustPan();
9342                 }
9343                 return this;
9344         },
9345
9346         // @method getContent: String|HTMLElement
9347         // Returns the content of the popup.
9348         getContent: function () {
9349                 return this._content;
9350         },
9351
9352         // @method setContent(htmlContent: String|HTMLElement|Function): this
9353         // Sets the HTML content of the popup. If a function is passed the source layer will be passed to the function. The function should return a `String` or `HTMLElement` to be used in the popup.
9354         setContent: function (content) {
9355                 this._content = content;
9356                 this.update();
9357                 return this;
9358         },
9359
9360         // @method getElement: String|HTMLElement
9361         // Alias for [getContent()](#popup-getcontent)
9362         getElement: function () {
9363                 return this._container;
9364         },
9365
9366         // @method update: null
9367         // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
9368         update: function () {
9369                 if (!this._map) { return; }
9370
9371                 this._container.style.visibility = 'hidden';
9372
9373                 this._updateContent();
9374                 this._updateLayout();
9375                 this._updatePosition();
9376
9377                 this._container.style.visibility = '';
9378
9379                 this._adjustPan();
9380         },
9381
9382         getEvents: function () {
9383                 var events = {
9384                         zoom: this._updatePosition,
9385                         viewreset: this._updatePosition
9386                 };
9387
9388                 if (this._zoomAnimated) {
9389                         events.zoomanim = this._animateZoom;
9390                 }
9391                 return events;
9392         },
9393
9394         // @method isOpen: Boolean
9395         // Returns `true` when the popup is visible on the map.
9396         isOpen: function () {
9397                 return !!this._map && this._map.hasLayer(this);
9398         },
9399
9400         // @method bringToFront: this
9401         // Brings this popup in front of other popups (in the same map pane).
9402         bringToFront: function () {
9403                 if (this._map) {
9404                         toFront(this._container);
9405                 }
9406                 return this;
9407         },
9408
9409         // @method bringToBack: this
9410         // Brings this popup to the back of other popups (in the same map pane).
9411         bringToBack: function () {
9412                 if (this._map) {
9413                         toBack(this._container);
9414                 }
9415                 return this;
9416         },
9417
9418         _updateContent: function () {
9419                 if (!this._content) { return; }
9420
9421                 var node = this._contentNode;
9422                 var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
9423
9424                 if (typeof content === 'string') {
9425                         node.innerHTML = content;
9426                 } else {
9427                         while (node.hasChildNodes()) {
9428                                 node.removeChild(node.firstChild);
9429                         }
9430                         node.appendChild(content);
9431                 }
9432                 this.fire('contentupdate');
9433         },
9434
9435         _updatePosition: function () {
9436                 if (!this._map) { return; }
9437
9438                 var pos = this._map.latLngToLayerPoint(this._latlng),
9439                     offset = toPoint(this.options.offset),
9440                     anchor = this._getAnchor();
9441
9442                 if (this._zoomAnimated) {
9443                         setPosition(this._container, pos.add(anchor));
9444                 } else {
9445                         offset = offset.add(pos).add(anchor);
9446                 }
9447
9448                 var bottom = this._containerBottom = -offset.y,
9449                     left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
9450
9451                 // bottom position the popup in case the height of the popup changes (images loading etc)
9452                 this._container.style.bottom = bottom + 'px';
9453                 this._container.style.left = left + 'px';
9454         },
9455
9456         _getAnchor: function () {
9457                 return [0, 0];
9458         }
9459
9460 });
9461
9462 /*
9463  * @class Popup
9464  * @inherits DivOverlay
9465  * @aka L.Popup
9466  * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
9467  * open popups while making sure that only one popup is open at one time
9468  * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
9469  *
9470  * @example
9471  *
9472  * If you want to just bind a popup to marker click and then open it, it's really easy:
9473  *
9474  * ```js
9475  * marker.bindPopup(popupContent).openPopup();
9476  * ```
9477  * Path overlays like polylines also have a `bindPopup` method.
9478  * Here's a more complicated way to open a popup on a map:
9479  *
9480  * ```js
9481  * var popup = L.popup()
9482  *      .setLatLng(latlng)
9483  *      .setContent('<p>Hello world!<br />This is a nice popup.</p>')
9484  *      .openOn(map);
9485  * ```
9486  */
9487
9488
9489 // @namespace Popup
9490 var Popup = DivOverlay.extend({
9491
9492         // @section
9493         // @aka Popup options
9494         options: {
9495                 // @option maxWidth: Number = 300
9496                 // Max width of the popup, in pixels.
9497                 maxWidth: 300,
9498
9499                 // @option minWidth: Number = 50
9500                 // Min width of the popup, in pixels.
9501                 minWidth: 50,
9502
9503                 // @option maxHeight: Number = null
9504                 // If set, creates a scrollable container of the given height
9505                 // inside a popup if its content exceeds it.
9506                 maxHeight: null,
9507
9508                 // @option autoPan: Boolean = true
9509                 // Set it to `false` if you don't want the map to do panning animation
9510                 // to fit the opened popup.
9511                 autoPan: true,
9512
9513                 // @option autoPanPaddingTopLeft: Point = null
9514                 // The margin between the popup and the top left corner of the map
9515                 // view after autopanning was performed.
9516                 autoPanPaddingTopLeft: null,
9517
9518                 // @option autoPanPaddingBottomRight: Point = null
9519                 // The margin between the popup and the bottom right corner of the map
9520                 // view after autopanning was performed.
9521                 autoPanPaddingBottomRight: null,
9522
9523                 // @option autoPanPadding: Point = Point(5, 5)
9524                 // Equivalent of setting both top left and bottom right autopan padding to the same value.
9525                 autoPanPadding: [5, 5],
9526
9527                 // @option keepInView: Boolean = false
9528                 // Set it to `true` if you want to prevent users from panning the popup
9529                 // off of the screen while it is open.
9530                 keepInView: false,
9531
9532                 // @option closeButton: Boolean = true
9533                 // Controls the presence of a close button in the popup.
9534                 closeButton: true,
9535
9536                 // @option autoClose: Boolean = true
9537                 // Set it to `false` if you want to override the default behavior of
9538                 // the popup closing when another popup is opened.
9539                 autoClose: true,
9540
9541                 // @option closeOnEscapeKey: Boolean = true
9542                 // Set it to `false` if you want to override the default behavior of
9543                 // the ESC key for closing of the popup.
9544                 closeOnEscapeKey: true,
9545
9546                 // @option closeOnClick: Boolean = *
9547                 // Set it if you want to override the default behavior of the popup closing when user clicks
9548                 // on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
9549
9550                 // @option className: String = ''
9551                 // A custom CSS class name to assign to the popup.
9552                 className: ''
9553         },
9554
9555         // @namespace Popup
9556         // @method openOn(map: Map): this
9557         // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
9558         openOn: function (map) {
9559                 map.openPopup(this);
9560                 return this;
9561         },
9562
9563         onAdd: function (map) {
9564                 DivOverlay.prototype.onAdd.call(this, map);
9565
9566                 // @namespace Map
9567                 // @section Popup events
9568                 // @event popupopen: PopupEvent
9569                 // Fired when a popup is opened in the map
9570                 map.fire('popupopen', {popup: this});
9571
9572                 if (this._source) {
9573                         // @namespace Layer
9574                         // @section Popup events
9575                         // @event popupopen: PopupEvent
9576                         // Fired when a popup bound to this layer is opened
9577                         this._source.fire('popupopen', {popup: this}, true);
9578                         // For non-path layers, we toggle the popup when clicking
9579                         // again the layer, so prevent the map to reopen it.
9580                         if (!(this._source instanceof Path)) {
9581                                 this._source.on('preclick', stopPropagation);
9582                         }
9583                 }
9584         },
9585
9586         onRemove: function (map) {
9587                 DivOverlay.prototype.onRemove.call(this, map);
9588
9589                 // @namespace Map
9590                 // @section Popup events
9591                 // @event popupclose: PopupEvent
9592                 // Fired when a popup in the map is closed
9593                 map.fire('popupclose', {popup: this});
9594
9595                 if (this._source) {
9596                         // @namespace Layer
9597                         // @section Popup events
9598                         // @event popupclose: PopupEvent
9599                         // Fired when a popup bound to this layer is closed
9600                         this._source.fire('popupclose', {popup: this}, true);
9601                         if (!(this._source instanceof Path)) {
9602                                 this._source.off('preclick', stopPropagation);
9603                         }
9604                 }
9605         },
9606
9607         getEvents: function () {
9608                 var events = DivOverlay.prototype.getEvents.call(this);
9609
9610                 if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
9611                         events.preclick = this._close;
9612                 }
9613
9614                 if (this.options.keepInView) {
9615                         events.moveend = this._adjustPan;
9616                 }
9617
9618                 return events;
9619         },
9620
9621         _close: function () {
9622                 if (this._map) {
9623                         this._map.closePopup(this);
9624                 }
9625         },
9626
9627         _initLayout: function () {
9628                 var prefix = 'leaflet-popup',
9629                     container = this._container = create$1('div',
9630                         prefix + ' ' + (this.options.className || '') +
9631                         ' leaflet-zoom-animated');
9632
9633                 var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
9634                 this._contentNode = create$1('div', prefix + '-content', wrapper);
9635
9636                 disableClickPropagation(wrapper);
9637                 disableScrollPropagation(this._contentNode);
9638                 on(wrapper, 'contextmenu', stopPropagation);
9639
9640                 this._tipContainer = create$1('div', prefix + '-tip-container', container);
9641                 this._tip = create$1('div', prefix + '-tip', this._tipContainer);
9642
9643                 if (this.options.closeButton) {
9644                         var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
9645                         closeButton.href = '#close';
9646                         closeButton.innerHTML = '&#215;';
9647
9648                         on(closeButton, 'click', this._onCloseButtonClick, this);
9649                 }
9650         },
9651
9652         _updateLayout: function () {
9653                 var container = this._contentNode,
9654                     style = container.style;
9655
9656                 style.width = '';
9657                 style.whiteSpace = 'nowrap';
9658
9659                 var width = container.offsetWidth;
9660                 width = Math.min(width, this.options.maxWidth);
9661                 width = Math.max(width, this.options.minWidth);
9662
9663                 style.width = (width + 1) + 'px';
9664                 style.whiteSpace = '';
9665
9666                 style.height = '';
9667
9668                 var height = container.offsetHeight,
9669                     maxHeight = this.options.maxHeight,
9670                     scrolledClass = 'leaflet-popup-scrolled';
9671
9672                 if (maxHeight && height > maxHeight) {
9673                         style.height = maxHeight + 'px';
9674                         addClass(container, scrolledClass);
9675                 } else {
9676                         removeClass(container, scrolledClass);
9677                 }
9678
9679                 this._containerWidth = this._container.offsetWidth;
9680         },
9681
9682         _animateZoom: function (e) {
9683                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
9684                     anchor = this._getAnchor();
9685                 setPosition(this._container, pos.add(anchor));
9686         },
9687
9688         _adjustPan: function () {
9689                 if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; }
9690
9691                 var map = this._map,
9692                     marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
9693                     containerHeight = this._container.offsetHeight + marginBottom,
9694                     containerWidth = this._containerWidth,
9695                     layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
9696
9697                 layerPos._add(getPosition(this._container));
9698
9699                 var containerPos = map.layerPointToContainerPoint(layerPos),
9700                     padding = toPoint(this.options.autoPanPadding),
9701                     paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
9702                     paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
9703                     size = map.getSize(),
9704                     dx = 0,
9705                     dy = 0;
9706
9707                 if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
9708                         dx = containerPos.x + containerWidth - size.x + paddingBR.x;
9709                 }
9710                 if (containerPos.x - dx - paddingTL.x < 0) { // left
9711                         dx = containerPos.x - paddingTL.x;
9712                 }
9713                 if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
9714                         dy = containerPos.y + containerHeight - size.y + paddingBR.y;
9715                 }
9716                 if (containerPos.y - dy - paddingTL.y < 0) { // top
9717                         dy = containerPos.y - paddingTL.y;
9718                 }
9719
9720                 // @namespace Map
9721                 // @section Popup events
9722                 // @event autopanstart: Event
9723                 // Fired when the map starts autopanning when opening a popup.
9724                 if (dx || dy) {
9725                         map
9726                             .fire('autopanstart')
9727                             .panBy([dx, dy]);
9728                 }
9729         },
9730
9731         _onCloseButtonClick: function (e) {
9732                 this._close();
9733                 stop(e);
9734         },
9735
9736         _getAnchor: function () {
9737                 // Where should we anchor the popup on the source layer?
9738                 return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
9739         }
9740
9741 });
9742
9743 // @namespace Popup
9744 // @factory L.popup(options?: Popup options, source?: Layer)
9745 // Instantiates a `Popup` object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the popup with a reference to the Layer to which it refers.
9746 var popup = function (options, source) {
9747         return new Popup(options, source);
9748 };
9749
9750
9751 /* @namespace Map
9752  * @section Interaction Options
9753  * @option closePopupOnClick: Boolean = true
9754  * Set it to `false` if you don't want popups to close when user clicks the map.
9755  */
9756 Map.mergeOptions({
9757         closePopupOnClick: true
9758 });
9759
9760
9761 // @namespace Map
9762 // @section Methods for Layers and Controls
9763 Map.include({
9764         // @method openPopup(popup: Popup): this
9765         // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
9766         // @alternative
9767         // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
9768         // Creates a popup with the specified content and options and opens it in the given point on a map.
9769         openPopup: function (popup, latlng, options) {
9770                 if (!(popup instanceof Popup)) {
9771                         popup = new Popup(options).setContent(popup);
9772                 }
9773
9774                 if (latlng) {
9775                         popup.setLatLng(latlng);
9776                 }
9777
9778                 if (this.hasLayer(popup)) {
9779                         return this;
9780                 }
9781
9782                 if (this._popup && this._popup.options.autoClose) {
9783                         this.closePopup();
9784                 }
9785
9786                 this._popup = popup;
9787                 return this.addLayer(popup);
9788         },
9789
9790         // @method closePopup(popup?: Popup): this
9791         // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
9792         closePopup: function (popup) {
9793                 if (!popup || popup === this._popup) {
9794                         popup = this._popup;
9795                         this._popup = null;
9796                 }
9797                 if (popup) {
9798                         this.removeLayer(popup);
9799                 }
9800                 return this;
9801         }
9802 });
9803
9804 /*
9805  * @namespace Layer
9806  * @section Popup methods example
9807  *
9808  * All layers share a set of methods convenient for binding popups to it.
9809  *
9810  * ```js
9811  * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
9812  * layer.openPopup();
9813  * layer.closePopup();
9814  * ```
9815  *
9816  * Popups will also be automatically opened when the layer is clicked on and closed when the layer is removed from the map or another popup is opened.
9817  */
9818
9819 // @section Popup methods
9820 Layer.include({
9821
9822         // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
9823         // Binds a popup to the layer with the passed `content` and sets up the
9824         // necessary event listeners. If a `Function` is passed it will receive
9825         // the layer as the first argument and should return a `String` or `HTMLElement`.
9826         bindPopup: function (content, options) {
9827
9828                 if (content instanceof Popup) {
9829                         setOptions(content, options);
9830                         this._popup = content;
9831                         content._source = this;
9832                 } else {
9833                         if (!this._popup || options) {
9834                                 this._popup = new Popup(options, this);
9835                         }
9836                         this._popup.setContent(content);
9837                 }
9838
9839                 if (!this._popupHandlersAdded) {
9840                         this.on({
9841                                 click: this._openPopup,
9842                                 keypress: this._onKeyPress,
9843                                 remove: this.closePopup,
9844                                 move: this._movePopup
9845                         });
9846                         this._popupHandlersAdded = true;
9847                 }
9848
9849                 return this;
9850         },
9851
9852         // @method unbindPopup(): this
9853         // Removes the popup previously bound with `bindPopup`.
9854         unbindPopup: function () {
9855                 if (this._popup) {
9856                         this.off({
9857                                 click: this._openPopup,
9858                                 keypress: this._onKeyPress,
9859                                 remove: this.closePopup,
9860                                 move: this._movePopup
9861                         });
9862                         this._popupHandlersAdded = false;
9863                         this._popup = null;
9864                 }
9865                 return this;
9866         },
9867
9868         // @method openPopup(latlng?: LatLng): this
9869         // Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
9870         openPopup: function (layer, latlng) {
9871                 if (!(layer instanceof Layer)) {
9872                         latlng = layer;
9873                         layer = this;
9874                 }
9875
9876                 if (layer instanceof FeatureGroup) {
9877                         for (var id in this._layers) {
9878                                 layer = this._layers[id];
9879                                 break;
9880                         }
9881                 }
9882
9883                 if (!latlng) {
9884                         latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
9885                 }
9886
9887                 if (this._popup && this._map) {
9888                         // set popup source to this layer
9889                         this._popup._source = layer;
9890
9891                         // update the popup (content, layout, ect...)
9892                         this._popup.update();
9893
9894                         // open the popup on the map
9895                         this._map.openPopup(this._popup, latlng);
9896                 }
9897
9898                 return this;
9899         },
9900
9901         // @method closePopup(): this
9902         // Closes the popup bound to this layer if it is open.
9903         closePopup: function () {
9904                 if (this._popup) {
9905                         this._popup._close();
9906                 }
9907                 return this;
9908         },
9909
9910         // @method togglePopup(): this
9911         // Opens or closes the popup bound to this layer depending on its current state.
9912         togglePopup: function (target) {
9913                 if (this._popup) {
9914                         if (this._popup._map) {
9915                                 this.closePopup();
9916                         } else {
9917                                 this.openPopup(target);
9918                         }
9919                 }
9920                 return this;
9921         },
9922
9923         // @method isPopupOpen(): boolean
9924         // Returns `true` if the popup bound to this layer is currently open.
9925         isPopupOpen: function () {
9926                 return (this._popup ? this._popup.isOpen() : false);
9927         },
9928
9929         // @method setPopupContent(content: String|HTMLElement|Popup): this
9930         // Sets the content of the popup bound to this layer.
9931         setPopupContent: function (content) {
9932                 if (this._popup) {
9933                         this._popup.setContent(content);
9934                 }
9935                 return this;
9936         },
9937
9938         // @method getPopup(): Popup
9939         // Returns the popup bound to this layer.
9940         getPopup: function () {
9941                 return this._popup;
9942         },
9943
9944         _openPopup: function (e) {
9945                 var layer = e.layer || e.target;
9946
9947                 if (!this._popup) {
9948                         return;
9949                 }
9950
9951                 if (!this._map) {
9952                         return;
9953                 }
9954
9955                 // prevent map click
9956                 stop(e);
9957
9958                 // if this inherits from Path its a vector and we can just
9959                 // open the popup at the new location
9960                 if (layer instanceof Path) {
9961                         this.openPopup(e.layer || e.target, e.latlng);
9962                         return;
9963                 }
9964
9965                 // otherwise treat it like a marker and figure out
9966                 // if we should toggle it open/closed
9967                 if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
9968                         this.closePopup();
9969                 } else {
9970                         this.openPopup(layer, e.latlng);
9971                 }
9972         },
9973
9974         _movePopup: function (e) {
9975                 this._popup.setLatLng(e.latlng);
9976         },
9977
9978         _onKeyPress: function (e) {
9979                 if (e.originalEvent.keyCode === 13) {
9980                         this._openPopup(e);
9981                 }
9982         }
9983 });
9984
9985 /*
9986  * @class Tooltip
9987  * @inherits DivOverlay
9988  * @aka L.Tooltip
9989  * Used to display small texts on top of map layers.
9990  *
9991  * @example
9992  *
9993  * ```js
9994  * marker.bindTooltip("my tooltip text").openTooltip();
9995  * ```
9996  * Note about tooltip offset. Leaflet takes two options in consideration
9997  * for computing tooltip offsetting:
9998  * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
9999  *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
10000  *   move it to the bottom. Negatives will move to the left and top.
10001  * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
10002  *   should adapt this value if you use a custom icon.
10003  */
10004
10005
10006 // @namespace Tooltip
10007 var Tooltip = DivOverlay.extend({
10008
10009         // @section
10010         // @aka Tooltip options
10011         options: {
10012                 // @option pane: String = 'tooltipPane'
10013                 // `Map pane` where the tooltip will be added.
10014                 pane: 'tooltipPane',
10015
10016                 // @option offset: Point = Point(0, 0)
10017                 // Optional offset of the tooltip position.
10018                 offset: [0, 0],
10019
10020                 // @option direction: String = 'auto'
10021                 // Direction where to open the tooltip. Possible values are: `right`, `left`,
10022                 // `top`, `bottom`, `center`, `auto`.
10023                 // `auto` will dynamically switch between `right` and `left` according to the tooltip
10024                 // position on the map.
10025                 direction: 'auto',
10026
10027                 // @option permanent: Boolean = false
10028                 // Whether to open the tooltip permanently or only on mouseover.
10029                 permanent: false,
10030
10031                 // @option sticky: Boolean = false
10032                 // If true, the tooltip will follow the mouse instead of being fixed at the feature center.
10033                 sticky: false,
10034
10035                 // @option interactive: Boolean = false
10036                 // If true, the tooltip will listen to the feature events.
10037                 interactive: false,
10038
10039                 // @option opacity: Number = 0.9
10040                 // Tooltip container opacity.
10041                 opacity: 0.9
10042         },
10043
10044         onAdd: function (map) {
10045                 DivOverlay.prototype.onAdd.call(this, map);
10046                 this.setOpacity(this.options.opacity);
10047
10048                 // @namespace Map
10049                 // @section Tooltip events
10050                 // @event tooltipopen: TooltipEvent
10051                 // Fired when a tooltip is opened in the map.
10052                 map.fire('tooltipopen', {tooltip: this});
10053
10054                 if (this._source) {
10055                         // @namespace Layer
10056                         // @section Tooltip events
10057                         // @event tooltipopen: TooltipEvent
10058                         // Fired when a tooltip bound to this layer is opened.
10059                         this._source.fire('tooltipopen', {tooltip: this}, true);
10060                 }
10061         },
10062
10063         onRemove: function (map) {
10064                 DivOverlay.prototype.onRemove.call(this, map);
10065
10066                 // @namespace Map
10067                 // @section Tooltip events
10068                 // @event tooltipclose: TooltipEvent
10069                 // Fired when a tooltip in the map is closed.
10070                 map.fire('tooltipclose', {tooltip: this});
10071
10072                 if (this._source) {
10073                         // @namespace Layer
10074                         // @section Tooltip events
10075                         // @event tooltipclose: TooltipEvent
10076                         // Fired when a tooltip bound to this layer is closed.
10077                         this._source.fire('tooltipclose', {tooltip: this}, true);
10078                 }
10079         },
10080
10081         getEvents: function () {
10082                 var events = DivOverlay.prototype.getEvents.call(this);
10083
10084                 if (touch && !this.options.permanent) {
10085                         events.preclick = this._close;
10086                 }
10087
10088                 return events;
10089         },
10090
10091         _close: function () {
10092                 if (this._map) {
10093                         this._map.closeTooltip(this);
10094                 }
10095         },
10096
10097         _initLayout: function () {
10098                 var prefix = 'leaflet-tooltip',
10099                     className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
10100
10101                 this._contentNode = this._container = create$1('div', className);
10102         },
10103
10104         _updateLayout: function () {},
10105
10106         _adjustPan: function () {},
10107
10108         _setPosition: function (pos) {
10109                 var map = this._map,
10110                     container = this._container,
10111                     centerPoint = map.latLngToContainerPoint(map.getCenter()),
10112                     tooltipPoint = map.layerPointToContainerPoint(pos),
10113                     direction = this.options.direction,
10114                     tooltipWidth = container.offsetWidth,
10115                     tooltipHeight = container.offsetHeight,
10116                     offset = toPoint(this.options.offset),
10117                     anchor = this._getAnchor();
10118
10119                 if (direction === 'top') {
10120                         pos = pos.add(toPoint(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true));
10121                 } else if (direction === 'bottom') {
10122                         pos = pos.subtract(toPoint(tooltipWidth / 2 - offset.x, -offset.y, true));
10123                 } else if (direction === 'center') {
10124                         pos = pos.subtract(toPoint(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true));
10125                 } else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
10126                         direction = 'right';
10127                         pos = pos.add(toPoint(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true));
10128                 } else {
10129                         direction = 'left';
10130                         pos = pos.subtract(toPoint(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true));
10131                 }
10132
10133                 removeClass(container, 'leaflet-tooltip-right');
10134                 removeClass(container, 'leaflet-tooltip-left');
10135                 removeClass(container, 'leaflet-tooltip-top');
10136                 removeClass(container, 'leaflet-tooltip-bottom');
10137                 addClass(container, 'leaflet-tooltip-' + direction);
10138                 setPosition(container, pos);
10139         },
10140
10141         _updatePosition: function () {
10142                 var pos = this._map.latLngToLayerPoint(this._latlng);
10143                 this._setPosition(pos);
10144         },
10145
10146         setOpacity: function (opacity) {
10147                 this.options.opacity = opacity;
10148
10149                 if (this._container) {
10150                         setOpacity(this._container, opacity);
10151                 }
10152         },
10153
10154         _animateZoom: function (e) {
10155                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
10156                 this._setPosition(pos);
10157         },
10158
10159         _getAnchor: function () {
10160                 // Where should we anchor the tooltip on the source layer?
10161                 return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
10162         }
10163
10164 });
10165
10166 // @namespace Tooltip
10167 // @factory L.tooltip(options?: Tooltip options, source?: Layer)
10168 // Instantiates a Tooltip object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the tooltip with a reference to the Layer to which it refers.
10169 var tooltip = function (options, source) {
10170         return new Tooltip(options, source);
10171 };
10172
10173 // @namespace Map
10174 // @section Methods for Layers and Controls
10175 Map.include({
10176
10177         // @method openTooltip(tooltip: Tooltip): this
10178         // Opens the specified tooltip.
10179         // @alternative
10180         // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
10181         // Creates a tooltip with the specified content and options and open it.
10182         openTooltip: function (tooltip, latlng, options) {
10183                 if (!(tooltip instanceof Tooltip)) {
10184                         tooltip = new Tooltip(options).setContent(tooltip);
10185                 }
10186
10187                 if (latlng) {
10188                         tooltip.setLatLng(latlng);
10189                 }
10190
10191                 if (this.hasLayer(tooltip)) {
10192                         return this;
10193                 }
10194
10195                 return this.addLayer(tooltip);
10196         },
10197
10198         // @method closeTooltip(tooltip?: Tooltip): this
10199         // Closes the tooltip given as parameter.
10200         closeTooltip: function (tooltip) {
10201                 if (tooltip) {
10202                         this.removeLayer(tooltip);
10203                 }
10204                 return this;
10205         }
10206
10207 });
10208
10209 /*
10210  * @namespace Layer
10211  * @section Tooltip methods example
10212  *
10213  * All layers share a set of methods convenient for binding tooltips to it.
10214  *
10215  * ```js
10216  * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
10217  * layer.openTooltip();
10218  * layer.closeTooltip();
10219  * ```
10220  */
10221
10222 // @section Tooltip methods
10223 Layer.include({
10224
10225         // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
10226         // Binds a tooltip to the layer with the passed `content` and sets up the
10227         // necessary event listeners. If a `Function` is passed it will receive
10228         // the layer as the first argument and should return a `String` or `HTMLElement`.
10229         bindTooltip: function (content, options) {
10230
10231                 if (content instanceof Tooltip) {
10232                         setOptions(content, options);
10233                         this._tooltip = content;
10234                         content._source = this;
10235                 } else {
10236                         if (!this._tooltip || options) {
10237                                 this._tooltip = new Tooltip(options, this);
10238                         }
10239                         this._tooltip.setContent(content);
10240
10241                 }
10242
10243                 this._initTooltipInteractions();
10244
10245                 if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
10246                         this.openTooltip();
10247                 }
10248
10249                 return this;
10250         },
10251
10252         // @method unbindTooltip(): this
10253         // Removes the tooltip previously bound with `bindTooltip`.
10254         unbindTooltip: function () {
10255                 if (this._tooltip) {
10256                         this._initTooltipInteractions(true);
10257                         this.closeTooltip();
10258                         this._tooltip = null;
10259                 }
10260                 return this;
10261         },
10262
10263         _initTooltipInteractions: function (remove$$1) {
10264                 if (!remove$$1 && this._tooltipHandlersAdded) { return; }
10265                 var onOff = remove$$1 ? 'off' : 'on',
10266                     events = {
10267                         remove: this.closeTooltip,
10268                         move: this._moveTooltip
10269                     };
10270                 if (!this._tooltip.options.permanent) {
10271                         events.mouseover = this._openTooltip;
10272                         events.mouseout = this.closeTooltip;
10273                         if (this._tooltip.options.sticky) {
10274                                 events.mousemove = this._moveTooltip;
10275                         }
10276                         if (touch) {
10277                                 events.click = this._openTooltip;
10278                         }
10279                 } else {
10280                         events.add = this._openTooltip;
10281                 }
10282                 this[onOff](events);
10283                 this._tooltipHandlersAdded = !remove$$1;
10284         },
10285
10286         // @method openTooltip(latlng?: LatLng): this
10287         // Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
10288         openTooltip: function (layer, latlng) {
10289                 if (!(layer instanceof Layer)) {
10290                         latlng = layer;
10291                         layer = this;
10292                 }
10293
10294                 if (layer instanceof FeatureGroup) {
10295                         for (var id in this._layers) {
10296                                 layer = this._layers[id];
10297                                 break;
10298                         }
10299                 }
10300
10301                 if (!latlng) {
10302                         latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
10303                 }
10304
10305                 if (this._tooltip && this._map) {
10306
10307                         // set tooltip source to this layer
10308                         this._tooltip._source = layer;
10309
10310                         // update the tooltip (content, layout, ect...)
10311                         this._tooltip.update();
10312
10313                         // open the tooltip on the map
10314                         this._map.openTooltip(this._tooltip, latlng);
10315
10316                         // Tooltip container may not be defined if not permanent and never
10317                         // opened.
10318                         if (this._tooltip.options.interactive && this._tooltip._container) {
10319                                 addClass(this._tooltip._container, 'leaflet-clickable');
10320                                 this.addInteractiveTarget(this._tooltip._container);
10321                         }
10322                 }
10323
10324                 return this;
10325         },
10326
10327         // @method closeTooltip(): this
10328         // Closes the tooltip bound to this layer if it is open.
10329         closeTooltip: function () {
10330                 if (this._tooltip) {
10331                         this._tooltip._close();
10332                         if (this._tooltip.options.interactive && this._tooltip._container) {
10333                                 removeClass(this._tooltip._container, 'leaflet-clickable');
10334                                 this.removeInteractiveTarget(this._tooltip._container);
10335                         }
10336                 }
10337                 return this;
10338         },
10339
10340         // @method toggleTooltip(): this
10341         // Opens or closes the tooltip bound to this layer depending on its current state.
10342         toggleTooltip: function (target) {
10343                 if (this._tooltip) {
10344                         if (this._tooltip._map) {
10345                                 this.closeTooltip();
10346                         } else {
10347                                 this.openTooltip(target);
10348                         }
10349                 }
10350                 return this;
10351         },
10352
10353         // @method isTooltipOpen(): boolean
10354         // Returns `true` if the tooltip bound to this layer is currently open.
10355         isTooltipOpen: function () {
10356                 return this._tooltip.isOpen();
10357         },
10358
10359         // @method setTooltipContent(content: String|HTMLElement|Tooltip): this
10360         // Sets the content of the tooltip bound to this layer.
10361         setTooltipContent: function (content) {
10362                 if (this._tooltip) {
10363                         this._tooltip.setContent(content);
10364                 }
10365                 return this;
10366         },
10367
10368         // @method getTooltip(): Tooltip
10369         // Returns the tooltip bound to this layer.
10370         getTooltip: function () {
10371                 return this._tooltip;
10372         },
10373
10374         _openTooltip: function (e) {
10375                 var layer = e.layer || e.target;
10376
10377                 if (!this._tooltip || !this._map) {
10378                         return;
10379                 }
10380                 this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
10381         },
10382
10383         _moveTooltip: function (e) {
10384                 var latlng = e.latlng, containerPoint, layerPoint;
10385                 if (this._tooltip.options.sticky && e.originalEvent) {
10386                         containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
10387                         layerPoint = this._map.containerPointToLayerPoint(containerPoint);
10388                         latlng = this._map.layerPointToLatLng(layerPoint);
10389                 }
10390                 this._tooltip.setLatLng(latlng);
10391         }
10392 });
10393
10394 /*
10395  * @class DivIcon
10396  * @aka L.DivIcon
10397  * @inherits Icon
10398  *
10399  * Represents a lightweight icon for markers that uses a simple `<div>`
10400  * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
10401  *
10402  * @example
10403  * ```js
10404  * var myIcon = L.divIcon({className: 'my-div-icon'});
10405  * // you can set .my-div-icon styles in CSS
10406  *
10407  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
10408  * ```
10409  *
10410  * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
10411  */
10412
10413 var DivIcon = Icon.extend({
10414         options: {
10415                 // @section
10416                 // @aka DivIcon options
10417                 iconSize: [12, 12], // also can be set through CSS
10418
10419                 // iconAnchor: (Point),
10420                 // popupAnchor: (Point),
10421
10422                 // @option html: String = ''
10423                 // Custom HTML code to put inside the div element, empty by default.
10424                 html: false,
10425
10426                 // @option bgPos: Point = [0, 0]
10427                 // Optional relative position of the background, in pixels
10428                 bgPos: null,
10429
10430                 className: 'leaflet-div-icon'
10431         },
10432
10433         createIcon: function (oldIcon) {
10434                 var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
10435                     options = this.options;
10436
10437                 div.innerHTML = options.html !== false ? options.html : '';
10438
10439                 if (options.bgPos) {
10440                         var bgPos = toPoint(options.bgPos);
10441                         div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
10442                 }
10443                 this._setIconStyles(div, 'icon');
10444
10445                 return div;
10446         },
10447
10448         createShadow: function () {
10449                 return null;
10450         }
10451 });
10452
10453 // @factory L.divIcon(options: DivIcon options)
10454 // Creates a `DivIcon` instance with the given options.
10455 function divIcon(options) {
10456         return new DivIcon(options);
10457 }
10458
10459 Icon.Default = IconDefault;
10460
10461 /*
10462  * @class GridLayer
10463  * @inherits Layer
10464  * @aka L.GridLayer
10465  *
10466  * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
10467  * GridLayer can be extended to create a tiled grid of HTML elements like `<canvas>`, `<img>` or `<div>`. GridLayer will handle creating and animating these DOM elements for you.
10468  *
10469  *
10470  * @section Synchronous usage
10471  * @example
10472  *
10473  * To create a custom layer, extend GridLayer and implement the `createTile()` method, which will be passed a `Point` object with the `x`, `y`, and `z` (zoom level) coordinates to draw your tile.
10474  *
10475  * ```js
10476  * var CanvasLayer = L.GridLayer.extend({
10477  *     createTile: function(coords){
10478  *         // create a <canvas> element for drawing
10479  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
10480  *
10481  *         // setup tile width and height according to the options
10482  *         var size = this.getTileSize();
10483  *         tile.width = size.x;
10484  *         tile.height = size.y;
10485  *
10486  *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
10487  *         var ctx = tile.getContext('2d');
10488  *
10489  *         // return the tile so it can be rendered on screen
10490  *         return tile;
10491  *     }
10492  * });
10493  * ```
10494  *
10495  * @section Asynchronous usage
10496  * @example
10497  *
10498  * Tile creation can also be asynchronous, this is useful when using a third-party drawing library. Once the tile is finished drawing it can be passed to the `done()` callback.
10499  *
10500  * ```js
10501  * var CanvasLayer = L.GridLayer.extend({
10502  *     createTile: function(coords, done){
10503  *         var error;
10504  *
10505  *         // create a <canvas> element for drawing
10506  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
10507  *
10508  *         // setup tile width and height according to the options
10509  *         var size = this.getTileSize();
10510  *         tile.width = size.x;
10511  *         tile.height = size.y;
10512  *
10513  *         // draw something asynchronously and pass the tile to the done() callback
10514  *         setTimeout(function() {
10515  *             done(error, tile);
10516  *         }, 1000);
10517  *
10518  *         return tile;
10519  *     }
10520  * });
10521  * ```
10522  *
10523  * @section
10524  */
10525
10526
10527 var GridLayer = Layer.extend({
10528
10529         // @section
10530         // @aka GridLayer options
10531         options: {
10532                 // @option tileSize: Number|Point = 256
10533                 // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
10534                 tileSize: 256,
10535
10536                 // @option opacity: Number = 1.0
10537                 // Opacity of the tiles. Can be used in the `createTile()` function.
10538                 opacity: 1,
10539
10540                 // @option updateWhenIdle: Boolean = (depends)
10541                 // Load new tiles only when panning ends.
10542                 // `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation.
10543                 // `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the
10544                 // [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers.
10545                 updateWhenIdle: mobile,
10546
10547                 // @option updateWhenZooming: Boolean = true
10548                 // By default, a smooth zoom animation (during a [touch zoom](#map-touchzoom) or a [`flyTo()`](#map-flyto)) will update grid layers every integer zoom level. Setting this option to `false` will update the grid layer only when the smooth animation ends.
10549                 updateWhenZooming: true,
10550
10551                 // @option updateInterval: Number = 200
10552                 // Tiles will not update more than once every `updateInterval` milliseconds when panning.
10553                 updateInterval: 200,
10554
10555                 // @option zIndex: Number = 1
10556                 // The explicit zIndex of the tile layer.
10557                 zIndex: 1,
10558
10559                 // @option bounds: LatLngBounds = undefined
10560                 // If set, tiles will only be loaded inside the set `LatLngBounds`.
10561                 bounds: null,
10562
10563                 // @option minZoom: Number = 0
10564                 // The minimum zoom level down to which this layer will be displayed (inclusive).
10565                 minZoom: 0,
10566
10567                 // @option maxZoom: Number = undefined
10568                 // The maximum zoom level up to which this layer will be displayed (inclusive).
10569                 maxZoom: undefined,
10570
10571                 // @option maxNativeZoom: Number = undefined
10572                 // Maximum zoom number the tile source has available. If it is specified,
10573                 // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
10574                 // from `maxNativeZoom` level and auto-scaled.
10575                 maxNativeZoom: undefined,
10576
10577                 // @option minNativeZoom: Number = undefined
10578                 // Minimum zoom number the tile source has available. If it is specified,
10579                 // the tiles on all zoom levels lower than `minNativeZoom` will be loaded
10580                 // from `minNativeZoom` level and auto-scaled.
10581                 minNativeZoom: undefined,
10582
10583                 // @option noWrap: Boolean = false
10584                 // Whether the layer is wrapped around the antimeridian. If `true`, the
10585                 // GridLayer will only be displayed once at low zoom levels. Has no
10586                 // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
10587                 // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
10588                 // tiles outside the CRS limits.
10589                 noWrap: false,
10590
10591                 // @option pane: String = 'tilePane'
10592                 // `Map pane` where the grid layer will be added.
10593                 pane: 'tilePane',
10594
10595                 // @option className: String = ''
10596                 // A custom class name to assign to the tile layer. Empty by default.
10597                 className: '',
10598
10599                 // @option keepBuffer: Number = 2
10600                 // When panning the map, keep this many rows and columns of tiles before unloading them.
10601                 keepBuffer: 2
10602         },
10603
10604         initialize: function (options) {
10605                 setOptions(this, options);
10606         },
10607
10608         onAdd: function () {
10609                 this._initContainer();
10610
10611                 this._levels = {};
10612                 this._tiles = {};
10613
10614                 this._resetView();
10615                 this._update();
10616         },
10617
10618         beforeAdd: function (map) {
10619                 map._addZoomLimit(this);
10620         },
10621
10622         onRemove: function (map) {
10623                 this._removeAllTiles();
10624                 remove(this._container);
10625                 map._removeZoomLimit(this);
10626                 this._container = null;
10627                 this._tileZoom = undefined;
10628         },
10629
10630         // @method bringToFront: this
10631         // Brings the tile layer to the top of all tile layers.
10632         bringToFront: function () {
10633                 if (this._map) {
10634                         toFront(this._container);
10635                         this._setAutoZIndex(Math.max);
10636                 }
10637                 return this;
10638         },
10639
10640         // @method bringToBack: this
10641         // Brings the tile layer to the bottom of all tile layers.
10642         bringToBack: function () {
10643                 if (this._map) {
10644                         toBack(this._container);
10645                         this._setAutoZIndex(Math.min);
10646                 }
10647                 return this;
10648         },
10649
10650         // @method getContainer: HTMLElement
10651         // Returns the HTML element that contains the tiles for this layer.
10652         getContainer: function () {
10653                 return this._container;
10654         },
10655
10656         // @method setOpacity(opacity: Number): this
10657         // Changes the [opacity](#gridlayer-opacity) of the grid layer.
10658         setOpacity: function (opacity) {
10659                 this.options.opacity = opacity;
10660                 this._updateOpacity();
10661                 return this;
10662         },
10663
10664         // @method setZIndex(zIndex: Number): this
10665         // Changes the [zIndex](#gridlayer-zindex) of the grid layer.
10666         setZIndex: function (zIndex) {
10667                 this.options.zIndex = zIndex;
10668                 this._updateZIndex();
10669
10670                 return this;
10671         },
10672
10673         // @method isLoading: Boolean
10674         // Returns `true` if any tile in the grid layer has not finished loading.
10675         isLoading: function () {
10676                 return this._loading;
10677         },
10678
10679         // @method redraw: this
10680         // Causes the layer to clear all the tiles and request them again.
10681         redraw: function () {
10682                 if (this._map) {
10683                         this._removeAllTiles();
10684                         this._update();
10685                 }
10686                 return this;
10687         },
10688
10689         getEvents: function () {
10690                 var events = {
10691                         viewprereset: this._invalidateAll,
10692                         viewreset: this._resetView,
10693                         zoom: this._resetView,
10694                         moveend: this._onMoveEnd
10695                 };
10696
10697                 if (!this.options.updateWhenIdle) {
10698                         // update tiles on move, but not more often than once per given interval
10699                         if (!this._onMove) {
10700                                 this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
10701                         }
10702
10703                         events.move = this._onMove;
10704                 }
10705
10706                 if (this._zoomAnimated) {
10707                         events.zoomanim = this._animateZoom;
10708                 }
10709
10710                 return events;
10711         },
10712
10713         // @section Extension methods
10714         // Layers extending `GridLayer` shall reimplement the following method.
10715         // @method createTile(coords: Object, done?: Function): HTMLElement
10716         // Called only internally, must be overridden by classes extending `GridLayer`.
10717         // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
10718         // is specified, it must be called when the tile has finished loading and drawing.
10719         createTile: function () {
10720                 return document.createElement('div');
10721         },
10722
10723         // @section
10724         // @method getTileSize: Point
10725         // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
10726         getTileSize: function () {
10727                 var s = this.options.tileSize;
10728                 return s instanceof Point ? s : new Point(s, s);
10729         },
10730
10731         _updateZIndex: function () {
10732                 if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
10733                         this._container.style.zIndex = this.options.zIndex;
10734                 }
10735         },
10736
10737         _setAutoZIndex: function (compare) {
10738                 // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
10739
10740                 var layers = this.getPane().children,
10741                     edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
10742
10743                 for (var i = 0, len = layers.length, zIndex; i < len; i++) {
10744
10745                         zIndex = layers[i].style.zIndex;
10746
10747                         if (layers[i] !== this._container && zIndex) {
10748                                 edgeZIndex = compare(edgeZIndex, +zIndex);
10749                         }
10750                 }
10751
10752                 if (isFinite(edgeZIndex)) {
10753                         this.options.zIndex = edgeZIndex + compare(-1, 1);
10754                         this._updateZIndex();
10755                 }
10756         },
10757
10758         _updateOpacity: function () {
10759                 if (!this._map) { return; }
10760
10761                 // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
10762                 if (ielt9) { return; }
10763
10764                 setOpacity(this._container, this.options.opacity);
10765
10766                 var now = +new Date(),
10767                     nextFrame = false,
10768                     willPrune = false;
10769
10770                 for (var key in this._tiles) {
10771                         var tile = this._tiles[key];
10772                         if (!tile.current || !tile.loaded) { continue; }
10773
10774                         var fade = Math.min(1, (now - tile.loaded) / 200);
10775
10776                         setOpacity(tile.el, fade);
10777                         if (fade < 1) {
10778                                 nextFrame = true;
10779                         } else {
10780                                 if (tile.active) {
10781                                         willPrune = true;
10782                                 } else {
10783                                         this._onOpaqueTile(tile);
10784                                 }
10785                                 tile.active = true;
10786                         }
10787                 }
10788
10789                 if (willPrune && !this._noPrune) { this._pruneTiles(); }
10790
10791                 if (nextFrame) {
10792                         cancelAnimFrame(this._fadeFrame);
10793                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
10794                 }
10795         },
10796
10797         _onOpaqueTile: falseFn,
10798
10799         _initContainer: function () {
10800                 if (this._container) { return; }
10801
10802                 this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
10803                 this._updateZIndex();
10804
10805                 if (this.options.opacity < 1) {
10806                         this._updateOpacity();
10807                 }
10808
10809                 this.getPane().appendChild(this._container);
10810         },
10811
10812         _updateLevels: function () {
10813
10814                 var zoom = this._tileZoom,
10815                     maxZoom = this.options.maxZoom;
10816
10817                 if (zoom === undefined) { return undefined; }
10818
10819                 for (var z in this._levels) {
10820                         if (this._levels[z].el.children.length || z === zoom) {
10821                                 this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
10822                                 this._onUpdateLevel(z);
10823                         } else {
10824                                 remove(this._levels[z].el);
10825                                 this._removeTilesAtZoom(z);
10826                                 this._onRemoveLevel(z);
10827                                 delete this._levels[z];
10828                         }
10829                 }
10830
10831                 var level = this._levels[zoom],
10832                     map = this._map;
10833
10834                 if (!level) {
10835                         level = this._levels[zoom] = {};
10836
10837                         level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
10838                         level.el.style.zIndex = maxZoom;
10839
10840                         level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
10841                         level.zoom = zoom;
10842
10843                         this._setZoomTransform(level, map.getCenter(), map.getZoom());
10844
10845                         // force the browser to consider the newly added element for transition
10846                         falseFn(level.el.offsetWidth);
10847
10848                         this._onCreateLevel(level);
10849                 }
10850
10851                 this._level = level;
10852
10853                 return level;
10854         },
10855
10856         _onUpdateLevel: falseFn,
10857
10858         _onRemoveLevel: falseFn,
10859
10860         _onCreateLevel: falseFn,
10861
10862         _pruneTiles: function () {
10863                 if (!this._map) {
10864                         return;
10865                 }
10866
10867                 var key, tile;
10868
10869                 var zoom = this._map.getZoom();
10870                 if (zoom > this.options.maxZoom ||
10871                         zoom < this.options.minZoom) {
10872                         this._removeAllTiles();
10873                         return;
10874                 }
10875
10876                 for (key in this._tiles) {
10877                         tile = this._tiles[key];
10878                         tile.retain = tile.current;
10879                 }
10880
10881                 for (key in this._tiles) {
10882                         tile = this._tiles[key];
10883                         if (tile.current && !tile.active) {
10884                                 var coords = tile.coords;
10885                                 if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
10886                                         this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
10887                                 }
10888                         }
10889                 }
10890
10891                 for (key in this._tiles) {
10892                         if (!this._tiles[key].retain) {
10893                                 this._removeTile(key);
10894                         }
10895                 }
10896         },
10897
10898         _removeTilesAtZoom: function (zoom) {
10899                 for (var key in this._tiles) {
10900                         if (this._tiles[key].coords.z !== zoom) {
10901                                 continue;
10902                         }
10903                         this._removeTile(key);
10904                 }
10905         },
10906
10907         _removeAllTiles: function () {
10908                 for (var key in this._tiles) {
10909                         this._removeTile(key);
10910                 }
10911         },
10912
10913         _invalidateAll: function () {
10914                 for (var z in this._levels) {
10915                         remove(this._levels[z].el);
10916                         this._onRemoveLevel(z);
10917                         delete this._levels[z];
10918                 }
10919                 this._removeAllTiles();
10920
10921                 this._tileZoom = undefined;
10922         },
10923
10924         _retainParent: function (x, y, z, minZoom) {
10925                 var x2 = Math.floor(x / 2),
10926                     y2 = Math.floor(y / 2),
10927                     z2 = z - 1,
10928                     coords2 = new Point(+x2, +y2);
10929                 coords2.z = +z2;
10930
10931                 var key = this._tileCoordsToKey(coords2),
10932                     tile = this._tiles[key];
10933
10934                 if (tile && tile.active) {
10935                         tile.retain = true;
10936                         return true;
10937
10938                 } else if (tile && tile.loaded) {
10939                         tile.retain = true;
10940                 }
10941
10942                 if (z2 > minZoom) {
10943                         return this._retainParent(x2, y2, z2, minZoom);
10944                 }
10945
10946                 return false;
10947         },
10948
10949         _retainChildren: function (x, y, z, maxZoom) {
10950
10951                 for (var i = 2 * x; i < 2 * x + 2; i++) {
10952                         for (var j = 2 * y; j < 2 * y + 2; j++) {
10953
10954                                 var coords = new Point(i, j);
10955                                 coords.z = z + 1;
10956
10957                                 var key = this._tileCoordsToKey(coords),
10958                                     tile = this._tiles[key];
10959
10960                                 if (tile && tile.active) {
10961                                         tile.retain = true;
10962                                         continue;
10963
10964                                 } else if (tile && tile.loaded) {
10965                                         tile.retain = true;
10966                                 }
10967
10968                                 if (z + 1 < maxZoom) {
10969                                         this._retainChildren(i, j, z + 1, maxZoom);
10970                                 }
10971                         }
10972                 }
10973         },
10974
10975         _resetView: function (e) {
10976                 var animating = e && (e.pinch || e.flyTo);
10977                 this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
10978         },
10979
10980         _animateZoom: function (e) {
10981                 this._setView(e.center, e.zoom, true, e.noUpdate);
10982         },
10983
10984         _clampZoom: function (zoom) {
10985                 var options = this.options;
10986
10987                 if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
10988                         return options.minNativeZoom;
10989                 }
10990
10991                 if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
10992                         return options.maxNativeZoom;
10993                 }
10994
10995                 return zoom;
10996         },
10997
10998         _setView: function (center, zoom, noPrune, noUpdate) {
10999                 var tileZoom = this._clampZoom(Math.round(zoom));
11000                 if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
11001                     (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
11002                         tileZoom = undefined;
11003                 }
11004
11005                 var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
11006
11007                 if (!noUpdate || tileZoomChanged) {
11008
11009                         this._tileZoom = tileZoom;
11010
11011                         if (this._abortLoading) {
11012                                 this._abortLoading();
11013                         }
11014
11015                         this._updateLevels();
11016                         this._resetGrid();
11017
11018                         if (tileZoom !== undefined) {
11019                                 this._update(center);
11020                         }
11021
11022                         if (!noPrune) {
11023                                 this._pruneTiles();
11024                         }
11025
11026                         // Flag to prevent _updateOpacity from pruning tiles during
11027                         // a zoom anim or a pinch gesture
11028                         this._noPrune = !!noPrune;
11029                 }
11030
11031                 this._setZoomTransforms(center, zoom);
11032         },
11033
11034         _setZoomTransforms: function (center, zoom) {
11035                 for (var i in this._levels) {
11036                         this._setZoomTransform(this._levels[i], center, zoom);
11037                 }
11038         },
11039
11040         _setZoomTransform: function (level, center, zoom) {
11041                 var scale = this._map.getZoomScale(zoom, level.zoom),
11042                     translate = level.origin.multiplyBy(scale)
11043                         .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
11044
11045                 if (any3d) {
11046                         setTransform(level.el, translate, scale);
11047                 } else {
11048                         setPosition(level.el, translate);
11049                 }
11050         },
11051
11052         _resetGrid: function () {
11053                 var map = this._map,
11054                     crs = map.options.crs,
11055                     tileSize = this._tileSize = this.getTileSize(),
11056                     tileZoom = this._tileZoom;
11057
11058                 var bounds = this._map.getPixelWorldBounds(this._tileZoom);
11059                 if (bounds) {
11060                         this._globalTileRange = this._pxBoundsToTileRange(bounds);
11061                 }
11062
11063                 this._wrapX = crs.wrapLng && !this.options.noWrap && [
11064                         Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
11065                         Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
11066                 ];
11067                 this._wrapY = crs.wrapLat && !this.options.noWrap && [
11068                         Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
11069                         Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
11070                 ];
11071         },
11072
11073         _onMoveEnd: function () {
11074                 if (!this._map || this._map._animatingZoom) { return; }
11075
11076                 this._update();
11077         },
11078
11079         _getTiledPixelBounds: function (center) {
11080                 var map = this._map,
11081                     mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
11082                     scale = map.getZoomScale(mapZoom, this._tileZoom),
11083                     pixelCenter = map.project(center, this._tileZoom).floor(),
11084                     halfSize = map.getSize().divideBy(scale * 2);
11085
11086                 return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
11087         },
11088
11089         // Private method to load tiles in the grid's active zoom level according to map bounds
11090         _update: function (center) {
11091                 var map = this._map;
11092                 if (!map) { return; }
11093                 var zoom = this._clampZoom(map.getZoom());
11094
11095                 if (center === undefined) { center = map.getCenter(); }
11096                 if (this._tileZoom === undefined) { return; }   // if out of minzoom/maxzoom
11097
11098                 var pixelBounds = this._getTiledPixelBounds(center),
11099                     tileRange = this._pxBoundsToTileRange(pixelBounds),
11100                     tileCenter = tileRange.getCenter(),
11101                     queue = [],
11102                     margin = this.options.keepBuffer,
11103                     noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
11104                                               tileRange.getTopRight().add([margin, -margin]));
11105
11106                 // Sanity check: panic if the tile range contains Infinity somewhere.
11107                 if (!(isFinite(tileRange.min.x) &&
11108                       isFinite(tileRange.min.y) &&
11109                       isFinite(tileRange.max.x) &&
11110                       isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
11111
11112                 for (var key in this._tiles) {
11113                         var c = this._tiles[key].coords;
11114                         if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
11115                                 this._tiles[key].current = false;
11116                         }
11117                 }
11118
11119                 // _update just loads more tiles. If the tile zoom level differs too much
11120                 // from the map's, let _setView reset levels and prune old tiles.
11121                 if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
11122
11123                 // create a queue of coordinates to load tiles from
11124                 for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
11125                         for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
11126                                 var coords = new Point(i, j);
11127                                 coords.z = this._tileZoom;
11128
11129                                 if (!this._isValidTile(coords)) { continue; }
11130
11131                                 var tile = this._tiles[this._tileCoordsToKey(coords)];
11132                                 if (tile) {
11133                                         tile.current = true;
11134                                 } else {
11135                                         queue.push(coords);
11136                                 }
11137                         }
11138                 }
11139
11140                 // sort tile queue to load tiles in order of their distance to center
11141                 queue.sort(function (a, b) {
11142                         return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
11143                 });
11144
11145                 if (queue.length !== 0) {
11146                         // if it's the first batch of tiles to load
11147                         if (!this._loading) {
11148                                 this._loading = true;
11149                                 // @event loading: Event
11150                                 // Fired when the grid layer starts loading tiles.
11151                                 this.fire('loading');
11152                         }
11153
11154                         // create DOM fragment to append tiles in one batch
11155                         var fragment = document.createDocumentFragment();
11156
11157                         for (i = 0; i < queue.length; i++) {
11158                                 this._addTile(queue[i], fragment);
11159                         }
11160
11161                         this._level.el.appendChild(fragment);
11162                 }
11163         },
11164
11165         _isValidTile: function (coords) {
11166                 var crs = this._map.options.crs;
11167
11168                 if (!crs.infinite) {
11169                         // don't load tile if it's out of bounds and not wrapped
11170                         var bounds = this._globalTileRange;
11171                         if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
11172                             (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
11173                 }
11174
11175                 if (!this.options.bounds) { return true; }
11176
11177                 // don't load tile if it doesn't intersect the bounds in options
11178                 var tileBounds = this._tileCoordsToBounds(coords);
11179                 return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
11180         },
11181
11182         _keyToBounds: function (key) {
11183                 return this._tileCoordsToBounds(this._keyToTileCoords(key));
11184         },
11185
11186         _tileCoordsToNwSe: function (coords) {
11187                 var map = this._map,
11188                     tileSize = this.getTileSize(),
11189                     nwPoint = coords.scaleBy(tileSize),
11190                     sePoint = nwPoint.add(tileSize),
11191                     nw = map.unproject(nwPoint, coords.z),
11192                     se = map.unproject(sePoint, coords.z);
11193                 return [nw, se];
11194         },
11195
11196         // converts tile coordinates to its geographical bounds
11197         _tileCoordsToBounds: function (coords) {
11198                 var bp = this._tileCoordsToNwSe(coords),
11199                     bounds = new LatLngBounds(bp[0], bp[1]);
11200
11201                 if (!this.options.noWrap) {
11202                         bounds = this._map.wrapLatLngBounds(bounds);
11203                 }
11204                 return bounds;
11205         },
11206         // converts tile coordinates to key for the tile cache
11207         _tileCoordsToKey: function (coords) {
11208                 return coords.x + ':' + coords.y + ':' + coords.z;
11209         },
11210
11211         // converts tile cache key to coordinates
11212         _keyToTileCoords: function (key) {
11213                 var k = key.split(':'),
11214                     coords = new Point(+k[0], +k[1]);
11215                 coords.z = +k[2];
11216                 return coords;
11217         },
11218
11219         _removeTile: function (key) {
11220                 var tile = this._tiles[key];
11221                 if (!tile) { return; }
11222
11223                 // Cancels any pending http requests associated with the tile
11224                 // unless we're on Android's stock browser,
11225                 // see https://github.com/Leaflet/Leaflet/issues/137
11226                 if (!androidStock) {
11227                         tile.el.setAttribute('src', emptyImageUrl);
11228                 }
11229                 remove(tile.el);
11230
11231                 delete this._tiles[key];
11232
11233                 // @event tileunload: TileEvent
11234                 // Fired when a tile is removed (e.g. when a tile goes off the screen).
11235                 this.fire('tileunload', {
11236                         tile: tile.el,
11237                         coords: this._keyToTileCoords(key)
11238                 });
11239         },
11240
11241         _initTile: function (tile) {
11242                 addClass(tile, 'leaflet-tile');
11243
11244                 var tileSize = this.getTileSize();
11245                 tile.style.width = tileSize.x + 'px';
11246                 tile.style.height = tileSize.y + 'px';
11247
11248                 tile.onselectstart = falseFn;
11249                 tile.onmousemove = falseFn;
11250
11251                 // update opacity on tiles in IE7-8 because of filter inheritance problems
11252                 if (ielt9 && this.options.opacity < 1) {
11253                         setOpacity(tile, this.options.opacity);
11254                 }
11255
11256                 // without this hack, tiles disappear after zoom on Chrome for Android
11257                 // https://github.com/Leaflet/Leaflet/issues/2078
11258                 if (android && !android23) {
11259                         tile.style.WebkitBackfaceVisibility = 'hidden';
11260                 }
11261         },
11262
11263         _addTile: function (coords, container) {
11264                 var tilePos = this._getTilePos(coords),
11265                     key = this._tileCoordsToKey(coords);
11266
11267                 var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
11268
11269                 this._initTile(tile);
11270
11271                 // if createTile is defined with a second argument ("done" callback),
11272                 // we know that tile is async and will be ready later; otherwise
11273                 if (this.createTile.length < 2) {
11274                         // mark tile as ready, but delay one frame for opacity animation to happen
11275                         requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
11276                 }
11277
11278                 setPosition(tile, tilePos);
11279
11280                 // save tile in cache
11281                 this._tiles[key] = {
11282                         el: tile,
11283                         coords: coords,
11284                         current: true
11285                 };
11286
11287                 container.appendChild(tile);
11288                 // @event tileloadstart: TileEvent
11289                 // Fired when a tile is requested and starts loading.
11290                 this.fire('tileloadstart', {
11291                         tile: tile,
11292                         coords: coords
11293                 });
11294         },
11295
11296         _tileReady: function (coords, err, tile) {
11297                 if (!this._map || tile.getAttribute('src') === emptyImageUrl) { return; }
11298
11299                 if (err) {
11300                         // @event tileerror: TileErrorEvent
11301                         // Fired when there is an error loading a tile.
11302                         this.fire('tileerror', {
11303                                 error: err,
11304                                 tile: tile,
11305                                 coords: coords
11306                         });
11307                 }
11308
11309                 var key = this._tileCoordsToKey(coords);
11310
11311                 tile = this._tiles[key];
11312                 if (!tile) { return; }
11313
11314                 tile.loaded = +new Date();
11315                 if (this._map._fadeAnimated) {
11316                         setOpacity(tile.el, 0);
11317                         cancelAnimFrame(this._fadeFrame);
11318                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
11319                 } else {
11320                         tile.active = true;
11321                         this._pruneTiles();
11322                 }
11323
11324                 if (!err) {
11325                         addClass(tile.el, 'leaflet-tile-loaded');
11326
11327                         // @event tileload: TileEvent
11328                         // Fired when a tile loads.
11329                         this.fire('tileload', {
11330                                 tile: tile.el,
11331                                 coords: coords
11332                         });
11333                 }
11334
11335                 if (this._noTilesToLoad()) {
11336                         this._loading = false;
11337                         // @event load: Event
11338                         // Fired when the grid layer loaded all visible tiles.
11339                         this.fire('load');
11340
11341                         if (ielt9 || !this._map._fadeAnimated) {
11342                                 requestAnimFrame(this._pruneTiles, this);
11343                         } else {
11344                                 // Wait a bit more than 0.2 secs (the duration of the tile fade-in)
11345                                 // to trigger a pruning.
11346                                 setTimeout(bind(this._pruneTiles, this), 250);
11347                         }
11348                 }
11349         },
11350
11351         _getTilePos: function (coords) {
11352                 return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
11353         },
11354
11355         _wrapCoords: function (coords) {
11356                 var newCoords = new Point(
11357                         this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
11358                         this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
11359                 newCoords.z = coords.z;
11360                 return newCoords;
11361         },
11362
11363         _pxBoundsToTileRange: function (bounds) {
11364                 var tileSize = this.getTileSize();
11365                 return new Bounds(
11366                         bounds.min.unscaleBy(tileSize).floor(),
11367                         bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
11368         },
11369
11370         _noTilesToLoad: function () {
11371                 for (var key in this._tiles) {
11372                         if (!this._tiles[key].loaded) { return false; }
11373                 }
11374                 return true;
11375         }
11376 });
11377
11378 // @factory L.gridLayer(options?: GridLayer options)
11379 // Creates a new instance of GridLayer with the supplied options.
11380 function gridLayer(options) {
11381         return new GridLayer(options);
11382 }
11383
11384 /*
11385  * @class TileLayer
11386  * @inherits GridLayer
11387  * @aka L.TileLayer
11388  * Used to load and display tile layers on the map. Extends `GridLayer`.
11389  *
11390  * @example
11391  *
11392  * ```js
11393  * L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar'}).addTo(map);
11394  * ```
11395  *
11396  * @section URL template
11397  * @example
11398  *
11399  * A string of the following form:
11400  *
11401  * ```
11402  * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
11403  * ```
11404  *
11405  * `{s}` means one of the available subdomains (used sequentially to help with browser parallel requests per domain limitation; subdomain values are specified in options; `a`, `b` or `c` by default, can be omitted), `{z}` — zoom level, `{x}` and `{y}` — tile coordinates. `{r}` can be used to add "&commat;2x" to the URL to load retina tiles.
11406  *
11407  * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
11408  *
11409  * ```
11410  * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
11411  * ```
11412  */
11413
11414
11415 var TileLayer = GridLayer.extend({
11416
11417         // @section
11418         // @aka TileLayer options
11419         options: {
11420                 // @option minZoom: Number = 0
11421                 // The minimum zoom level down to which this layer will be displayed (inclusive).
11422                 minZoom: 0,
11423
11424                 // @option maxZoom: Number = 18
11425                 // The maximum zoom level up to which this layer will be displayed (inclusive).
11426                 maxZoom: 18,
11427
11428                 // @option subdomains: String|String[] = 'abc'
11429                 // Subdomains of the tile service. Can be passed in the form of one string (where each letter is a subdomain name) or an array of strings.
11430                 subdomains: 'abc',
11431
11432                 // @option errorTileUrl: String = ''
11433                 // URL to the tile image to show in place of the tile that failed to load.
11434                 errorTileUrl: '',
11435
11436                 // @option zoomOffset: Number = 0
11437                 // The zoom number used in tile URLs will be offset with this value.
11438                 zoomOffset: 0,
11439
11440                 // @option tms: Boolean = false
11441                 // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
11442                 tms: false,
11443
11444                 // @option zoomReverse: Boolean = false
11445                 // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
11446                 zoomReverse: false,
11447
11448                 // @option detectRetina: Boolean = false
11449                 // If `true` and user is on a retina display, it will request four tiles of half the specified size and a bigger zoom level in place of one to utilize the high resolution.
11450                 detectRetina: false,
11451
11452                 // @option crossOrigin: Boolean|String = false
11453                 // Whether the crossOrigin attribute will be added to the tiles.
11454                 // If a String is provided, all tiles will have their crossOrigin attribute set to the String provided. This is needed if you want to access tile pixel data.
11455                 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
11456                 crossOrigin: false
11457         },
11458
11459         initialize: function (url, options) {
11460
11461                 this._url = url;
11462
11463                 options = setOptions(this, options);
11464
11465                 // detecting retina displays, adjusting tileSize and zoom levels
11466                 if (options.detectRetina && retina && options.maxZoom > 0) {
11467
11468                         options.tileSize = Math.floor(options.tileSize / 2);
11469
11470                         if (!options.zoomReverse) {
11471                                 options.zoomOffset++;
11472                                 options.maxZoom--;
11473                         } else {
11474                                 options.zoomOffset--;
11475                                 options.minZoom++;
11476                         }
11477
11478                         options.minZoom = Math.max(0, options.minZoom);
11479                 }
11480
11481                 if (typeof options.subdomains === 'string') {
11482                         options.subdomains = options.subdomains.split('');
11483                 }
11484
11485                 // for https://github.com/Leaflet/Leaflet/issues/137
11486                 if (!android) {
11487                         this.on('tileunload', this._onTileRemove);
11488                 }
11489         },
11490
11491         // @method setUrl(url: String, noRedraw?: Boolean): this
11492         // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
11493         setUrl: function (url, noRedraw) {
11494                 this._url = url;
11495
11496                 if (!noRedraw) {
11497                         this.redraw();
11498                 }
11499                 return this;
11500         },
11501
11502         // @method createTile(coords: Object, done?: Function): HTMLElement
11503         // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
11504         // to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
11505         // callback is called when the tile has been loaded.
11506         createTile: function (coords, done) {
11507                 var tile = document.createElement('img');
11508
11509                 on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
11510                 on(tile, 'error', bind(this._tileOnError, this, done, tile));
11511
11512                 if (this.options.crossOrigin || this.options.crossOrigin === '') {
11513                         tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
11514                 }
11515
11516                 /*
11517                  Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
11518                  http://www.w3.org/TR/WCAG20-TECHS/H67
11519                 */
11520                 tile.alt = '';
11521
11522                 /*
11523                  Set role="presentation" to force screen readers to ignore this
11524                  https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
11525                 */
11526                 tile.setAttribute('role', 'presentation');
11527
11528                 tile.src = this.getTileUrl(coords);
11529
11530                 return tile;
11531         },
11532
11533         // @section Extension methods
11534         // @uninheritable
11535         // Layers extending `TileLayer` might reimplement the following method.
11536         // @method getTileUrl(coords: Object): String
11537         // Called only internally, returns the URL for a tile given its coordinates.
11538         // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
11539         getTileUrl: function (coords) {
11540                 var data = {
11541                         r: retina ? '@2x' : '',
11542                         s: this._getSubdomain(coords),
11543                         x: coords.x,
11544                         y: coords.y,
11545                         z: this._getZoomForUrl()
11546                 };
11547                 if (this._map && !this._map.options.crs.infinite) {
11548                         var invertedY = this._globalTileRange.max.y - coords.y;
11549                         if (this.options.tms) {
11550                                 data['y'] = invertedY;
11551                         }
11552                         data['-y'] = invertedY;
11553                 }
11554
11555                 return template(this._url, extend(data, this.options));
11556         },
11557
11558         _tileOnLoad: function (done, tile) {
11559                 // For https://github.com/Leaflet/Leaflet/issues/3332
11560                 if (ielt9) {
11561                         setTimeout(bind(done, this, null, tile), 0);
11562                 } else {
11563                         done(null, tile);
11564                 }
11565         },
11566
11567         _tileOnError: function (done, tile, e) {
11568                 var errorUrl = this.options.errorTileUrl;
11569                 if (errorUrl && tile.getAttribute('src') !== errorUrl) {
11570                         tile.src = errorUrl;
11571                 }
11572                 done(e, tile);
11573         },
11574
11575         _onTileRemove: function (e) {
11576                 e.tile.onload = null;
11577         },
11578
11579         _getZoomForUrl: function () {
11580                 var zoom = this._tileZoom,
11581                 maxZoom = this.options.maxZoom,
11582                 zoomReverse = this.options.zoomReverse,
11583                 zoomOffset = this.options.zoomOffset;
11584
11585                 if (zoomReverse) {
11586                         zoom = maxZoom - zoom;
11587                 }
11588
11589                 return zoom + zoomOffset;
11590         },
11591
11592         _getSubdomain: function (tilePoint) {
11593                 var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
11594                 return this.options.subdomains[index];
11595         },
11596
11597         // stops loading all tiles in the background layer
11598         _abortLoading: function () {
11599                 var i, tile;
11600                 for (i in this._tiles) {
11601                         if (this._tiles[i].coords.z !== this._tileZoom) {
11602                                 tile = this._tiles[i].el;
11603
11604                                 tile.onload = falseFn;
11605                                 tile.onerror = falseFn;
11606
11607                                 if (!tile.complete) {
11608                                         tile.src = emptyImageUrl;
11609                                         remove(tile);
11610                                         delete this._tiles[i];
11611                                 }
11612                         }
11613                 }
11614         }
11615 });
11616
11617
11618 // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
11619 // Instantiates a tile layer object given a `URL template` and optionally an options object.
11620
11621 function tileLayer(url, options) {
11622         return new TileLayer(url, options);
11623 }
11624
11625 /*
11626  * @class TileLayer.WMS
11627  * @inherits TileLayer
11628  * @aka L.TileLayer.WMS
11629  * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
11630  *
11631  * @example
11632  *
11633  * ```js
11634  * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
11635  *      layers: 'nexrad-n0r-900913',
11636  *      format: 'image/png',
11637  *      transparent: true,
11638  *      attribution: "Weather data © 2012 IEM Nexrad"
11639  * });
11640  * ```
11641  */
11642
11643 var TileLayerWMS = TileLayer.extend({
11644
11645         // @section
11646         // @aka TileLayer.WMS options
11647         // If any custom options not documented here are used, they will be sent to the
11648         // WMS server as extra parameters in each request URL. This can be useful for
11649         // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
11650         defaultWmsParams: {
11651                 service: 'WMS',
11652                 request: 'GetMap',
11653
11654                 // @option layers: String = ''
11655                 // **(required)** Comma-separated list of WMS layers to show.
11656                 layers: '',
11657
11658                 // @option styles: String = ''
11659                 // Comma-separated list of WMS styles.
11660                 styles: '',
11661
11662                 // @option format: String = 'image/jpeg'
11663                 // WMS image format (use `'image/png'` for layers with transparency).
11664                 format: 'image/jpeg',
11665
11666                 // @option transparent: Boolean = false
11667                 // If `true`, the WMS service will return images with transparency.
11668                 transparent: false,
11669
11670                 // @option version: String = '1.1.1'
11671                 // Version of the WMS service to use
11672                 version: '1.1.1'
11673         },
11674
11675         options: {
11676                 // @option crs: CRS = null
11677                 // Coordinate Reference System to use for the WMS requests, defaults to
11678                 // map CRS. Don't change this if you're not sure what it means.
11679                 crs: null,
11680
11681                 // @option uppercase: Boolean = false
11682                 // If `true`, WMS request parameter keys will be uppercase.
11683                 uppercase: false
11684         },
11685
11686         initialize: function (url, options) {
11687
11688                 this._url = url;
11689
11690                 var wmsParams = extend({}, this.defaultWmsParams);
11691
11692                 // all keys that are not TileLayer options go to WMS params
11693                 for (var i in options) {
11694                         if (!(i in this.options)) {
11695                                 wmsParams[i] = options[i];
11696                         }
11697                 }
11698
11699                 options = setOptions(this, options);
11700
11701                 var realRetina = options.detectRetina && retina ? 2 : 1;
11702                 var tileSize = this.getTileSize();
11703                 wmsParams.width = tileSize.x * realRetina;
11704                 wmsParams.height = tileSize.y * realRetina;
11705
11706                 this.wmsParams = wmsParams;
11707         },
11708
11709         onAdd: function (map) {
11710
11711                 this._crs = this.options.crs || map.options.crs;
11712                 this._wmsVersion = parseFloat(this.wmsParams.version);
11713
11714                 var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
11715                 this.wmsParams[projectionKey] = this._crs.code;
11716
11717                 TileLayer.prototype.onAdd.call(this, map);
11718         },
11719
11720         getTileUrl: function (coords) {
11721
11722                 var tileBounds = this._tileCoordsToNwSe(coords),
11723                     crs = this._crs,
11724                     bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])),
11725                     min = bounds.min,
11726                     max = bounds.max,
11727                     bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
11728                     [min.y, min.x, max.y, max.x] :
11729                     [min.x, min.y, max.x, max.y]).join(','),
11730                     url = TileLayer.prototype.getTileUrl.call(this, coords);
11731                 return url +
11732                         getParamString(this.wmsParams, url, this.options.uppercase) +
11733                         (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
11734         },
11735
11736         // @method setParams(params: Object, noRedraw?: Boolean): this
11737         // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
11738         setParams: function (params, noRedraw) {
11739
11740                 extend(this.wmsParams, params);
11741
11742                 if (!noRedraw) {
11743                         this.redraw();
11744                 }
11745
11746                 return this;
11747         }
11748 });
11749
11750
11751 // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
11752 // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
11753 function tileLayerWMS(url, options) {
11754         return new TileLayerWMS(url, options);
11755 }
11756
11757 TileLayer.WMS = TileLayerWMS;
11758 tileLayer.wms = tileLayerWMS;
11759
11760 /*
11761  * @class Renderer
11762  * @inherits Layer
11763  * @aka L.Renderer
11764  *
11765  * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
11766  * DOM container of the renderer, its bounds, and its zoom animation.
11767  *
11768  * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
11769  * itself can be added or removed to the map. All paths use a renderer, which can
11770  * be implicit (the map will decide the type of renderer and use it automatically)
11771  * or explicit (using the [`renderer`](#path-renderer) option of the path).
11772  *
11773  * Do not use this class directly, use `SVG` and `Canvas` instead.
11774  *
11775  * @event update: Event
11776  * Fired when the renderer updates its bounds, center and zoom, for example when
11777  * its map has moved
11778  */
11779
11780 var Renderer = Layer.extend({
11781
11782         // @section
11783         // @aka Renderer options
11784         options: {
11785                 // @option padding: Number = 0.1
11786                 // How much to extend the clip area around the map view (relative to its size)
11787                 // e.g. 0.1 would be 10% of map view in each direction
11788                 padding: 0.1,
11789
11790                 // @option tolerance: Number = 0
11791                 // How much to extend click tolerance round a path/object on the map
11792                 tolerance : 0
11793         },
11794
11795         initialize: function (options) {
11796                 setOptions(this, options);
11797                 stamp(this);
11798                 this._layers = this._layers || {};
11799         },
11800
11801         onAdd: function () {
11802                 if (!this._container) {
11803                         this._initContainer(); // defined by renderer implementations
11804
11805                         if (this._zoomAnimated) {
11806                                 addClass(this._container, 'leaflet-zoom-animated');
11807                         }
11808                 }
11809
11810                 this.getPane().appendChild(this._container);
11811                 this._update();
11812                 this.on('update', this._updatePaths, this);
11813         },
11814
11815         onRemove: function () {
11816                 this.off('update', this._updatePaths, this);
11817                 this._destroyContainer();
11818         },
11819
11820         getEvents: function () {
11821                 var events = {
11822                         viewreset: this._reset,
11823                         zoom: this._onZoom,
11824                         moveend: this._update,
11825                         zoomend: this._onZoomEnd
11826                 };
11827                 if (this._zoomAnimated) {
11828                         events.zoomanim = this._onAnimZoom;
11829                 }
11830                 return events;
11831         },
11832
11833         _onAnimZoom: function (ev) {
11834                 this._updateTransform(ev.center, ev.zoom);
11835         },
11836
11837         _onZoom: function () {
11838                 this._updateTransform(this._map.getCenter(), this._map.getZoom());
11839         },
11840
11841         _updateTransform: function (center, zoom) {
11842                 var scale = this._map.getZoomScale(zoom, this._zoom),
11843                     position = getPosition(this._container),
11844                     viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
11845                     currentCenterPoint = this._map.project(this._center, zoom),
11846                     destCenterPoint = this._map.project(center, zoom),
11847                     centerOffset = destCenterPoint.subtract(currentCenterPoint),
11848
11849                     topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
11850
11851                 if (any3d) {
11852                         setTransform(this._container, topLeftOffset, scale);
11853                 } else {
11854                         setPosition(this._container, topLeftOffset);
11855                 }
11856         },
11857
11858         _reset: function () {
11859                 this._update();
11860                 this._updateTransform(this._center, this._zoom);
11861
11862                 for (var id in this._layers) {
11863                         this._layers[id]._reset();
11864                 }
11865         },
11866
11867         _onZoomEnd: function () {
11868                 for (var id in this._layers) {
11869                         this._layers[id]._project();
11870                 }
11871         },
11872
11873         _updatePaths: function () {
11874                 for (var id in this._layers) {
11875                         this._layers[id]._update();
11876                 }
11877         },
11878
11879         _update: function () {
11880                 // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
11881                 // Subclasses are responsible of firing the 'update' event.
11882                 var p = this.options.padding,
11883                     size = this._map.getSize(),
11884                     min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
11885
11886                 this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
11887
11888                 this._center = this._map.getCenter();
11889                 this._zoom = this._map.getZoom();
11890         }
11891 });
11892
11893 /*
11894  * @class Canvas
11895  * @inherits Renderer
11896  * @aka L.Canvas
11897  *
11898  * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
11899  * Inherits `Renderer`.
11900  *
11901  * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
11902  * available in all web browsers, notably IE8, and overlapping geometries might
11903  * not display properly in some edge cases.
11904  *
11905  * @example
11906  *
11907  * Use Canvas by default for all paths in the map:
11908  *
11909  * ```js
11910  * var map = L.map('map', {
11911  *      renderer: L.canvas()
11912  * });
11913  * ```
11914  *
11915  * Use a Canvas renderer with extra padding for specific vector geometries:
11916  *
11917  * ```js
11918  * var map = L.map('map');
11919  * var myRenderer = L.canvas({ padding: 0.5 });
11920  * var line = L.polyline( coordinates, { renderer: myRenderer } );
11921  * var circle = L.circle( center, { renderer: myRenderer } );
11922  * ```
11923  */
11924
11925 var Canvas = Renderer.extend({
11926         getEvents: function () {
11927                 var events = Renderer.prototype.getEvents.call(this);
11928                 events.viewprereset = this._onViewPreReset;
11929                 return events;
11930         },
11931
11932         _onViewPreReset: function () {
11933                 // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
11934                 this._postponeUpdatePaths = true;
11935         },
11936
11937         onAdd: function () {
11938                 Renderer.prototype.onAdd.call(this);
11939
11940                 // Redraw vectors since canvas is cleared upon removal,
11941                 // in case of removing the renderer itself from the map.
11942                 this._draw();
11943         },
11944
11945         _initContainer: function () {
11946                 var container = this._container = document.createElement('canvas');
11947
11948                 on(container, 'mousemove', throttle(this._onMouseMove, 32, this), this);
11949                 on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
11950                 on(container, 'mouseout', this._handleMouseOut, this);
11951
11952                 this._ctx = container.getContext('2d');
11953         },
11954
11955         _destroyContainer: function () {
11956                 cancelAnimFrame(this._redrawRequest);
11957                 delete this._ctx;
11958                 remove(this._container);
11959                 off(this._container);
11960                 delete this._container;
11961         },
11962
11963         _updatePaths: function () {
11964                 if (this._postponeUpdatePaths) { return; }
11965
11966                 var layer;
11967                 this._redrawBounds = null;
11968                 for (var id in this._layers) {
11969                         layer = this._layers[id];
11970                         layer._update();
11971                 }
11972                 this._redraw();
11973         },
11974
11975         _update: function () {
11976                 if (this._map._animatingZoom && this._bounds) { return; }
11977
11978                 this._drawnLayers = {};
11979
11980                 Renderer.prototype._update.call(this);
11981
11982                 var b = this._bounds,
11983                     container = this._container,
11984                     size = b.getSize(),
11985                     m = retina ? 2 : 1;
11986
11987                 setPosition(container, b.min);
11988
11989                 // set canvas size (also clearing it); use double size on retina
11990                 container.width = m * size.x;
11991                 container.height = m * size.y;
11992                 container.style.width = size.x + 'px';
11993                 container.style.height = size.y + 'px';
11994
11995                 if (retina) {
11996                         this._ctx.scale(2, 2);
11997                 }
11998
11999                 // translate so we use the same path coordinates after canvas element moves
12000                 this._ctx.translate(-b.min.x, -b.min.y);
12001
12002                 // Tell paths to redraw themselves
12003                 this.fire('update');
12004         },
12005
12006         _reset: function () {
12007                 Renderer.prototype._reset.call(this);
12008
12009                 if (this._postponeUpdatePaths) {
12010                         this._postponeUpdatePaths = false;
12011                         this._updatePaths();
12012                 }
12013         },
12014
12015         _initPath: function (layer) {
12016                 this._updateDashArray(layer);
12017                 this._layers[stamp(layer)] = layer;
12018
12019                 var order = layer._order = {
12020                         layer: layer,
12021                         prev: this._drawLast,
12022                         next: null
12023                 };
12024                 if (this._drawLast) { this._drawLast.next = order; }
12025                 this._drawLast = order;
12026                 this._drawFirst = this._drawFirst || this._drawLast;
12027         },
12028
12029         _addPath: function (layer) {
12030                 this._requestRedraw(layer);
12031         },
12032
12033         _removePath: function (layer) {
12034                 var order = layer._order;
12035                 var next = order.next;
12036                 var prev = order.prev;
12037
12038                 if (next) {
12039                         next.prev = prev;
12040                 } else {
12041                         this._drawLast = prev;
12042                 }
12043                 if (prev) {
12044                         prev.next = next;
12045                 } else {
12046                         this._drawFirst = next;
12047                 }
12048
12049                 delete this._drawnLayers[layer._leaflet_id];
12050
12051                 delete layer._order;
12052
12053                 delete this._layers[stamp(layer)];
12054
12055                 this._requestRedraw(layer);
12056         },
12057
12058         _updatePath: function (layer) {
12059                 // Redraw the union of the layer's old pixel
12060                 // bounds and the new pixel bounds.
12061                 this._extendRedrawBounds(layer);
12062                 layer._project();
12063                 layer._update();
12064                 // The redraw will extend the redraw bounds
12065                 // with the new pixel bounds.
12066                 this._requestRedraw(layer);
12067         },
12068
12069         _updateStyle: function (layer) {
12070                 this._updateDashArray(layer);
12071                 this._requestRedraw(layer);
12072         },
12073
12074         _updateDashArray: function (layer) {
12075                 if (typeof layer.options.dashArray === 'string') {
12076                         var parts = layer.options.dashArray.split(','),
12077                             dashArray = [],
12078                             i;
12079                         for (i = 0; i < parts.length; i++) {
12080                                 dashArray.push(Number(parts[i]));
12081                         }
12082                         layer.options._dashArray = dashArray;
12083                 } else {
12084                         layer.options._dashArray = layer.options.dashArray;
12085                 }
12086         },
12087
12088         _requestRedraw: function (layer) {
12089                 if (!this._map) { return; }
12090
12091                 this._extendRedrawBounds(layer);
12092                 this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
12093         },
12094
12095         _extendRedrawBounds: function (layer) {
12096                 if (layer._pxBounds) {
12097                         var padding = (layer.options.weight || 0) + 1;
12098                         this._redrawBounds = this._redrawBounds || new Bounds();
12099                         this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
12100                         this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
12101                 }
12102         },
12103
12104         _redraw: function () {
12105                 this._redrawRequest = null;
12106
12107                 if (this._redrawBounds) {
12108                         this._redrawBounds.min._floor();
12109                         this._redrawBounds.max._ceil();
12110                 }
12111
12112                 this._clear(); // clear layers in redraw bounds
12113                 this._draw(); // draw layers
12114
12115                 this._redrawBounds = null;
12116         },
12117
12118         _clear: function () {
12119                 var bounds = this._redrawBounds;
12120                 if (bounds) {
12121                         var size = bounds.getSize();
12122                         this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
12123                 } else {
12124                         this._ctx.clearRect(0, 0, this._container.width, this._container.height);
12125                 }
12126         },
12127
12128         _draw: function () {
12129                 var layer, bounds = this._redrawBounds;
12130                 this._ctx.save();
12131                 if (bounds) {
12132                         var size = bounds.getSize();
12133                         this._ctx.beginPath();
12134                         this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
12135                         this._ctx.clip();
12136                 }
12137
12138                 this._drawing = true;
12139
12140                 for (var order = this._drawFirst; order; order = order.next) {
12141                         layer = order.layer;
12142                         if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
12143                                 layer._updatePath();
12144                         }
12145                 }
12146
12147                 this._drawing = false;
12148
12149                 this._ctx.restore();  // Restore state before clipping.
12150         },
12151
12152         _updatePoly: function (layer, closed) {
12153                 if (!this._drawing) { return; }
12154
12155                 var i, j, len2, p,
12156                     parts = layer._parts,
12157                     len = parts.length,
12158                     ctx = this._ctx;
12159
12160                 if (!len) { return; }
12161
12162                 this._drawnLayers[layer._leaflet_id] = layer;
12163
12164                 ctx.beginPath();
12165
12166                 for (i = 0; i < len; i++) {
12167                         for (j = 0, len2 = parts[i].length; j < len2; j++) {
12168                                 p = parts[i][j];
12169                                 ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
12170                         }
12171                         if (closed) {
12172                                 ctx.closePath();
12173                         }
12174                 }
12175
12176                 this._fillStroke(ctx, layer);
12177
12178                 // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
12179         },
12180
12181         _updateCircle: function (layer) {
12182
12183                 if (!this._drawing || layer._empty()) { return; }
12184
12185                 var p = layer._point,
12186                     ctx = this._ctx,
12187                     r = Math.max(Math.round(layer._radius), 1),
12188                     s = (Math.max(Math.round(layer._radiusY), 1) || r) / r;
12189
12190                 this._drawnLayers[layer._leaflet_id] = layer;
12191
12192                 if (s !== 1) {
12193                         ctx.save();
12194                         ctx.scale(1, s);
12195                 }
12196
12197                 ctx.beginPath();
12198                 ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
12199
12200                 if (s !== 1) {
12201                         ctx.restore();
12202                 }
12203
12204                 this._fillStroke(ctx, layer);
12205         },
12206
12207         _fillStroke: function (ctx, layer) {
12208                 var options = layer.options;
12209
12210                 if (options.fill) {
12211                         ctx.globalAlpha = options.fillOpacity;
12212                         ctx.fillStyle = options.fillColor || options.color;
12213                         ctx.fill(options.fillRule || 'evenodd');
12214                 }
12215
12216                 if (options.stroke && options.weight !== 0) {
12217                         if (ctx.setLineDash) {
12218                                 ctx.setLineDash(layer.options && layer.options._dashArray || []);
12219                         }
12220                         ctx.globalAlpha = options.opacity;
12221                         ctx.lineWidth = options.weight;
12222                         ctx.strokeStyle = options.color;
12223                         ctx.lineCap = options.lineCap;
12224                         ctx.lineJoin = options.lineJoin;
12225                         ctx.stroke();
12226                 }
12227         },
12228
12229         // Canvas obviously doesn't have mouse events for individual drawn objects,
12230         // so we emulate that by calculating what's under the mouse on mousemove/click manually
12231
12232         _onClick: function (e) {
12233                 var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
12234
12235                 for (var order = this._drawFirst; order; order = order.next) {
12236                         layer = order.layer;
12237                         if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
12238                                 clickedLayer = layer;
12239                         }
12240                 }
12241                 if (clickedLayer)  {
12242                         fakeStop(e);
12243                         this._fireEvent([clickedLayer], e);
12244                 }
12245         },
12246
12247         _onMouseMove: function (e) {
12248                 if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
12249
12250                 var point = this._map.mouseEventToLayerPoint(e);
12251                 this._handleMouseHover(e, point);
12252         },
12253
12254
12255         _handleMouseOut: function (e) {
12256                 var layer = this._hoveredLayer;
12257                 if (layer) {
12258                         // if we're leaving the layer, fire mouseout
12259                         removeClass(this._container, 'leaflet-interactive');
12260                         this._fireEvent([layer], e, 'mouseout');
12261                         this._hoveredLayer = null;
12262                 }
12263         },
12264
12265         _handleMouseHover: function (e, point) {
12266                 var layer, candidateHoveredLayer;
12267
12268                 for (var order = this._drawFirst; order; order = order.next) {
12269                         layer = order.layer;
12270                         if (layer.options.interactive && layer._containsPoint(point)) {
12271                                 candidateHoveredLayer = layer;
12272                         }
12273                 }
12274
12275                 if (candidateHoveredLayer !== this._hoveredLayer) {
12276                         this._handleMouseOut(e);
12277
12278                         if (candidateHoveredLayer) {
12279                                 addClass(this._container, 'leaflet-interactive'); // change cursor
12280                                 this._fireEvent([candidateHoveredLayer], e, 'mouseover');
12281                                 this._hoveredLayer = candidateHoveredLayer;
12282                         }
12283                 }
12284
12285                 if (this._hoveredLayer) {
12286                         this._fireEvent([this._hoveredLayer], e);
12287                 }
12288         },
12289
12290         _fireEvent: function (layers, e, type) {
12291                 this._map._fireDOMEvent(e, type || e.type, layers);
12292         },
12293
12294         _bringToFront: function (layer) {
12295                 var order = layer._order;
12296                 var next = order.next;
12297                 var prev = order.prev;
12298
12299                 if (next) {
12300                         next.prev = prev;
12301                 } else {
12302                         // Already last
12303                         return;
12304                 }
12305                 if (prev) {
12306                         prev.next = next;
12307                 } else if (next) {
12308                         // Update first entry unless this is the
12309                         // single entry
12310                         this._drawFirst = next;
12311                 }
12312
12313                 order.prev = this._drawLast;
12314                 this._drawLast.next = order;
12315
12316                 order.next = null;
12317                 this._drawLast = order;
12318
12319                 this._requestRedraw(layer);
12320         },
12321
12322         _bringToBack: function (layer) {
12323                 var order = layer._order;
12324                 var next = order.next;
12325                 var prev = order.prev;
12326
12327                 if (prev) {
12328                         prev.next = next;
12329                 } else {
12330                         // Already first
12331                         return;
12332                 }
12333                 if (next) {
12334                         next.prev = prev;
12335                 } else if (prev) {
12336                         // Update last entry unless this is the
12337                         // single entry
12338                         this._drawLast = prev;
12339                 }
12340
12341                 order.prev = null;
12342
12343                 order.next = this._drawFirst;
12344                 this._drawFirst.prev = order;
12345                 this._drawFirst = order;
12346
12347                 this._requestRedraw(layer);
12348         }
12349 });
12350
12351 // @factory L.canvas(options?: Renderer options)
12352 // Creates a Canvas renderer with the given options.
12353 function canvas$1(options) {
12354         return canvas ? new Canvas(options) : null;
12355 }
12356
12357 /*
12358  * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
12359  */
12360
12361
12362 var vmlCreate = (function () {
12363         try {
12364                 document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
12365                 return function (name) {
12366                         return document.createElement('<lvml:' + name + ' class="lvml">');
12367                 };
12368         } catch (e) {
12369                 return function (name) {
12370                         return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
12371                 };
12372         }
12373 })();
12374
12375
12376 /*
12377  * @class SVG
12378  *
12379  * Although SVG is not available on IE7 and IE8, these browsers support [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language), and the SVG renderer will fall back to VML in this case.
12380  *
12381  * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
12382  * with old versions of Internet Explorer.
12383  */
12384
12385 // mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
12386 var vmlMixin = {
12387
12388         _initContainer: function () {
12389                 this._container = create$1('div', 'leaflet-vml-container');
12390         },
12391
12392         _update: function () {
12393                 if (this._map._animatingZoom) { return; }
12394                 Renderer.prototype._update.call(this);
12395                 this.fire('update');
12396         },
12397
12398         _initPath: function (layer) {
12399                 var container = layer._container = vmlCreate('shape');
12400
12401                 addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
12402
12403                 container.coordsize = '1 1';
12404
12405                 layer._path = vmlCreate('path');
12406                 container.appendChild(layer._path);
12407
12408                 this._updateStyle(layer);
12409                 this._layers[stamp(layer)] = layer;
12410         },
12411
12412         _addPath: function (layer) {
12413                 var container = layer._container;
12414                 this._container.appendChild(container);
12415
12416                 if (layer.options.interactive) {
12417                         layer.addInteractiveTarget(container);
12418                 }
12419         },
12420
12421         _removePath: function (layer) {
12422                 var container = layer._container;
12423                 remove(container);
12424                 layer.removeInteractiveTarget(container);
12425                 delete this._layers[stamp(layer)];
12426         },
12427
12428         _updateStyle: function (layer) {
12429                 var stroke = layer._stroke,
12430                     fill = layer._fill,
12431                     options = layer.options,
12432                     container = layer._container;
12433
12434                 container.stroked = !!options.stroke;
12435                 container.filled = !!options.fill;
12436
12437                 if (options.stroke) {
12438                         if (!stroke) {
12439                                 stroke = layer._stroke = vmlCreate('stroke');
12440                         }
12441                         container.appendChild(stroke);
12442                         stroke.weight = options.weight + 'px';
12443                         stroke.color = options.color;
12444                         stroke.opacity = options.opacity;
12445
12446                         if (options.dashArray) {
12447                                 stroke.dashStyle = isArray(options.dashArray) ?
12448                                     options.dashArray.join(' ') :
12449                                     options.dashArray.replace(/( *, *)/g, ' ');
12450                         } else {
12451                                 stroke.dashStyle = '';
12452                         }
12453                         stroke.endcap = options.lineCap.replace('butt', 'flat');
12454                         stroke.joinstyle = options.lineJoin;
12455
12456                 } else if (stroke) {
12457                         container.removeChild(stroke);
12458                         layer._stroke = null;
12459                 }
12460
12461                 if (options.fill) {
12462                         if (!fill) {
12463                                 fill = layer._fill = vmlCreate('fill');
12464                         }
12465                         container.appendChild(fill);
12466                         fill.color = options.fillColor || options.color;
12467                         fill.opacity = options.fillOpacity;
12468
12469                 } else if (fill) {
12470                         container.removeChild(fill);
12471                         layer._fill = null;
12472                 }
12473         },
12474
12475         _updateCircle: function (layer) {
12476                 var p = layer._point.round(),
12477                     r = Math.round(layer._radius),
12478                     r2 = Math.round(layer._radiusY || r);
12479
12480                 this._setPath(layer, layer._empty() ? 'M0 0' :
12481                         'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
12482         },
12483
12484         _setPath: function (layer, path) {
12485                 layer._path.v = path;
12486         },
12487
12488         _bringToFront: function (layer) {
12489                 toFront(layer._container);
12490         },
12491
12492         _bringToBack: function (layer) {
12493                 toBack(layer._container);
12494         }
12495 };
12496
12497 var create$2 = vml ? vmlCreate : svgCreate;
12498
12499 /*
12500  * @class SVG
12501  * @inherits Renderer
12502  * @aka L.SVG
12503  *
12504  * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
12505  * Inherits `Renderer`.
12506  *
12507  * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
12508  * available in all web browsers, notably Android 2.x and 3.x.
12509  *
12510  * Although SVG is not available on IE7 and IE8, these browsers support
12511  * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
12512  * (a now deprecated technology), and the SVG renderer will fall back to VML in
12513  * this case.
12514  *
12515  * @example
12516  *
12517  * Use SVG by default for all paths in the map:
12518  *
12519  * ```js
12520  * var map = L.map('map', {
12521  *      renderer: L.svg()
12522  * });
12523  * ```
12524  *
12525  * Use a SVG renderer with extra padding for specific vector geometries:
12526  *
12527  * ```js
12528  * var map = L.map('map');
12529  * var myRenderer = L.svg({ padding: 0.5 });
12530  * var line = L.polyline( coordinates, { renderer: myRenderer } );
12531  * var circle = L.circle( center, { renderer: myRenderer } );
12532  * ```
12533  */
12534
12535 var SVG = Renderer.extend({
12536
12537         getEvents: function () {
12538                 var events = Renderer.prototype.getEvents.call(this);
12539                 events.zoomstart = this._onZoomStart;
12540                 return events;
12541         },
12542
12543         _initContainer: function () {
12544                 this._container = create$2('svg');
12545
12546                 // makes it possible to click through svg root; we'll reset it back in individual paths
12547                 this._container.setAttribute('pointer-events', 'none');
12548
12549                 this._rootGroup = create$2('g');
12550                 this._container.appendChild(this._rootGroup);
12551         },
12552
12553         _destroyContainer: function () {
12554                 remove(this._container);
12555                 off(this._container);
12556                 delete this._container;
12557                 delete this._rootGroup;
12558                 delete this._svgSize;
12559         },
12560
12561         _onZoomStart: function () {
12562                 // Drag-then-pinch interactions might mess up the center and zoom.
12563                 // In this case, the easiest way to prevent this is re-do the renderer
12564                 //   bounds and padding when the zooming starts.
12565                 this._update();
12566         },
12567
12568         _update: function () {
12569                 if (this._map._animatingZoom && this._bounds) { return; }
12570
12571                 Renderer.prototype._update.call(this);
12572
12573                 var b = this._bounds,
12574                     size = b.getSize(),
12575                     container = this._container;
12576
12577                 // set size of svg-container if changed
12578                 if (!this._svgSize || !this._svgSize.equals(size)) {
12579                         this._svgSize = size;
12580                         container.setAttribute('width', size.x);
12581                         container.setAttribute('height', size.y);
12582                 }
12583
12584                 // movement: update container viewBox so that we don't have to change coordinates of individual layers
12585                 setPosition(container, b.min);
12586                 container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
12587
12588                 this.fire('update');
12589         },
12590
12591         // methods below are called by vector layers implementations
12592
12593         _initPath: function (layer) {
12594                 var path = layer._path = create$2('path');
12595
12596                 // @namespace Path
12597                 // @option className: String = null
12598                 // Custom class name set on an element. Only for SVG renderer.
12599                 if (layer.options.className) {
12600                         addClass(path, layer.options.className);
12601                 }
12602
12603                 if (layer.options.interactive) {
12604                         addClass(path, 'leaflet-interactive');
12605                 }
12606
12607                 this._updateStyle(layer);
12608                 this._layers[stamp(layer)] = layer;
12609         },
12610
12611         _addPath: function (layer) {
12612                 if (!this._rootGroup) { this._initContainer(); }
12613                 this._rootGroup.appendChild(layer._path);
12614                 layer.addInteractiveTarget(layer._path);
12615         },
12616
12617         _removePath: function (layer) {
12618                 remove(layer._path);
12619                 layer.removeInteractiveTarget(layer._path);
12620                 delete this._layers[stamp(layer)];
12621         },
12622
12623         _updatePath: function (layer) {
12624                 layer._project();
12625                 layer._update();
12626         },
12627
12628         _updateStyle: function (layer) {
12629                 var path = layer._path,
12630                     options = layer.options;
12631
12632                 if (!path) { return; }
12633
12634                 if (options.stroke) {
12635                         path.setAttribute('stroke', options.color);
12636                         path.setAttribute('stroke-opacity', options.opacity);
12637                         path.setAttribute('stroke-width', options.weight);
12638                         path.setAttribute('stroke-linecap', options.lineCap);
12639                         path.setAttribute('stroke-linejoin', options.lineJoin);
12640
12641                         if (options.dashArray) {
12642                                 path.setAttribute('stroke-dasharray', options.dashArray);
12643                         } else {
12644                                 path.removeAttribute('stroke-dasharray');
12645                         }
12646
12647                         if (options.dashOffset) {
12648                                 path.setAttribute('stroke-dashoffset', options.dashOffset);
12649                         } else {
12650                                 path.removeAttribute('stroke-dashoffset');
12651                         }
12652                 } else {
12653                         path.setAttribute('stroke', 'none');
12654                 }
12655
12656                 if (options.fill) {
12657                         path.setAttribute('fill', options.fillColor || options.color);
12658                         path.setAttribute('fill-opacity', options.fillOpacity);
12659                         path.setAttribute('fill-rule', options.fillRule || 'evenodd');
12660                 } else {
12661                         path.setAttribute('fill', 'none');
12662                 }
12663         },
12664
12665         _updatePoly: function (layer, closed) {
12666                 this._setPath(layer, pointsToPath(layer._parts, closed));
12667         },
12668
12669         _updateCircle: function (layer) {
12670                 var p = layer._point,
12671                     r = Math.max(Math.round(layer._radius), 1),
12672                     r2 = Math.max(Math.round(layer._radiusY), 1) || r,
12673                     arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
12674
12675                 // drawing a circle with two half-arcs
12676                 var d = layer._empty() ? 'M0 0' :
12677                         'M' + (p.x - r) + ',' + p.y +
12678                         arc + (r * 2) + ',0 ' +
12679                         arc + (-r * 2) + ',0 ';
12680
12681                 this._setPath(layer, d);
12682         },
12683
12684         _setPath: function (layer, path) {
12685                 layer._path.setAttribute('d', path);
12686         },
12687
12688         // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
12689         _bringToFront: function (layer) {
12690                 toFront(layer._path);
12691         },
12692
12693         _bringToBack: function (layer) {
12694                 toBack(layer._path);
12695         }
12696 });
12697
12698 if (vml) {
12699         SVG.include(vmlMixin);
12700 }
12701
12702 // @namespace SVG
12703 // @factory L.svg(options?: Renderer options)
12704 // Creates a SVG renderer with the given options.
12705 function svg$1(options) {
12706         return svg || vml ? new SVG(options) : null;
12707 }
12708
12709 Map.include({
12710         // @namespace Map; @method getRenderer(layer: Path): Renderer
12711         // Returns the instance of `Renderer` that should be used to render the given
12712         // `Path`. It will ensure that the `renderer` options of the map and paths
12713         // are respected, and that the renderers do exist on the map.
12714         getRenderer: function (layer) {
12715                 // @namespace Path; @option renderer: Renderer
12716                 // Use this specific instance of `Renderer` for this path. Takes
12717                 // precedence over the map's [default renderer](#map-renderer).
12718                 var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
12719
12720                 if (!renderer) {
12721                         renderer = this._renderer = this._createRenderer();
12722                 }
12723
12724                 if (!this.hasLayer(renderer)) {
12725                         this.addLayer(renderer);
12726                 }
12727                 return renderer;
12728         },
12729
12730         _getPaneRenderer: function (name) {
12731                 if (name === 'overlayPane' || name === undefined) {
12732                         return false;
12733                 }
12734
12735                 var renderer = this._paneRenderers[name];
12736                 if (renderer === undefined) {
12737                         renderer = this._createRenderer({pane: name});
12738                         this._paneRenderers[name] = renderer;
12739                 }
12740                 return renderer;
12741         },
12742
12743         _createRenderer: function (options) {
12744                 // @namespace Map; @option preferCanvas: Boolean = false
12745                 // Whether `Path`s should be rendered on a `Canvas` renderer.
12746                 // By default, all `Path`s are rendered in a `SVG` renderer.
12747                 return (this.options.preferCanvas && canvas$1(options)) || svg$1(options);
12748         }
12749 });
12750
12751 /*
12752  * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
12753  */
12754
12755 /*
12756  * @class Rectangle
12757  * @aka L.Rectangle
12758  * @inherits Polygon
12759  *
12760  * A class for drawing rectangle overlays on a map. Extends `Polygon`.
12761  *
12762  * @example
12763  *
12764  * ```js
12765  * // define rectangle geographical bounds
12766  * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
12767  *
12768  * // create an orange rectangle
12769  * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
12770  *
12771  * // zoom the map to the rectangle bounds
12772  * map.fitBounds(bounds);
12773  * ```
12774  *
12775  */
12776
12777
12778 var Rectangle = Polygon.extend({
12779         initialize: function (latLngBounds, options) {
12780                 Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
12781         },
12782
12783         // @method setBounds(latLngBounds: LatLngBounds): this
12784         // Redraws the rectangle with the passed bounds.
12785         setBounds: function (latLngBounds) {
12786                 return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
12787         },
12788
12789         _boundsToLatLngs: function (latLngBounds) {
12790                 latLngBounds = toLatLngBounds(latLngBounds);
12791                 return [
12792                         latLngBounds.getSouthWest(),
12793                         latLngBounds.getNorthWest(),
12794                         latLngBounds.getNorthEast(),
12795                         latLngBounds.getSouthEast()
12796                 ];
12797         }
12798 });
12799
12800
12801 // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
12802 function rectangle(latLngBounds, options) {
12803         return new Rectangle(latLngBounds, options);
12804 }
12805
12806 SVG.create = create$2;
12807 SVG.pointsToPath = pointsToPath;
12808
12809 GeoJSON.geometryToLayer = geometryToLayer;
12810 GeoJSON.coordsToLatLng = coordsToLatLng;
12811 GeoJSON.coordsToLatLngs = coordsToLatLngs;
12812 GeoJSON.latLngToCoords = latLngToCoords;
12813 GeoJSON.latLngsToCoords = latLngsToCoords;
12814 GeoJSON.getFeature = getFeature;
12815 GeoJSON.asFeature = asFeature;
12816
12817 /*
12818  * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
12819  * (zoom to a selected bounding box), enabled by default.
12820  */
12821
12822 // @namespace Map
12823 // @section Interaction Options
12824 Map.mergeOptions({
12825         // @option boxZoom: Boolean = true
12826         // Whether the map can be zoomed to a rectangular area specified by
12827         // dragging the mouse while pressing the shift key.
12828         boxZoom: true
12829 });
12830
12831 var BoxZoom = Handler.extend({
12832         initialize: function (map) {
12833                 this._map = map;
12834                 this._container = map._container;
12835                 this._pane = map._panes.overlayPane;
12836                 this._resetStateTimeout = 0;
12837                 map.on('unload', this._destroy, this);
12838         },
12839
12840         addHooks: function () {
12841                 on(this._container, 'mousedown', this._onMouseDown, this);
12842         },
12843
12844         removeHooks: function () {
12845                 off(this._container, 'mousedown', this._onMouseDown, this);
12846         },
12847
12848         moved: function () {
12849                 return this._moved;
12850         },
12851
12852         _destroy: function () {
12853                 remove(this._pane);
12854                 delete this._pane;
12855         },
12856
12857         _resetState: function () {
12858                 this._resetStateTimeout = 0;
12859                 this._moved = false;
12860         },
12861
12862         _clearDeferredResetState: function () {
12863                 if (this._resetStateTimeout !== 0) {
12864                         clearTimeout(this._resetStateTimeout);
12865                         this._resetStateTimeout = 0;
12866                 }
12867         },
12868
12869         _onMouseDown: function (e) {
12870                 if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
12871
12872                 // Clear the deferred resetState if it hasn't executed yet, otherwise it
12873                 // will interrupt the interaction and orphan a box element in the container.
12874                 this._clearDeferredResetState();
12875                 this._resetState();
12876
12877                 disableTextSelection();
12878                 disableImageDrag();
12879
12880                 this._startPoint = this._map.mouseEventToContainerPoint(e);
12881
12882                 on(document, {
12883                         contextmenu: stop,
12884                         mousemove: this._onMouseMove,
12885                         mouseup: this._onMouseUp,
12886                         keydown: this._onKeyDown
12887                 }, this);
12888         },
12889
12890         _onMouseMove: function (e) {
12891                 if (!this._moved) {
12892                         this._moved = true;
12893
12894                         this._box = create$1('div', 'leaflet-zoom-box', this._container);
12895                         addClass(this._container, 'leaflet-crosshair');
12896
12897                         this._map.fire('boxzoomstart');
12898                 }
12899
12900                 this._point = this._map.mouseEventToContainerPoint(e);
12901
12902                 var bounds = new Bounds(this._point, this._startPoint),
12903                     size = bounds.getSize();
12904
12905                 setPosition(this._box, bounds.min);
12906
12907                 this._box.style.width  = size.x + 'px';
12908                 this._box.style.height = size.y + 'px';
12909         },
12910
12911         _finish: function () {
12912                 if (this._moved) {
12913                         remove(this._box);
12914                         removeClass(this._container, 'leaflet-crosshair');
12915                 }
12916
12917                 enableTextSelection();
12918                 enableImageDrag();
12919
12920                 off(document, {
12921                         contextmenu: stop,
12922                         mousemove: this._onMouseMove,
12923                         mouseup: this._onMouseUp,
12924                         keydown: this._onKeyDown
12925                 }, this);
12926         },
12927
12928         _onMouseUp: function (e) {
12929                 if ((e.which !== 1) && (e.button !== 1)) { return; }
12930
12931                 this._finish();
12932
12933                 if (!this._moved) { return; }
12934                 // Postpone to next JS tick so internal click event handling
12935                 // still see it as "moved".
12936                 this._clearDeferredResetState();
12937                 this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
12938
12939                 var bounds = new LatLngBounds(
12940                         this._map.containerPointToLatLng(this._startPoint),
12941                         this._map.containerPointToLatLng(this._point));
12942
12943                 this._map
12944                         .fitBounds(bounds)
12945                         .fire('boxzoomend', {boxZoomBounds: bounds});
12946         },
12947
12948         _onKeyDown: function (e) {
12949                 if (e.keyCode === 27) {
12950                         this._finish();
12951                 }
12952         }
12953 });
12954
12955 // @section Handlers
12956 // @property boxZoom: Handler
12957 // Box (shift-drag with mouse) zoom handler.
12958 Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
12959
12960 /*
12961  * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
12962  */
12963
12964 // @namespace Map
12965 // @section Interaction Options
12966
12967 Map.mergeOptions({
12968         // @option doubleClickZoom: Boolean|String = true
12969         // Whether the map can be zoomed in by double clicking on it and
12970         // zoomed out by double clicking while holding shift. If passed
12971         // `'center'`, double-click zoom will zoom to the center of the
12972         //  view regardless of where the mouse was.
12973         doubleClickZoom: true
12974 });
12975
12976 var DoubleClickZoom = Handler.extend({
12977         addHooks: function () {
12978                 this._map.on('dblclick', this._onDoubleClick, this);
12979         },
12980
12981         removeHooks: function () {
12982                 this._map.off('dblclick', this._onDoubleClick, this);
12983         },
12984
12985         _onDoubleClick: function (e) {
12986                 var map = this._map,
12987                     oldZoom = map.getZoom(),
12988                     delta = map.options.zoomDelta,
12989                     zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
12990
12991                 if (map.options.doubleClickZoom === 'center') {
12992                         map.setZoom(zoom);
12993                 } else {
12994                         map.setZoomAround(e.containerPoint, zoom);
12995                 }
12996         }
12997 });
12998
12999 // @section Handlers
13000 //
13001 // Map properties include interaction handlers that allow you to control
13002 // interaction behavior in runtime, enabling or disabling certain features such
13003 // as dragging or touch zoom (see `Handler` methods). For example:
13004 //
13005 // ```js
13006 // map.doubleClickZoom.disable();
13007 // ```
13008 //
13009 // @property doubleClickZoom: Handler
13010 // Double click zoom handler.
13011 Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
13012
13013 /*
13014  * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
13015  */
13016
13017 // @namespace Map
13018 // @section Interaction Options
13019 Map.mergeOptions({
13020         // @option dragging: Boolean = true
13021         // Whether the map be draggable with mouse/touch or not.
13022         dragging: true,
13023
13024         // @section Panning Inertia Options
13025         // @option inertia: Boolean = *
13026         // If enabled, panning of the map will have an inertia effect where
13027         // the map builds momentum while dragging and continues moving in
13028         // the same direction for some time. Feels especially nice on touch
13029         // devices. Enabled by default unless running on old Android devices.
13030         inertia: !android23,
13031
13032         // @option inertiaDeceleration: Number = 3000
13033         // The rate with which the inertial movement slows down, in pixels/second².
13034         inertiaDeceleration: 3400, // px/s^2
13035
13036         // @option inertiaMaxSpeed: Number = Infinity
13037         // Max speed of the inertial movement, in pixels/second.
13038         inertiaMaxSpeed: Infinity, // px/s
13039
13040         // @option easeLinearity: Number = 0.2
13041         easeLinearity: 0.2,
13042
13043         // TODO refactor, move to CRS
13044         // @option worldCopyJump: Boolean = false
13045         // With this option enabled, the map tracks when you pan to another "copy"
13046         // of the world and seamlessly jumps to the original one so that all overlays
13047         // like markers and vector layers are still visible.
13048         worldCopyJump: false,
13049
13050         // @option maxBoundsViscosity: Number = 0.0
13051         // If `maxBounds` is set, this option will control how solid the bounds
13052         // are when dragging the map around. The default value of `0.0` allows the
13053         // user to drag outside the bounds at normal speed, higher values will
13054         // slow down map dragging outside bounds, and `1.0` makes the bounds fully
13055         // solid, preventing the user from dragging outside the bounds.
13056         maxBoundsViscosity: 0.0
13057 });
13058
13059 var Drag = Handler.extend({
13060         addHooks: function () {
13061                 if (!this._draggable) {
13062                         var map = this._map;
13063
13064                         this._draggable = new Draggable(map._mapPane, map._container);
13065
13066                         this._draggable.on({
13067                                 dragstart: this._onDragStart,
13068                                 drag: this._onDrag,
13069                                 dragend: this._onDragEnd
13070                         }, this);
13071
13072                         this._draggable.on('predrag', this._onPreDragLimit, this);
13073                         if (map.options.worldCopyJump) {
13074                                 this._draggable.on('predrag', this._onPreDragWrap, this);
13075                                 map.on('zoomend', this._onZoomEnd, this);
13076
13077                                 map.whenReady(this._onZoomEnd, this);
13078                         }
13079                 }
13080                 addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
13081                 this._draggable.enable();
13082                 this._positions = [];
13083                 this._times = [];
13084         },
13085
13086         removeHooks: function () {
13087                 removeClass(this._map._container, 'leaflet-grab');
13088                 removeClass(this._map._container, 'leaflet-touch-drag');
13089                 this._draggable.disable();
13090         },
13091
13092         moved: function () {
13093                 return this._draggable && this._draggable._moved;
13094         },
13095
13096         moving: function () {
13097                 return this._draggable && this._draggable._moving;
13098         },
13099
13100         _onDragStart: function () {
13101                 var map = this._map;
13102
13103                 map._stop();
13104                 if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
13105                         var bounds = toLatLngBounds(this._map.options.maxBounds);
13106
13107                         this._offsetLimit = toBounds(
13108                                 this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
13109                                 this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
13110                                         .add(this._map.getSize()));
13111
13112                         this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
13113                 } else {
13114                         this._offsetLimit = null;
13115                 }
13116
13117                 map
13118                     .fire('movestart')
13119                     .fire('dragstart');
13120
13121                 if (map.options.inertia) {
13122                         this._positions = [];
13123                         this._times = [];
13124                 }
13125         },
13126
13127         _onDrag: function (e) {
13128                 if (this._map.options.inertia) {
13129                         var time = this._lastTime = +new Date(),
13130                             pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
13131
13132                         this._positions.push(pos);
13133                         this._times.push(time);
13134
13135                         this._prunePositions(time);
13136                 }
13137
13138                 this._map
13139                     .fire('move', e)
13140                     .fire('drag', e);
13141         },
13142
13143         _prunePositions: function (time) {
13144                 while (this._positions.length > 1 && time - this._times[0] > 50) {
13145                         this._positions.shift();
13146                         this._times.shift();
13147                 }
13148         },
13149
13150         _onZoomEnd: function () {
13151                 var pxCenter = this._map.getSize().divideBy(2),
13152                     pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
13153
13154                 this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
13155                 this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
13156         },
13157
13158         _viscousLimit: function (value, threshold) {
13159                 return value - (value - threshold) * this._viscosity;
13160         },
13161
13162         _onPreDragLimit: function () {
13163                 if (!this._viscosity || !this._offsetLimit) { return; }
13164
13165                 var offset = this._draggable._newPos.subtract(this._draggable._startPos);
13166
13167                 var limit = this._offsetLimit;
13168                 if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
13169                 if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
13170                 if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
13171                 if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
13172
13173                 this._draggable._newPos = this._draggable._startPos.add(offset);
13174         },
13175
13176         _onPreDragWrap: function () {
13177                 // TODO refactor to be able to adjust map pane position after zoom
13178                 var worldWidth = this._worldWidth,
13179                     halfWidth = Math.round(worldWidth / 2),
13180                     dx = this._initialWorldOffset,
13181                     x = this._draggable._newPos.x,
13182                     newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
13183                     newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
13184                     newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
13185
13186                 this._draggable._absPos = this._draggable._newPos.clone();
13187                 this._draggable._newPos.x = newX;
13188         },
13189
13190         _onDragEnd: function (e) {
13191                 var map = this._map,
13192                     options = map.options,
13193
13194                     noInertia = !options.inertia || this._times.length < 2;
13195
13196                 map.fire('dragend', e);
13197
13198                 if (noInertia) {
13199                         map.fire('moveend');
13200
13201                 } else {
13202                         this._prunePositions(+new Date());
13203
13204                         var direction = this._lastPos.subtract(this._positions[0]),
13205                             duration = (this._lastTime - this._times[0]) / 1000,
13206                             ease = options.easeLinearity,
13207
13208                             speedVector = direction.multiplyBy(ease / duration),
13209                             speed = speedVector.distanceTo([0, 0]),
13210
13211                             limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
13212                             limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
13213
13214                             decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
13215                             offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
13216
13217                         if (!offset.x && !offset.y) {
13218                                 map.fire('moveend');
13219
13220                         } else {
13221                                 offset = map._limitOffset(offset, map.options.maxBounds);
13222
13223                                 requestAnimFrame(function () {
13224                                         map.panBy(offset, {
13225                                                 duration: decelerationDuration,
13226                                                 easeLinearity: ease,
13227                                                 noMoveStart: true,
13228                                                 animate: true
13229                                         });
13230                                 });
13231                         }
13232                 }
13233         }
13234 });
13235
13236 // @section Handlers
13237 // @property dragging: Handler
13238 // Map dragging handler (by both mouse and touch).
13239 Map.addInitHook('addHandler', 'dragging', Drag);
13240
13241 /*
13242  * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
13243  */
13244
13245 // @namespace Map
13246 // @section Keyboard Navigation Options
13247 Map.mergeOptions({
13248         // @option keyboard: Boolean = true
13249         // Makes the map focusable and allows users to navigate the map with keyboard
13250         // arrows and `+`/`-` keys.
13251         keyboard: true,
13252
13253         // @option keyboardPanDelta: Number = 80
13254         // Amount of pixels to pan when pressing an arrow key.
13255         keyboardPanDelta: 80
13256 });
13257
13258 var Keyboard = Handler.extend({
13259
13260         keyCodes: {
13261                 left:    [37],
13262                 right:   [39],
13263                 down:    [40],
13264                 up:      [38],
13265                 zoomIn:  [187, 107, 61, 171],
13266                 zoomOut: [189, 109, 54, 173]
13267         },
13268
13269         initialize: function (map) {
13270                 this._map = map;
13271
13272                 this._setPanDelta(map.options.keyboardPanDelta);
13273                 this._setZoomDelta(map.options.zoomDelta);
13274         },
13275
13276         addHooks: function () {
13277                 var container = this._map._container;
13278
13279                 // make the container focusable by tabbing
13280                 if (container.tabIndex <= 0) {
13281                         container.tabIndex = '0';
13282                 }
13283
13284                 on(container, {
13285                         focus: this._onFocus,
13286                         blur: this._onBlur,
13287                         mousedown: this._onMouseDown
13288                 }, this);
13289
13290                 this._map.on({
13291                         focus: this._addHooks,
13292                         blur: this._removeHooks
13293                 }, this);
13294         },
13295
13296         removeHooks: function () {
13297                 this._removeHooks();
13298
13299                 off(this._map._container, {
13300                         focus: this._onFocus,
13301                         blur: this._onBlur,
13302                         mousedown: this._onMouseDown
13303                 }, this);
13304
13305                 this._map.off({
13306                         focus: this._addHooks,
13307                         blur: this._removeHooks
13308                 }, this);
13309         },
13310
13311         _onMouseDown: function () {
13312                 if (this._focused) { return; }
13313
13314                 var body = document.body,
13315                     docEl = document.documentElement,
13316                     top = body.scrollTop || docEl.scrollTop,
13317                     left = body.scrollLeft || docEl.scrollLeft;
13318
13319                 this._map._container.focus();
13320
13321                 window.scrollTo(left, top);
13322         },
13323
13324         _onFocus: function () {
13325                 this._focused = true;
13326                 this._map.fire('focus');
13327         },
13328
13329         _onBlur: function () {
13330                 this._focused = false;
13331                 this._map.fire('blur');
13332         },
13333
13334         _setPanDelta: function (panDelta) {
13335                 var keys = this._panKeys = {},
13336                     codes = this.keyCodes,
13337                     i, len;
13338
13339                 for (i = 0, len = codes.left.length; i < len; i++) {
13340                         keys[codes.left[i]] = [-1 * panDelta, 0];
13341                 }
13342                 for (i = 0, len = codes.right.length; i < len; i++) {
13343                         keys[codes.right[i]] = [panDelta, 0];
13344                 }
13345                 for (i = 0, len = codes.down.length; i < len; i++) {
13346                         keys[codes.down[i]] = [0, panDelta];
13347                 }
13348                 for (i = 0, len = codes.up.length; i < len; i++) {
13349                         keys[codes.up[i]] = [0, -1 * panDelta];
13350                 }
13351         },
13352
13353         _setZoomDelta: function (zoomDelta) {
13354                 var keys = this._zoomKeys = {},
13355                     codes = this.keyCodes,
13356                     i, len;
13357
13358                 for (i = 0, len = codes.zoomIn.length; i < len; i++) {
13359                         keys[codes.zoomIn[i]] = zoomDelta;
13360                 }
13361                 for (i = 0, len = codes.zoomOut.length; i < len; i++) {
13362                         keys[codes.zoomOut[i]] = -zoomDelta;
13363                 }
13364         },
13365
13366         _addHooks: function () {
13367                 on(document, 'keydown', this._onKeyDown, this);
13368         },
13369
13370         _removeHooks: function () {
13371                 off(document, 'keydown', this._onKeyDown, this);
13372         },
13373
13374         _onKeyDown: function (e) {
13375                 if (e.altKey || e.ctrlKey || e.metaKey) { return; }
13376
13377                 var key = e.keyCode,
13378                     map = this._map,
13379                     offset;
13380
13381                 if (key in this._panKeys) {
13382                         if (!map._panAnim || !map._panAnim._inProgress) {
13383                                 offset = this._panKeys[key];
13384                                 if (e.shiftKey) {
13385                                         offset = toPoint(offset).multiplyBy(3);
13386                                 }
13387
13388                                 map.panBy(offset);
13389
13390                                 if (map.options.maxBounds) {
13391                                         map.panInsideBounds(map.options.maxBounds);
13392                                 }
13393                         }
13394                 } else if (key in this._zoomKeys) {
13395                         map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
13396
13397                 } else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) {
13398                         map.closePopup();
13399
13400                 } else {
13401                         return;
13402                 }
13403
13404                 stop(e);
13405         }
13406 });
13407
13408 // @section Handlers
13409 // @section Handlers
13410 // @property keyboard: Handler
13411 // Keyboard navigation handler.
13412 Map.addInitHook('addHandler', 'keyboard', Keyboard);
13413
13414 /*
13415  * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
13416  */
13417
13418 // @namespace Map
13419 // @section Interaction Options
13420 Map.mergeOptions({
13421         // @section Mousewheel options
13422         // @option scrollWheelZoom: Boolean|String = true
13423         // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
13424         // it will zoom to the center of the view regardless of where the mouse was.
13425         scrollWheelZoom: true,
13426
13427         // @option wheelDebounceTime: Number = 40
13428         // Limits the rate at which a wheel can fire (in milliseconds). By default
13429         // user can't zoom via wheel more often than once per 40 ms.
13430         wheelDebounceTime: 40,
13431
13432         // @option wheelPxPerZoomLevel: Number = 60
13433         // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
13434         // mean a change of one full zoom level. Smaller values will make wheel-zooming
13435         // faster (and vice versa).
13436         wheelPxPerZoomLevel: 60
13437 });
13438
13439 var ScrollWheelZoom = Handler.extend({
13440         addHooks: function () {
13441                 on(this._map._container, 'mousewheel', this._onWheelScroll, this);
13442
13443                 this._delta = 0;
13444         },
13445
13446         removeHooks: function () {
13447                 off(this._map._container, 'mousewheel', this._onWheelScroll, this);
13448         },
13449
13450         _onWheelScroll: function (e) {
13451                 var delta = getWheelDelta(e);
13452
13453                 var debounce = this._map.options.wheelDebounceTime;
13454
13455                 this._delta += delta;
13456                 this._lastMousePos = this._map.mouseEventToContainerPoint(e);
13457
13458                 if (!this._startTime) {
13459                         this._startTime = +new Date();
13460                 }
13461
13462                 var left = Math.max(debounce - (+new Date() - this._startTime), 0);
13463
13464                 clearTimeout(this._timer);
13465                 this._timer = setTimeout(bind(this._performZoom, this), left);
13466
13467                 stop(e);
13468         },
13469
13470         _performZoom: function () {
13471                 var map = this._map,
13472                     zoom = map.getZoom(),
13473                     snap = this._map.options.zoomSnap || 0;
13474
13475                 map._stop(); // stop panning and fly animations if any
13476
13477                 // map the delta with a sigmoid function to -4..4 range leaning on -1..1
13478                 var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
13479                     d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
13480                     d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
13481                     delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
13482
13483                 this._delta = 0;
13484                 this._startTime = null;
13485
13486                 if (!delta) { return; }
13487
13488                 if (map.options.scrollWheelZoom === 'center') {
13489                         map.setZoom(zoom + delta);
13490                 } else {
13491                         map.setZoomAround(this._lastMousePos, zoom + delta);
13492                 }
13493         }
13494 });
13495
13496 // @section Handlers
13497 // @property scrollWheelZoom: Handler
13498 // Scroll wheel zoom handler.
13499 Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
13500
13501 /*
13502  * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
13503  */
13504
13505 // @namespace Map
13506 // @section Interaction Options
13507 Map.mergeOptions({
13508         // @section Touch interaction options
13509         // @option tap: Boolean = true
13510         // Enables mobile hacks for supporting instant taps (fixing 200ms click
13511         // delay on iOS/Android) and touch holds (fired as `contextmenu` events).
13512         tap: true,
13513
13514         // @option tapTolerance: Number = 15
13515         // The max number of pixels a user can shift his finger during touch
13516         // for it to be considered a valid tap.
13517         tapTolerance: 15
13518 });
13519
13520 var Tap = Handler.extend({
13521         addHooks: function () {
13522                 on(this._map._container, 'touchstart', this._onDown, this);
13523         },
13524
13525         removeHooks: function () {
13526                 off(this._map._container, 'touchstart', this._onDown, this);
13527         },
13528
13529         _onDown: function (e) {
13530                 if (!e.touches) { return; }
13531
13532                 preventDefault(e);
13533
13534                 this._fireClick = true;
13535
13536                 // don't simulate click or track longpress if more than 1 touch
13537                 if (e.touches.length > 1) {
13538                         this._fireClick = false;
13539                         clearTimeout(this._holdTimeout);
13540                         return;
13541                 }
13542
13543                 var first = e.touches[0],
13544                     el = first.target;
13545
13546                 this._startPos = this._newPos = new Point(first.clientX, first.clientY);
13547
13548                 // if touching a link, highlight it
13549                 if (el.tagName && el.tagName.toLowerCase() === 'a') {
13550                         addClass(el, 'leaflet-active');
13551                 }
13552
13553                 // simulate long hold but setting a timeout
13554                 this._holdTimeout = setTimeout(bind(function () {
13555                         if (this._isTapValid()) {
13556                                 this._fireClick = false;
13557                                 this._onUp();
13558                                 this._simulateEvent('contextmenu', first);
13559                         }
13560                 }, this), 1000);
13561
13562                 this._simulateEvent('mousedown', first);
13563
13564                 on(document, {
13565                         touchmove: this._onMove,
13566                         touchend: this._onUp
13567                 }, this);
13568         },
13569
13570         _onUp: function (e) {
13571                 clearTimeout(this._holdTimeout);
13572
13573                 off(document, {
13574                         touchmove: this._onMove,
13575                         touchend: this._onUp
13576                 }, this);
13577
13578                 if (this._fireClick && e && e.changedTouches) {
13579
13580                         var first = e.changedTouches[0],
13581                             el = first.target;
13582
13583                         if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
13584                                 removeClass(el, 'leaflet-active');
13585                         }
13586
13587                         this._simulateEvent('mouseup', first);
13588
13589                         // simulate click if the touch didn't move too much
13590                         if (this._isTapValid()) {
13591                                 this._simulateEvent('click', first);
13592                         }
13593                 }
13594         },
13595
13596         _isTapValid: function () {
13597                 return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
13598         },
13599
13600         _onMove: function (e) {
13601                 var first = e.touches[0];
13602                 this._newPos = new Point(first.clientX, first.clientY);
13603                 this._simulateEvent('mousemove', first);
13604         },
13605
13606         _simulateEvent: function (type, e) {
13607                 var simulatedEvent = document.createEvent('MouseEvents');
13608
13609                 simulatedEvent._simulated = true;
13610                 e.target._simulatedClick = true;
13611
13612                 simulatedEvent.initMouseEvent(
13613                         type, true, true, window, 1,
13614                         e.screenX, e.screenY,
13615                         e.clientX, e.clientY,
13616                         false, false, false, false, 0, null);
13617
13618                 e.target.dispatchEvent(simulatedEvent);
13619         }
13620 });
13621
13622 // @section Handlers
13623 // @property tap: Handler
13624 // Mobile touch hacks (quick tap and touch hold) handler.
13625 if (touch && !pointer) {
13626         Map.addInitHook('addHandler', 'tap', Tap);
13627 }
13628
13629 /*
13630  * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
13631  */
13632
13633 // @namespace Map
13634 // @section Interaction Options
13635 Map.mergeOptions({
13636         // @section Touch interaction options
13637         // @option touchZoom: Boolean|String = *
13638         // Whether the map can be zoomed by touch-dragging with two fingers. If
13639         // passed `'center'`, it will zoom to the center of the view regardless of
13640         // where the touch events (fingers) were. Enabled for touch-capable web
13641         // browsers except for old Androids.
13642         touchZoom: touch && !android23,
13643
13644         // @option bounceAtZoomLimits: Boolean = true
13645         // Set it to false if you don't want the map to zoom beyond min/max zoom
13646         // and then bounce back when pinch-zooming.
13647         bounceAtZoomLimits: true
13648 });
13649
13650 var TouchZoom = Handler.extend({
13651         addHooks: function () {
13652                 addClass(this._map._container, 'leaflet-touch-zoom');
13653                 on(this._map._container, 'touchstart', this._onTouchStart, this);
13654         },
13655
13656         removeHooks: function () {
13657                 removeClass(this._map._container, 'leaflet-touch-zoom');
13658                 off(this._map._container, 'touchstart', this._onTouchStart, this);
13659         },
13660
13661         _onTouchStart: function (e) {
13662                 var map = this._map;
13663                 if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
13664
13665                 var p1 = map.mouseEventToContainerPoint(e.touches[0]),
13666                     p2 = map.mouseEventToContainerPoint(e.touches[1]);
13667
13668                 this._centerPoint = map.getSize()._divideBy(2);
13669                 this._startLatLng = map.containerPointToLatLng(this._centerPoint);
13670                 if (map.options.touchZoom !== 'center') {
13671                         this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
13672                 }
13673
13674                 this._startDist = p1.distanceTo(p2);
13675                 this._startZoom = map.getZoom();
13676
13677                 this._moved = false;
13678                 this._zooming = true;
13679
13680                 map._stop();
13681
13682                 on(document, 'touchmove', this._onTouchMove, this);
13683                 on(document, 'touchend', this._onTouchEnd, this);
13684
13685                 preventDefault(e);
13686         },
13687
13688         _onTouchMove: function (e) {
13689                 if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
13690
13691                 var map = this._map,
13692                     p1 = map.mouseEventToContainerPoint(e.touches[0]),
13693                     p2 = map.mouseEventToContainerPoint(e.touches[1]),
13694                     scale = p1.distanceTo(p2) / this._startDist;
13695
13696                 this._zoom = map.getScaleZoom(scale, this._startZoom);
13697
13698                 if (!map.options.bounceAtZoomLimits && (
13699                         (this._zoom < map.getMinZoom() && scale < 1) ||
13700                         (this._zoom > map.getMaxZoom() && scale > 1))) {
13701                         this._zoom = map._limitZoom(this._zoom);
13702                 }
13703
13704                 if (map.options.touchZoom === 'center') {
13705                         this._center = this._startLatLng;
13706                         if (scale === 1) { return; }
13707                 } else {
13708                         // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
13709                         var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
13710                         if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
13711                         this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
13712                 }
13713
13714                 if (!this._moved) {
13715                         map._moveStart(true, false);
13716                         this._moved = true;
13717                 }
13718
13719                 cancelAnimFrame(this._animRequest);
13720
13721                 var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
13722                 this._animRequest = requestAnimFrame(moveFn, this, true);
13723
13724                 preventDefault(e);
13725         },
13726
13727         _onTouchEnd: function () {
13728                 if (!this._moved || !this._zooming) {
13729                         this._zooming = false;
13730                         return;
13731                 }
13732
13733                 this._zooming = false;
13734                 cancelAnimFrame(this._animRequest);
13735
13736                 off(document, 'touchmove', this._onTouchMove);
13737                 off(document, 'touchend', this._onTouchEnd);
13738
13739                 // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
13740                 if (this._map.options.zoomAnimation) {
13741                         this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
13742                 } else {
13743                         this._map._resetView(this._center, this._map._limitZoom(this._zoom));
13744                 }
13745         }
13746 });
13747
13748 // @section Handlers
13749 // @property touchZoom: Handler
13750 // Touch zoom handler.
13751 Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
13752
13753 Map.BoxZoom = BoxZoom;
13754 Map.DoubleClickZoom = DoubleClickZoom;
13755 Map.Drag = Drag;
13756 Map.Keyboard = Keyboard;
13757 Map.ScrollWheelZoom = ScrollWheelZoom;
13758 Map.Tap = Tap;
13759 Map.TouchZoom = TouchZoom;
13760
13761 Object.freeze = freeze;
13762
13763 exports.version = version;
13764 exports.Control = Control;
13765 exports.control = control;
13766 exports.Browser = Browser;
13767 exports.Evented = Evented;
13768 exports.Mixin = Mixin;
13769 exports.Util = Util;
13770 exports.Class = Class;
13771 exports.Handler = Handler;
13772 exports.extend = extend;
13773 exports.bind = bind;
13774 exports.stamp = stamp;
13775 exports.setOptions = setOptions;
13776 exports.DomEvent = DomEvent;
13777 exports.DomUtil = DomUtil;
13778 exports.PosAnimation = PosAnimation;
13779 exports.Draggable = Draggable;
13780 exports.LineUtil = LineUtil;
13781 exports.PolyUtil = PolyUtil;
13782 exports.Point = Point;
13783 exports.point = toPoint;
13784 exports.Bounds = Bounds;
13785 exports.bounds = toBounds;
13786 exports.Transformation = Transformation;
13787 exports.transformation = toTransformation;
13788 exports.Projection = index;
13789 exports.LatLng = LatLng;
13790 exports.latLng = toLatLng;
13791 exports.LatLngBounds = LatLngBounds;
13792 exports.latLngBounds = toLatLngBounds;
13793 exports.CRS = CRS;
13794 exports.GeoJSON = GeoJSON;
13795 exports.geoJSON = geoJSON;
13796 exports.geoJson = geoJson;
13797 exports.Layer = Layer;
13798 exports.LayerGroup = LayerGroup;
13799 exports.layerGroup = layerGroup;
13800 exports.FeatureGroup = FeatureGroup;
13801 exports.featureGroup = featureGroup;
13802 exports.ImageOverlay = ImageOverlay;
13803 exports.imageOverlay = imageOverlay;
13804 exports.VideoOverlay = VideoOverlay;
13805 exports.videoOverlay = videoOverlay;
13806 exports.DivOverlay = DivOverlay;
13807 exports.Popup = Popup;
13808 exports.popup = popup;
13809 exports.Tooltip = Tooltip;
13810 exports.tooltip = tooltip;
13811 exports.Icon = Icon;
13812 exports.icon = icon;
13813 exports.DivIcon = DivIcon;
13814 exports.divIcon = divIcon;
13815 exports.Marker = Marker;
13816 exports.marker = marker;
13817 exports.TileLayer = TileLayer;
13818 exports.tileLayer = tileLayer;
13819 exports.GridLayer = GridLayer;
13820 exports.gridLayer = gridLayer;
13821 exports.SVG = SVG;
13822 exports.svg = svg$1;
13823 exports.Renderer = Renderer;
13824 exports.Canvas = Canvas;
13825 exports.canvas = canvas$1;
13826 exports.Path = Path;
13827 exports.CircleMarker = CircleMarker;
13828 exports.circleMarker = circleMarker;
13829 exports.Circle = Circle;
13830 exports.circle = circle;
13831 exports.Polyline = Polyline;
13832 exports.polyline = polyline;
13833 exports.Polygon = Polygon;
13834 exports.polygon = polygon;
13835 exports.Rectangle = Rectangle;
13836 exports.rectangle = rectangle;
13837 exports.Map = Map;
13838 exports.map = createMap;
13839
13840 var oldL = window.L;
13841 exports.noConflict = function() {
13842         window.L = oldL;
13843         return this;
13844 }
13845
13846 // Always export us to window global (see #2364)
13847 window.L = exports;
13848
13849 })));
13850 //# sourceMappingURL=leaflet-src.js.map