]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/leaflet/leaflet.js
Fix rubocop warning
[rails.git] / vendor / assets / leaflet / leaflet.js
1 /*
2  * Leaflet 1.1.0, a JS library for interactive maps. http://leafletjs.com
3  * (c) 2010-2017 Vladimir Agafonkin, (c) 2010-2011 CloudMade
4  */
5 (function (global, factory) {
6         typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
7         typeof define === 'function' && define.amd ? define(['exports'], factory) :
8         (factory((global.L = global.L || {})));
9 }(this, (function (exports) { 'use strict';
10
11 var version = "1.1.0";
12
13 /*
14  * @namespace Util
15  *
16  * Various utility functions, used by Leaflet internally.
17  */
18
19 // @function extend(dest: Object, src?: Object): Object
20 // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
21 function extend(dest) {
22         var i, j, len, src;
23
24         for (j = 1, len = arguments.length; j < len; j++) {
25                 src = arguments[j];
26                 for (i in src) {
27                         dest[i] = src[i];
28                 }
29         }
30         return dest;
31 }
32
33 // @function create(proto: Object, properties?: Object): Object
34 // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
35 var create = Object.create || (function () {
36         function F() {}
37         return function (proto) {
38                 F.prototype = proto;
39                 return new F();
40         };
41 })();
42
43 // @function bind(fn: Function, …): Function
44 // Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
45 // Has a `L.bind()` shortcut.
46 function bind(fn, obj) {
47         var slice = Array.prototype.slice;
48
49         if (fn.bind) {
50                 return fn.bind.apply(fn, slice.call(arguments, 1));
51         }
52
53         var args = slice.call(arguments, 2);
54
55         return function () {
56                 return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
57         };
58 }
59
60 // @property lastId: Number
61 // Last unique ID used by [`stamp()`](#util-stamp)
62 var lastId = 0;
63
64 // @function stamp(obj: Object): Number
65 // Returns the unique ID of an object, assiging it one if it doesn't have it.
66 function stamp(obj) {
67         /*eslint-disable */
68         obj._leaflet_id = obj._leaflet_id || ++lastId;
69         return obj._leaflet_id;
70         /*eslint-enable */
71 }
72
73 // @function throttle(fn: Function, time: Number, context: Object): Function
74 // Returns a function which executes function `fn` with the given scope `context`
75 // (so that the `this` keyword refers to `context` inside `fn`'s code). The function
76 // `fn` will be called no more than one time per given amount of `time`. The arguments
77 // received by the bound function will be any arguments passed when binding the
78 // function, followed by any arguments passed when invoking the bound function.
79 // Has an `L.throttle` shortcut.
80 function throttle(fn, time, context) {
81         var lock, args, wrapperFn, later;
82
83         later = function () {
84                 // reset lock and call if queued
85                 lock = false;
86                 if (args) {
87                         wrapperFn.apply(context, args);
88                         args = false;
89                 }
90         };
91
92         wrapperFn = function () {
93                 if (lock) {
94                         // called too soon, queue to call later
95                         args = arguments;
96
97                 } else {
98                         // call and lock until later
99                         fn.apply(context, arguments);
100                         setTimeout(later, time);
101                         lock = true;
102                 }
103         };
104
105         return wrapperFn;
106 }
107
108 // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
109 // Returns the number `num` modulo `range` in such a way so it lies within
110 // `range[0]` and `range[1]`. The returned value will be always smaller than
111 // `range[1]` unless `includeMax` is set to `true`.
112 function wrapNum(x, range, includeMax) {
113         var max = range[1],
114             min = range[0],
115             d = max - min;
116         return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
117 }
118
119 // @function falseFn(): Function
120 // Returns a function which always returns `false`.
121 function falseFn() { return false; }
122
123 // @function formatNum(num: Number, digits?: Number): Number
124 // Returns the number `num` rounded to `digits` decimals, or to 5 decimals by default.
125 function formatNum(num, digits) {
126         var pow = Math.pow(10, digits || 5);
127         return Math.round(num * pow) / pow;
128 }
129
130 // @function trim(str: String): String
131 // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
132 function trim(str) {
133         return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
134 }
135
136 // @function splitWords(str: String): String[]
137 // Trims and splits the string on whitespace and returns the array of parts.
138 function splitWords(str) {
139         return trim(str).split(/\s+/);
140 }
141
142 // @function setOptions(obj: Object, options: Object): Object
143 // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
144 function setOptions(obj, options) {
145         if (!obj.hasOwnProperty('options')) {
146                 obj.options = obj.options ? create(obj.options) : {};
147         }
148         for (var i in options) {
149                 obj.options[i] = options[i];
150         }
151         return obj.options;
152 }
153
154 // @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
155 // Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
156 // translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
157 // be appended at the end. If `uppercase` is `true`, the parameter names will
158 // be uppercased (e.g. `'?A=foo&B=bar'`)
159 function getParamString(obj, existingUrl, uppercase) {
160         var params = [];
161         for (var i in obj) {
162                 params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
163         }
164         return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
165 }
166
167 var templateRe = /\{ *([\w_\-]+) *\}/g;
168
169 // @function template(str: String, data: Object): String
170 // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
171 // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
172 // `('Hello foo, bar')`. You can also specify functions instead of strings for
173 // data values — they will be evaluated passing `data` as an argument.
174 function template(str, data) {
175         return str.replace(templateRe, function (str, key) {
176                 var value = data[key];
177
178                 if (value === undefined) {
179                         throw new Error('No value provided for variable ' + str);
180
181                 } else if (typeof value === 'function') {
182                         value = value(data);
183                 }
184                 return value;
185         });
186 }
187
188 // @function isArray(obj): Boolean
189 // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
190 var isArray = Array.isArray || function (obj) {
191         return (Object.prototype.toString.call(obj) === '[object Array]');
192 };
193
194 // @function indexOf(array: Array, el: Object): Number
195 // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
196 function indexOf(array, el) {
197         for (var i = 0; i < array.length; i++) {
198                 if (array[i] === el) { return i; }
199         }
200         return -1;
201 }
202
203 // @property emptyImageUrl: String
204 // Data URI string containing a base64-encoded empty GIF image.
205 // Used as a hack to free memory from unused images on WebKit-powered
206 // mobile devices (by setting image `src` to this string).
207 var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
208
209 // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
210
211 function getPrefixed(name) {
212         return window['webkit' + name] || window['moz' + name] || window['ms' + name];
213 }
214
215 var lastTime = 0;
216
217 // fallback for IE 7-8
218 function timeoutDefer(fn) {
219         var time = +new Date(),
220             timeToCall = Math.max(0, 16 - (time - lastTime));
221
222         lastTime = time + timeToCall;
223         return window.setTimeout(fn, timeToCall);
224 }
225
226 var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer;
227 var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
228                 getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
229
230 // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
231 // Schedules `fn` to be executed when the browser repaints. `fn` is bound to
232 // `context` if given. When `immediate` is set, `fn` is called immediately if
233 // the browser doesn't have native support for
234 // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
235 // otherwise it's delayed. Returns a request ID that can be used to cancel the request.
236 function requestAnimFrame(fn, context, immediate) {
237         if (immediate && requestFn === timeoutDefer) {
238                 fn.call(context);
239         } else {
240                 return requestFn.call(window, bind(fn, context));
241         }
242 }
243
244 // @function cancelAnimFrame(id: Number): undefined
245 // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
246 function cancelAnimFrame(id) {
247         if (id) {
248                 cancelFn.call(window, id);
249         }
250 }
251
252
253 var Util = (Object.freeze || Object)({
254         extend: extend,
255         create: create,
256         bind: bind,
257         lastId: lastId,
258         stamp: stamp,
259         throttle: throttle,
260         wrapNum: wrapNum,
261         falseFn: falseFn,
262         formatNum: formatNum,
263         trim: trim,
264         splitWords: splitWords,
265         setOptions: setOptions,
266         getParamString: getParamString,
267         template: template,
268         isArray: isArray,
269         indexOf: indexOf,
270         emptyImageUrl: emptyImageUrl,
271         requestFn: requestFn,
272         cancelFn: cancelFn,
273         requestAnimFrame: requestAnimFrame,
274         cancelAnimFrame: cancelAnimFrame
275 });
276
277 // @class Class
278 // @aka L.Class
279
280 // @section
281 // @uninheritable
282
283 // Thanks to John Resig and Dean Edwards for inspiration!
284
285 function Class() {}
286
287 Class.extend = function (props) {
288
289         // @function extend(props: Object): Function
290         // [Extends the current class](#class-inheritance) given the properties to be included.
291         // Returns a Javascript function that is a class constructor (to be called with `new`).
292         var NewClass = function () {
293
294                 // call the constructor
295                 if (this.initialize) {
296                         this.initialize.apply(this, arguments);
297                 }
298
299                 // call all constructor hooks
300                 this.callInitHooks();
301         };
302
303         var parentProto = NewClass.__super__ = this.prototype;
304
305         var proto = create(parentProto);
306         proto.constructor = NewClass;
307
308         NewClass.prototype = proto;
309
310         // inherit parent's statics
311         for (var i in this) {
312                 if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') {
313                         NewClass[i] = this[i];
314                 }
315         }
316
317         // mix static properties into the class
318         if (props.statics) {
319                 extend(NewClass, props.statics);
320                 delete props.statics;
321         }
322
323         // mix includes into the prototype
324         if (props.includes) {
325                 checkDeprecatedMixinEvents(props.includes);
326                 extend.apply(null, [proto].concat(props.includes));
327                 delete props.includes;
328         }
329
330         // merge options
331         if (proto.options) {
332                 props.options = extend(create(proto.options), props.options);
333         }
334
335         // mix given properties into the prototype
336         extend(proto, props);
337
338         proto._initHooks = [];
339
340         // add method for calling all hooks
341         proto.callInitHooks = function () {
342
343                 if (this._initHooksCalled) { return; }
344
345                 if (parentProto.callInitHooks) {
346                         parentProto.callInitHooks.call(this);
347                 }
348
349                 this._initHooksCalled = true;
350
351                 for (var i = 0, len = proto._initHooks.length; i < len; i++) {
352                         proto._initHooks[i].call(this);
353                 }
354         };
355
356         return NewClass;
357 };
358
359
360 // @function include(properties: Object): this
361 // [Includes a mixin](#class-includes) into the current class.
362 Class.include = function (props) {
363         extend(this.prototype, props);
364         return this;
365 };
366
367 // @function mergeOptions(options: Object): this
368 // [Merges `options`](#class-options) into the defaults of the class.
369 Class.mergeOptions = function (options) {
370         extend(this.prototype.options, options);
371         return this;
372 };
373
374 // @function addInitHook(fn: Function): this
375 // Adds a [constructor hook](#class-constructor-hooks) to the class.
376 Class.addInitHook = function (fn) { // (Function) || (String, args...)
377         var args = Array.prototype.slice.call(arguments, 1);
378
379         var init = typeof fn === 'function' ? fn : function () {
380                 this[fn].apply(this, args);
381         };
382
383         this.prototype._initHooks = this.prototype._initHooks || [];
384         this.prototype._initHooks.push(init);
385         return this;
386 };
387
388 function checkDeprecatedMixinEvents(includes) {
389         if (!L || !L.Mixin) { return; }
390
391         includes = isArray(includes) ? includes : [includes];
392
393         for (var i = 0; i < includes.length; i++) {
394                 if (includes[i] === L.Mixin.Events) {
395                         console.warn('Deprecated include of L.Mixin.Events: ' +
396                                 'this property will be removed in future releases, ' +
397                                 'please inherit from L.Evented instead.', new Error().stack);
398                 }
399         }
400 }
401
402 /*
403  * @class Evented
404  * @aka L.Evented
405  * @inherits Class
406  *
407  * A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event).
408  *
409  * @example
410  *
411  * ```js
412  * map.on('click', function(e) {
413  *      alert(e.latlng);
414  * } );
415  * ```
416  *
417  * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
418  *
419  * ```js
420  * function onClick(e) { ... }
421  *
422  * map.on('click', onClick);
423  * map.off('click', onClick);
424  * ```
425  */
426
427 var Events = {
428         /* @method on(type: String, fn: Function, context?: Object): this
429          * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`).
430          *
431          * @alternative
432          * @method on(eventMap: Object): this
433          * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
434          */
435         on: function (types, fn, context) {
436
437                 // types can be a map of types/handlers
438                 if (typeof types === 'object') {
439                         for (var type in types) {
440                                 // we don't process space-separated events here for performance;
441                                 // it's a hot path since Layer uses the on(obj) syntax
442                                 this._on(type, types[type], fn);
443                         }
444
445                 } else {
446                         // types can be a string of space-separated words
447                         types = splitWords(types);
448
449                         for (var i = 0, len = types.length; i < len; i++) {
450                                 this._on(types[i], fn, context);
451                         }
452                 }
453
454                 return this;
455         },
456
457         /* @method off(type: String, fn?: Function, context?: Object): this
458          * Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener.
459          *
460          * @alternative
461          * @method off(eventMap: Object): this
462          * Removes a set of type/listener pairs.
463          *
464          * @alternative
465          * @method off: this
466          * Removes all listeners to all events on the object.
467          */
468         off: function (types, fn, context) {
469
470                 if (!types) {
471                         // clear all listeners if called without arguments
472                         delete this._events;
473
474                 } else if (typeof types === 'object') {
475                         for (var type in types) {
476                                 this._off(type, types[type], fn);
477                         }
478
479                 } else {
480                         types = splitWords(types);
481
482                         for (var i = 0, len = types.length; i < len; i++) {
483                                 this._off(types[i], fn, context);
484                         }
485                 }
486
487                 return this;
488         },
489
490         // attach listener (without syntactic sugar now)
491         _on: function (type, fn, context) {
492                 this._events = this._events || {};
493
494                 /* get/init listeners for type */
495                 var typeListeners = this._events[type];
496                 if (!typeListeners) {
497                         typeListeners = [];
498                         this._events[type] = typeListeners;
499                 }
500
501                 if (context === this) {
502                         // Less memory footprint.
503                         context = undefined;
504                 }
505                 var newListener = {fn: fn, ctx: context},
506                     listeners = typeListeners;
507
508                 // check if fn already there
509                 for (var i = 0, len = listeners.length; i < len; i++) {
510                         if (listeners[i].fn === fn && listeners[i].ctx === context) {
511                                 return;
512                         }
513                 }
514
515                 listeners.push(newListener);
516         },
517
518         _off: function (type, fn, context) {
519                 var listeners,
520                     i,
521                     len;
522
523                 if (!this._events) { return; }
524
525                 listeners = this._events[type];
526
527                 if (!listeners) {
528                         return;
529                 }
530
531                 if (!fn) {
532                         // Set all removed listeners to noop so they are not called if remove happens in fire
533                         for (i = 0, len = listeners.length; i < len; i++) {
534                                 listeners[i].fn = falseFn;
535                         }
536                         // clear all listeners for a type if function isn't specified
537                         delete this._events[type];
538                         return;
539                 }
540
541                 if (context === this) {
542                         context = undefined;
543                 }
544
545                 if (listeners) {
546
547                         // find fn and remove it
548                         for (i = 0, len = listeners.length; i < len; i++) {
549                                 var l = listeners[i];
550                                 if (l.ctx !== context) { continue; }
551                                 if (l.fn === fn) {
552
553                                         // set the removed listener to noop so that's not called if remove happens in fire
554                                         l.fn = falseFn;
555
556                                         if (this._firingCount) {
557                                                 /* copy array in case events are being fired */
558                                                 this._events[type] = listeners = listeners.slice();
559                                         }
560                                         listeners.splice(i, 1);
561
562                                         return;
563                                 }
564                         }
565                 }
566         },
567
568         // @method fire(type: String, data?: Object, propagate?: Boolean): this
569         // Fires an event of the specified type. You can optionally provide an data
570         // object — the first argument of the listener function will contain its
571         // properties. The event can optionally be propagated to event parents.
572         fire: function (type, data, propagate) {
573                 if (!this.listens(type, propagate)) { return this; }
574
575                 var event = extend({}, data, {type: type, target: this});
576
577                 if (this._events) {
578                         var listeners = this._events[type];
579
580                         if (listeners) {
581                                 this._firingCount = (this._firingCount + 1) || 1;
582                                 for (var i = 0, len = listeners.length; i < len; i++) {
583                                         var l = listeners[i];
584                                         l.fn.call(l.ctx || this, event);
585                                 }
586
587                                 this._firingCount--;
588                         }
589                 }
590
591                 if (propagate) {
592                         // propagate the event to parents (set with addEventParent)
593                         this._propagateEvent(event);
594                 }
595
596                 return this;
597         },
598
599         // @method listens(type: String): Boolean
600         // Returns `true` if a particular event type has any listeners attached to it.
601         listens: function (type, propagate) {
602                 var listeners = this._events && this._events[type];
603                 if (listeners && listeners.length) { return true; }
604
605                 if (propagate) {
606                         // also check parents for listeners if event propagates
607                         for (var id in this._eventParents) {
608                                 if (this._eventParents[id].listens(type, propagate)) { return true; }
609                         }
610                 }
611                 return false;
612         },
613
614         // @method once(…): this
615         // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
616         once: function (types, fn, context) {
617
618                 if (typeof types === 'object') {
619                         for (var type in types) {
620                                 this.once(type, types[type], fn);
621                         }
622                         return this;
623                 }
624
625                 var handler = bind(function () {
626                         this
627                             .off(types, fn, context)
628                             .off(types, handler, context);
629                 }, this);
630
631                 // add a listener that's executed once and removed after that
632                 return this
633                     .on(types, fn, context)
634                     .on(types, handler, context);
635         },
636
637         // @method addEventParent(obj: Evented): this
638         // Adds an event parent - an `Evented` that will receive propagated events
639         addEventParent: function (obj) {
640                 this._eventParents = this._eventParents || {};
641                 this._eventParents[stamp(obj)] = obj;
642                 return this;
643         },
644
645         // @method removeEventParent(obj: Evented): this
646         // Removes an event parent, so it will stop receiving propagated events
647         removeEventParent: function (obj) {
648                 if (this._eventParents) {
649                         delete this._eventParents[stamp(obj)];
650                 }
651                 return this;
652         },
653
654         _propagateEvent: function (e) {
655                 for (var id in this._eventParents) {
656                         this._eventParents[id].fire(e.type, extend({layer: e.target}, e), true);
657                 }
658         }
659 };
660
661 // aliases; we should ditch those eventually
662
663 // @method addEventListener(…): this
664 // Alias to [`on(…)`](#evented-on)
665 Events.addEventListener = Events.on;
666
667 // @method removeEventListener(…): this
668 // Alias to [`off(…)`](#evented-off)
669
670 // @method clearAllEventListeners(…): this
671 // Alias to [`off()`](#evented-off)
672 Events.removeEventListener = Events.clearAllEventListeners = Events.off;
673
674 // @method addOneTimeEventListener(…): this
675 // Alias to [`once(…)`](#evented-once)
676 Events.addOneTimeEventListener = Events.once;
677
678 // @method fireEvent(…): this
679 // Alias to [`fire(…)`](#evented-fire)
680 Events.fireEvent = Events.fire;
681
682 // @method hasEventListeners(…): Boolean
683 // Alias to [`listens(…)`](#evented-listens)
684 Events.hasEventListeners = Events.listens;
685
686 var Evented = Class.extend(Events);
687
688 /*
689  * @class Point
690  * @aka L.Point
691  *
692  * Represents a point with `x` and `y` coordinates in pixels.
693  *
694  * @example
695  *
696  * ```js
697  * var point = L.point(200, 300);
698  * ```
699  *
700  * All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent:
701  *
702  * ```js
703  * map.panBy([200, 300]);
704  * map.panBy(L.point(200, 300));
705  * ```
706  */
707
708 function Point(x, y, round) {
709         // @property x: Number; The `x` coordinate of the point
710         this.x = (round ? Math.round(x) : x);
711         // @property y: Number; The `y` coordinate of the point
712         this.y = (round ? Math.round(y) : y);
713 }
714
715 Point.prototype = {
716
717         // @method clone(): Point
718         // Returns a copy of the current point.
719         clone: function () {
720                 return new Point(this.x, this.y);
721         },
722
723         // @method add(otherPoint: Point): Point
724         // Returns the result of addition of the current and the given points.
725         add: function (point) {
726                 // non-destructive, returns a new point
727                 return this.clone()._add(toPoint(point));
728         },
729
730         _add: function (point) {
731                 // destructive, used directly for performance in situations where it's safe to modify existing point
732                 this.x += point.x;
733                 this.y += point.y;
734                 return this;
735         },
736
737         // @method subtract(otherPoint: Point): Point
738         // Returns the result of subtraction of the given point from the current.
739         subtract: function (point) {
740                 return this.clone()._subtract(toPoint(point));
741         },
742
743         _subtract: function (point) {
744                 this.x -= point.x;
745                 this.y -= point.y;
746                 return this;
747         },
748
749         // @method divideBy(num: Number): Point
750         // Returns the result of division of the current point by the given number.
751         divideBy: function (num) {
752                 return this.clone()._divideBy(num);
753         },
754
755         _divideBy: function (num) {
756                 this.x /= num;
757                 this.y /= num;
758                 return this;
759         },
760
761         // @method multiplyBy(num: Number): Point
762         // Returns the result of multiplication of the current point by the given number.
763         multiplyBy: function (num) {
764                 return this.clone()._multiplyBy(num);
765         },
766
767         _multiplyBy: function (num) {
768                 this.x *= num;
769                 this.y *= num;
770                 return this;
771         },
772
773         // @method scaleBy(scale: Point): Point
774         // Multiply each coordinate of the current point by each coordinate of
775         // `scale`. In linear algebra terms, multiply the point by the
776         // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
777         // defined by `scale`.
778         scaleBy: function (point) {
779                 return new Point(this.x * point.x, this.y * point.y);
780         },
781
782         // @method unscaleBy(scale: Point): Point
783         // Inverse of `scaleBy`. Divide each coordinate of the current point by
784         // each coordinate of `scale`.
785         unscaleBy: function (point) {
786                 return new Point(this.x / point.x, this.y / point.y);
787         },
788
789         // @method round(): Point
790         // Returns a copy of the current point with rounded coordinates.
791         round: function () {
792                 return this.clone()._round();
793         },
794
795         _round: function () {
796                 this.x = Math.round(this.x);
797                 this.y = Math.round(this.y);
798                 return this;
799         },
800
801         // @method floor(): Point
802         // Returns a copy of the current point with floored coordinates (rounded down).
803         floor: function () {
804                 return this.clone()._floor();
805         },
806
807         _floor: function () {
808                 this.x = Math.floor(this.x);
809                 this.y = Math.floor(this.y);
810                 return this;
811         },
812
813         // @method ceil(): Point
814         // Returns a copy of the current point with ceiled coordinates (rounded up).
815         ceil: function () {
816                 return this.clone()._ceil();
817         },
818
819         _ceil: function () {
820                 this.x = Math.ceil(this.x);
821                 this.y = Math.ceil(this.y);
822                 return this;
823         },
824
825         // @method distanceTo(otherPoint: Point): Number
826         // Returns the cartesian distance between the current and the given points.
827         distanceTo: function (point) {
828                 point = toPoint(point);
829
830                 var x = point.x - this.x,
831                     y = point.y - this.y;
832
833                 return Math.sqrt(x * x + y * y);
834         },
835
836         // @method equals(otherPoint: Point): Boolean
837         // Returns `true` if the given point has the same coordinates.
838         equals: function (point) {
839                 point = toPoint(point);
840
841                 return point.x === this.x &&
842                        point.y === this.y;
843         },
844
845         // @method contains(otherPoint: Point): Boolean
846         // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
847         contains: function (point) {
848                 point = toPoint(point);
849
850                 return Math.abs(point.x) <= Math.abs(this.x) &&
851                        Math.abs(point.y) <= Math.abs(this.y);
852         },
853
854         // @method toString(): String
855         // Returns a string representation of the point for debugging purposes.
856         toString: function () {
857                 return 'Point(' +
858                         formatNum(this.x) + ', ' +
859                         formatNum(this.y) + ')';
860         }
861 };
862
863 // @factory L.point(x: Number, y: Number, round?: Boolean)
864 // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
865
866 // @alternative
867 // @factory L.point(coords: Number[])
868 // Expects an array of the form `[x, y]` instead.
869
870 // @alternative
871 // @factory L.point(coords: Object)
872 // Expects a plain object of the form `{x: Number, y: Number}` instead.
873 function toPoint(x, y, round) {
874         if (x instanceof Point) {
875                 return x;
876         }
877         if (isArray(x)) {
878                 return new Point(x[0], x[1]);
879         }
880         if (x === undefined || x === null) {
881                 return x;
882         }
883         if (typeof x === 'object' && 'x' in x && 'y' in x) {
884                 return new Point(x.x, x.y);
885         }
886         return new Point(x, y, round);
887 }
888
889 /*
890  * @class Bounds
891  * @aka L.Bounds
892  *
893  * Represents a rectangular area in pixel coordinates.
894  *
895  * @example
896  *
897  * ```js
898  * var p1 = L.point(10, 10),
899  * p2 = L.point(40, 60),
900  * bounds = L.bounds(p1, p2);
901  * ```
902  *
903  * All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
904  *
905  * ```js
906  * otherBounds.intersects([[10, 10], [40, 60]]);
907  * ```
908  */
909
910 function Bounds(a, b) {
911         if (!a) { return; }
912
913         var points = b ? [a, b] : a;
914
915         for (var i = 0, len = points.length; i < len; i++) {
916                 this.extend(points[i]);
917         }
918 }
919
920 Bounds.prototype = {
921         // @method extend(point: Point): this
922         // Extends the bounds to contain the given point.
923         extend: function (point) { // (Point)
924                 point = toPoint(point);
925
926                 // @property min: Point
927                 // The top left corner of the rectangle.
928                 // @property max: Point
929                 // The bottom right corner of the rectangle.
930                 if (!this.min && !this.max) {
931                         this.min = point.clone();
932                         this.max = point.clone();
933                 } else {
934                         this.min.x = Math.min(point.x, this.min.x);
935                         this.max.x = Math.max(point.x, this.max.x);
936                         this.min.y = Math.min(point.y, this.min.y);
937                         this.max.y = Math.max(point.y, this.max.y);
938                 }
939                 return this;
940         },
941
942         // @method getCenter(round?: Boolean): Point
943         // Returns the center point of the bounds.
944         getCenter: function (round) {
945                 return new Point(
946                         (this.min.x + this.max.x) / 2,
947                         (this.min.y + this.max.y) / 2, round);
948         },
949
950         // @method getBottomLeft(): Point
951         // Returns the bottom-left point of the bounds.
952         getBottomLeft: function () {
953                 return new Point(this.min.x, this.max.y);
954         },
955
956         // @method getTopRight(): Point
957         // Returns the top-right point of the bounds.
958         getTopRight: function () { // -> Point
959                 return new Point(this.max.x, this.min.y);
960         },
961
962         // @method getTopLeft(): Point
963         // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)).
964         getTopLeft: function () {
965                 return this.min; // left, top
966         },
967
968         // @method getBottomRight(): Point
969         // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)).
970         getBottomRight: function () {
971                 return this.max; // right, bottom
972         },
973
974         // @method getSize(): Point
975         // Returns the size of the given bounds
976         getSize: function () {
977                 return this.max.subtract(this.min);
978         },
979
980         // @method contains(otherBounds: Bounds): Boolean
981         // Returns `true` if the rectangle contains the given one.
982         // @alternative
983         // @method contains(point: Point): Boolean
984         // Returns `true` if the rectangle contains the given point.
985         contains: function (obj) {
986                 var min, max;
987
988                 if (typeof obj[0] === 'number' || obj instanceof Point) {
989                         obj = toPoint(obj);
990                 } else {
991                         obj = toBounds(obj);
992                 }
993
994                 if (obj instanceof Bounds) {
995                         min = obj.min;
996                         max = obj.max;
997                 } else {
998                         min = max = obj;
999                 }
1000
1001                 return (min.x >= this.min.x) &&
1002                        (max.x <= this.max.x) &&
1003                        (min.y >= this.min.y) &&
1004                        (max.y <= this.max.y);
1005         },
1006
1007         // @method intersects(otherBounds: Bounds): Boolean
1008         // Returns `true` if the rectangle intersects the given bounds. Two bounds
1009         // intersect if they have at least one point in common.
1010         intersects: function (bounds) { // (Bounds) -> Boolean
1011                 bounds = toBounds(bounds);
1012
1013                 var min = this.min,
1014                     max = this.max,
1015                     min2 = bounds.min,
1016                     max2 = bounds.max,
1017                     xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
1018                     yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
1019
1020                 return xIntersects && yIntersects;
1021         },
1022
1023         // @method overlaps(otherBounds: Bounds): Boolean
1024         // Returns `true` if the rectangle overlaps the given bounds. Two bounds
1025         // overlap if their intersection is an area.
1026         overlaps: function (bounds) { // (Bounds) -> Boolean
1027                 bounds = toBounds(bounds);
1028
1029                 var min = this.min,
1030                     max = this.max,
1031                     min2 = bounds.min,
1032                     max2 = bounds.max,
1033                     xOverlaps = (max2.x > min.x) && (min2.x < max.x),
1034                     yOverlaps = (max2.y > min.y) && (min2.y < max.y);
1035
1036                 return xOverlaps && yOverlaps;
1037         },
1038
1039         isValid: function () {
1040                 return !!(this.min && this.max);
1041         }
1042 };
1043
1044
1045 // @factory L.bounds(corner1: Point, corner2: Point)
1046 // Creates a Bounds object from two corners coordinate pairs.
1047 // @alternative
1048 // @factory L.bounds(points: Point[])
1049 // Creates a Bounds object from the given array of points.
1050 function toBounds(a, b) {
1051         if (!a || a instanceof Bounds) {
1052                 return a;
1053         }
1054         return new Bounds(a, b);
1055 }
1056
1057 /*
1058  * @class LatLngBounds
1059  * @aka L.LatLngBounds
1060  *
1061  * Represents a rectangular geographical area on a map.
1062  *
1063  * @example
1064  *
1065  * ```js
1066  * var corner1 = L.latLng(40.712, -74.227),
1067  * corner2 = L.latLng(40.774, -74.125),
1068  * bounds = L.latLngBounds(corner1, corner2);
1069  * ```
1070  *
1071  * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
1072  *
1073  * ```js
1074  * map.fitBounds([
1075  *      [40.712, -74.227],
1076  *      [40.774, -74.125]
1077  * ]);
1078  * ```
1079  *
1080  * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range.
1081  */
1082
1083 function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
1084         if (!corner1) { return; }
1085
1086         var latlngs = corner2 ? [corner1, corner2] : corner1;
1087
1088         for (var i = 0, len = latlngs.length; i < len; i++) {
1089                 this.extend(latlngs[i]);
1090         }
1091 }
1092
1093 LatLngBounds.prototype = {
1094
1095         // @method extend(latlng: LatLng): this
1096         // Extend the bounds to contain the given point
1097
1098         // @alternative
1099         // @method extend(otherBounds: LatLngBounds): this
1100         // Extend the bounds to contain the given bounds
1101         extend: function (obj) {
1102                 var sw = this._southWest,
1103                     ne = this._northEast,
1104                     sw2, ne2;
1105
1106                 if (obj instanceof LatLng) {
1107                         sw2 = obj;
1108                         ne2 = obj;
1109
1110                 } else if (obj instanceof LatLngBounds) {
1111                         sw2 = obj._southWest;
1112                         ne2 = obj._northEast;
1113
1114                         if (!sw2 || !ne2) { return this; }
1115
1116                 } else {
1117                         return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this;
1118                 }
1119
1120                 if (!sw && !ne) {
1121                         this._southWest = new LatLng(sw2.lat, sw2.lng);
1122                         this._northEast = new LatLng(ne2.lat, ne2.lng);
1123                 } else {
1124                         sw.lat = Math.min(sw2.lat, sw.lat);
1125                         sw.lng = Math.min(sw2.lng, sw.lng);
1126                         ne.lat = Math.max(ne2.lat, ne.lat);
1127                         ne.lng = Math.max(ne2.lng, ne.lng);
1128                 }
1129
1130                 return this;
1131         },
1132
1133         // @method pad(bufferRatio: Number): LatLngBounds
1134         // Returns bigger bounds created by extending the current bounds by a given percentage in each direction.
1135         pad: function (bufferRatio) {
1136                 var sw = this._southWest,
1137                     ne = this._northEast,
1138                     heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
1139                     widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
1140
1141                 return new LatLngBounds(
1142                         new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
1143                         new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
1144         },
1145
1146         // @method getCenter(): LatLng
1147         // Returns the center point of the bounds.
1148         getCenter: function () {
1149                 return new LatLng(
1150                         (this._southWest.lat + this._northEast.lat) / 2,
1151                         (this._southWest.lng + this._northEast.lng) / 2);
1152         },
1153
1154         // @method getSouthWest(): LatLng
1155         // Returns the south-west point of the bounds.
1156         getSouthWest: function () {
1157                 return this._southWest;
1158         },
1159
1160         // @method getNorthEast(): LatLng
1161         // Returns the north-east point of the bounds.
1162         getNorthEast: function () {
1163                 return this._northEast;
1164         },
1165
1166         // @method getNorthWest(): LatLng
1167         // Returns the north-west point of the bounds.
1168         getNorthWest: function () {
1169                 return new LatLng(this.getNorth(), this.getWest());
1170         },
1171
1172         // @method getSouthEast(): LatLng
1173         // Returns the south-east point of the bounds.
1174         getSouthEast: function () {
1175                 return new LatLng(this.getSouth(), this.getEast());
1176         },
1177
1178         // @method getWest(): Number
1179         // Returns the west longitude of the bounds
1180         getWest: function () {
1181                 return this._southWest.lng;
1182         },
1183
1184         // @method getSouth(): Number
1185         // Returns the south latitude of the bounds
1186         getSouth: function () {
1187                 return this._southWest.lat;
1188         },
1189
1190         // @method getEast(): Number
1191         // Returns the east longitude of the bounds
1192         getEast: function () {
1193                 return this._northEast.lng;
1194         },
1195
1196         // @method getNorth(): Number
1197         // Returns the north latitude of the bounds
1198         getNorth: function () {
1199                 return this._northEast.lat;
1200         },
1201
1202         // @method contains(otherBounds: LatLngBounds): Boolean
1203         // Returns `true` if the rectangle contains the given one.
1204
1205         // @alternative
1206         // @method contains (latlng: LatLng): Boolean
1207         // Returns `true` if the rectangle contains the given point.
1208         contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
1209                 if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) {
1210                         obj = toLatLng(obj);
1211                 } else {
1212                         obj = toLatLngBounds(obj);
1213                 }
1214
1215                 var sw = this._southWest,
1216                     ne = this._northEast,
1217                     sw2, ne2;
1218
1219                 if (obj instanceof LatLngBounds) {
1220                         sw2 = obj.getSouthWest();
1221                         ne2 = obj.getNorthEast();
1222                 } else {
1223                         sw2 = ne2 = obj;
1224                 }
1225
1226                 return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
1227                        (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
1228         },
1229
1230         // @method intersects(otherBounds: LatLngBounds): Boolean
1231         // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
1232         intersects: function (bounds) {
1233                 bounds = toLatLngBounds(bounds);
1234
1235                 var sw = this._southWest,
1236                     ne = this._northEast,
1237                     sw2 = bounds.getSouthWest(),
1238                     ne2 = bounds.getNorthEast(),
1239
1240                     latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
1241                     lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
1242
1243                 return latIntersects && lngIntersects;
1244         },
1245
1246         // @method overlaps(otherBounds: Bounds): Boolean
1247         // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
1248         overlaps: function (bounds) {
1249                 bounds = toLatLngBounds(bounds);
1250
1251                 var sw = this._southWest,
1252                     ne = this._northEast,
1253                     sw2 = bounds.getSouthWest(),
1254                     ne2 = bounds.getNorthEast(),
1255
1256                     latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
1257                     lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
1258
1259                 return latOverlaps && lngOverlaps;
1260         },
1261
1262         // @method toBBoxString(): String
1263         // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data.
1264         toBBoxString: function () {
1265                 return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
1266         },
1267
1268         // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean
1269         // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overriden by setting `maxMargin` to a small number.
1270         equals: function (bounds, maxMargin) {
1271                 if (!bounds) { return false; }
1272
1273                 bounds = toLatLngBounds(bounds);
1274
1275                 return this._southWest.equals(bounds.getSouthWest(), maxMargin) &&
1276                        this._northEast.equals(bounds.getNorthEast(), maxMargin);
1277         },
1278
1279         // @method isValid(): Boolean
1280         // Returns `true` if the bounds are properly initialized.
1281         isValid: function () {
1282                 return !!(this._southWest && this._northEast);
1283         }
1284 };
1285
1286 // TODO International date line?
1287
1288 // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
1289 // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
1290
1291 // @alternative
1292 // @factory L.latLngBounds(latlngs: LatLng[])
1293 // Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds).
1294 function toLatLngBounds(a, b) {
1295         if (a instanceof LatLngBounds) {
1296                 return a;
1297         }
1298         return new LatLngBounds(a, b);
1299 }
1300
1301 /* @class LatLng
1302  * @aka L.LatLng
1303  *
1304  * Represents a geographical point with a certain latitude and longitude.
1305  *
1306  * @example
1307  *
1308  * ```
1309  * var latlng = L.latLng(50.5, 30.5);
1310  * ```
1311  *
1312  * All Leaflet methods that accept LatLng objects also accept them in a simple Array form and simple object form (unless noted otherwise), so these lines are equivalent:
1313  *
1314  * ```
1315  * map.panTo([50, 30]);
1316  * map.panTo({lon: 30, lat: 50});
1317  * map.panTo({lat: 50, lng: 30});
1318  * map.panTo(L.latLng(50, 30));
1319  * ```
1320  */
1321
1322 function LatLng(lat, lng, alt) {
1323         if (isNaN(lat) || isNaN(lng)) {
1324                 throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
1325         }
1326
1327         // @property lat: Number
1328         // Latitude in degrees
1329         this.lat = +lat;
1330
1331         // @property lng: Number
1332         // Longitude in degrees
1333         this.lng = +lng;
1334
1335         // @property alt: Number
1336         // Altitude in meters (optional)
1337         if (alt !== undefined) {
1338                 this.alt = +alt;
1339         }
1340 }
1341
1342 LatLng.prototype = {
1343         // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
1344         // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overriden by setting `maxMargin` to a small number.
1345         equals: function (obj, maxMargin) {
1346                 if (!obj) { return false; }
1347
1348                 obj = toLatLng(obj);
1349
1350                 var margin = Math.max(
1351                         Math.abs(this.lat - obj.lat),
1352                         Math.abs(this.lng - obj.lng));
1353
1354                 return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
1355         },
1356
1357         // @method toString(): String
1358         // Returns a string representation of the point (for debugging purposes).
1359         toString: function (precision) {
1360                 return 'LatLng(' +
1361                         formatNum(this.lat, precision) + ', ' +
1362                         formatNum(this.lng, precision) + ')';
1363         },
1364
1365         // @method distanceTo(otherLatLng: LatLng): Number
1366         // Returns the distance (in meters) to the given `LatLng` calculated using the [Haversine formula](http://en.wikipedia.org/wiki/Haversine_formula).
1367         distanceTo: function (other) {
1368                 return Earth.distance(this, toLatLng(other));
1369         },
1370
1371         // @method wrap(): LatLng
1372         // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
1373         wrap: function () {
1374                 return Earth.wrapLatLng(this);
1375         },
1376
1377         // @method toBounds(sizeInMeters: Number): LatLngBounds
1378         // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
1379         toBounds: function (sizeInMeters) {
1380                 var latAccuracy = 180 * sizeInMeters / 40075017,
1381                     lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
1382
1383                 return toLatLngBounds(
1384                         [this.lat - latAccuracy, this.lng - lngAccuracy],
1385                         [this.lat + latAccuracy, this.lng + lngAccuracy]);
1386         },
1387
1388         clone: function () {
1389                 return new LatLng(this.lat, this.lng, this.alt);
1390         }
1391 };
1392
1393
1394
1395 // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
1396 // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
1397
1398 // @alternative
1399 // @factory L.latLng(coords: Array): LatLng
1400 // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
1401
1402 // @alternative
1403 // @factory L.latLng(coords: Object): LatLng
1404 // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
1405
1406 function toLatLng(a, b, c) {
1407         if (a instanceof LatLng) {
1408                 return a;
1409         }
1410         if (isArray(a) && typeof a[0] !== 'object') {
1411                 if (a.length === 3) {
1412                         return new LatLng(a[0], a[1], a[2]);
1413                 }
1414                 if (a.length === 2) {
1415                         return new LatLng(a[0], a[1]);
1416                 }
1417                 return null;
1418         }
1419         if (a === undefined || a === null) {
1420                 return a;
1421         }
1422         if (typeof a === 'object' && 'lat' in a) {
1423                 return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
1424         }
1425         if (b === undefined) {
1426                 return null;
1427         }
1428         return new LatLng(a, b, c);
1429 }
1430
1431 /*
1432  * @namespace CRS
1433  * @crs L.CRS.Base
1434  * Object that defines coordinate reference systems for projecting
1435  * geographical points into pixel (screen) coordinates and back (and to
1436  * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
1437  * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
1438  *
1439  * Leaflet defines the most usual CRSs by default. If you want to use a
1440  * CRS not defined by default, take a look at the
1441  * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
1442  */
1443
1444 var CRS = {
1445         // @method latLngToPoint(latlng: LatLng, zoom: Number): Point
1446         // Projects geographical coordinates into pixel coordinates for a given zoom.
1447         latLngToPoint: function (latlng, zoom) {
1448                 var projectedPoint = this.projection.project(latlng),
1449                     scale = this.scale(zoom);
1450
1451                 return this.transformation._transform(projectedPoint, scale);
1452         },
1453
1454         // @method pointToLatLng(point: Point, zoom: Number): LatLng
1455         // The inverse of `latLngToPoint`. Projects pixel coordinates on a given
1456         // zoom into geographical coordinates.
1457         pointToLatLng: function (point, zoom) {
1458                 var scale = this.scale(zoom),
1459                     untransformedPoint = this.transformation.untransform(point, scale);
1460
1461                 return this.projection.unproject(untransformedPoint);
1462         },
1463
1464         // @method project(latlng: LatLng): Point
1465         // Projects geographical coordinates into coordinates in units accepted for
1466         // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
1467         project: function (latlng) {
1468                 return this.projection.project(latlng);
1469         },
1470
1471         // @method unproject(point: Point): LatLng
1472         // Given a projected coordinate returns the corresponding LatLng.
1473         // The inverse of `project`.
1474         unproject: function (point) {
1475                 return this.projection.unproject(point);
1476         },
1477
1478         // @method scale(zoom: Number): Number
1479         // Returns the scale used when transforming projected coordinates into
1480         // pixel coordinates for a particular zoom. For example, it returns
1481         // `256 * 2^zoom` for Mercator-based CRS.
1482         scale: function (zoom) {
1483                 return 256 * Math.pow(2, zoom);
1484         },
1485
1486         // @method zoom(scale: Number): Number
1487         // Inverse of `scale()`, returns the zoom level corresponding to a scale
1488         // factor of `scale`.
1489         zoom: function (scale) {
1490                 return Math.log(scale / 256) / Math.LN2;
1491         },
1492
1493         // @method getProjectedBounds(zoom: Number): Bounds
1494         // Returns the projection's bounds scaled and transformed for the provided `zoom`.
1495         getProjectedBounds: function (zoom) {
1496                 if (this.infinite) { return null; }
1497
1498                 var b = this.projection.bounds,
1499                     s = this.scale(zoom),
1500                     min = this.transformation.transform(b.min, s),
1501                     max = this.transformation.transform(b.max, s);
1502
1503                 return new Bounds(min, max);
1504         },
1505
1506         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
1507         // Returns the distance between two geographical coordinates.
1508
1509         // @property code: String
1510         // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
1511         //
1512         // @property wrapLng: Number[]
1513         // An array of two numbers defining whether the longitude (horizontal) coordinate
1514         // axis wraps around a given range and how. Defaults to `[-180, 180]` in most
1515         // geographical CRSs. If `undefined`, the longitude axis does not wrap around.
1516         //
1517         // @property wrapLat: Number[]
1518         // Like `wrapLng`, but for the latitude (vertical) axis.
1519
1520         // wrapLng: [min, max],
1521         // wrapLat: [min, max],
1522
1523         // @property infinite: Boolean
1524         // If true, the coordinate space will be unbounded (infinite in both axes)
1525         infinite: false,
1526
1527         // @method wrapLatLng(latlng: LatLng): LatLng
1528         // Returns a `LatLng` where lat and lng has been wrapped according to the
1529         // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
1530         wrapLatLng: function (latlng) {
1531                 var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
1532                     lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
1533                     alt = latlng.alt;
1534
1535                 return new LatLng(lat, lng, alt);
1536         },
1537
1538         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
1539         // Returns a `LatLngBounds` with the same size as the given one, ensuring
1540         // that its center is within the CRS's bounds.
1541         // Only accepts actual `L.LatLngBounds` instances, not arrays.
1542         wrapLatLngBounds: function (bounds) {
1543                 var center = bounds.getCenter(),
1544                     newCenter = this.wrapLatLng(center),
1545                     latShift = center.lat - newCenter.lat,
1546                     lngShift = center.lng - newCenter.lng;
1547
1548                 if (latShift === 0 && lngShift === 0) {
1549                         return bounds;
1550                 }
1551
1552                 var sw = bounds.getSouthWest(),
1553                     ne = bounds.getNorthEast(),
1554                     newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift),
1555                     newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift);
1556
1557                 return new LatLngBounds(newSw, newNe);
1558         }
1559 };
1560
1561 /*
1562  * @namespace CRS
1563  * @crs L.CRS.Earth
1564  *
1565  * Serves as the base for CRS that are global such that they cover the earth.
1566  * Can only be used as the base for other CRS and cannot be used directly,
1567  * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
1568  * meters.
1569  */
1570
1571 var Earth = extend({}, CRS, {
1572         wrapLng: [-180, 180],
1573
1574         // Mean Earth Radius, as recommended for use by
1575         // the International Union of Geodesy and Geophysics,
1576         // see http://rosettacode.org/wiki/Haversine_formula
1577         R: 6371000,
1578
1579         // distance between two geographical points using spherical law of cosines approximation
1580         distance: function (latlng1, latlng2) {
1581                 var rad = Math.PI / 180,
1582                     lat1 = latlng1.lat * rad,
1583                     lat2 = latlng2.lat * rad,
1584                     a = Math.sin(lat1) * Math.sin(lat2) +
1585                         Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlng2.lng - latlng1.lng) * rad);
1586
1587                 return this.R * Math.acos(Math.min(a, 1));
1588         }
1589 });
1590
1591 /*
1592  * @namespace Projection
1593  * @projection L.Projection.SphericalMercator
1594  *
1595  * Spherical Mercator projection — the most common projection for online maps,
1596  * used by almost all free and commercial tile providers. Assumes that Earth is
1597  * a sphere. Used by the `EPSG:3857` CRS.
1598  */
1599
1600 var SphericalMercator = {
1601
1602         R: 6378137,
1603         MAX_LATITUDE: 85.0511287798,
1604
1605         project: function (latlng) {
1606                 var d = Math.PI / 180,
1607                     max = this.MAX_LATITUDE,
1608                     lat = Math.max(Math.min(max, latlng.lat), -max),
1609                     sin = Math.sin(lat * d);
1610
1611                 return new Point(
1612                                 this.R * latlng.lng * d,
1613                                 this.R * Math.log((1 + sin) / (1 - sin)) / 2);
1614         },
1615
1616         unproject: function (point) {
1617                 var d = 180 / Math.PI;
1618
1619                 return new LatLng(
1620                         (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
1621                         point.x * d / this.R);
1622         },
1623
1624         bounds: (function () {
1625                 var d = 6378137 * Math.PI;
1626                 return new Bounds([-d, -d], [d, d]);
1627         })()
1628 };
1629
1630 /*
1631  * @class Transformation
1632  * @aka L.Transformation
1633  *
1634  * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
1635  * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
1636  * the reverse. Used by Leaflet in its projections code.
1637  *
1638  * @example
1639  *
1640  * ```js
1641  * var transformation = L.transformation(2, 5, -1, 10),
1642  *      p = L.point(1, 2),
1643  *      p2 = transformation.transform(p), //  L.point(7, 8)
1644  *      p3 = transformation.untransform(p2); //  L.point(1, 2)
1645  * ```
1646  */
1647
1648
1649 // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
1650 // Creates a `Transformation` object with the given coefficients.
1651 function Transformation(a, b, c, d) {
1652         if (isArray(a)) {
1653                 // use array properties
1654                 this._a = a[0];
1655                 this._b = a[1];
1656                 this._c = a[2];
1657                 this._d = a[3];
1658                 return;
1659         }
1660         this._a = a;
1661         this._b = b;
1662         this._c = c;
1663         this._d = d;
1664 }
1665
1666 Transformation.prototype = {
1667         // @method transform(point: Point, scale?: Number): Point
1668         // Returns a transformed point, optionally multiplied by the given scale.
1669         // Only accepts actual `L.Point` instances, not arrays.
1670         transform: function (point, scale) { // (Point, Number) -> Point
1671                 return this._transform(point.clone(), scale);
1672         },
1673
1674         // destructive transform (faster)
1675         _transform: function (point, scale) {
1676                 scale = scale || 1;
1677                 point.x = scale * (this._a * point.x + this._b);
1678                 point.y = scale * (this._c * point.y + this._d);
1679                 return point;
1680         },
1681
1682         // @method untransform(point: Point, scale?: Number): Point
1683         // Returns the reverse transformation of the given point, optionally divided
1684         // by the given scale. Only accepts actual `L.Point` instances, not arrays.
1685         untransform: function (point, scale) {
1686                 scale = scale || 1;
1687                 return new Point(
1688                         (point.x / scale - this._b) / this._a,
1689                         (point.y / scale - this._d) / this._c);
1690         }
1691 };
1692
1693 // factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1694
1695 // @factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1696 // Instantiates a Transformation object with the given coefficients.
1697
1698 // @alternative
1699 // @factory L.transformation(coefficients: Array): Transformation
1700 // Expects an coeficients array of the form
1701 // `[a: Number, b: Number, c: Number, d: Number]`.
1702
1703 function toTransformation(a, b, c, d) {
1704         return new Transformation(a, b, c, d);
1705 }
1706
1707 /*
1708  * @namespace CRS
1709  * @crs L.CRS.EPSG3857
1710  *
1711  * The most common CRS for online maps, used by almost all free and commercial
1712  * tile providers. Uses Spherical Mercator projection. Set in by default in
1713  * Map's `crs` option.
1714  */
1715
1716 var EPSG3857 = extend({}, Earth, {
1717         code: 'EPSG:3857',
1718         projection: SphericalMercator,
1719
1720         transformation: (function () {
1721                 var scale = 0.5 / (Math.PI * SphericalMercator.R);
1722                 return toTransformation(scale, 0.5, -scale, 0.5);
1723         }())
1724 });
1725
1726 var EPSG900913 = extend({}, EPSG3857, {
1727         code: 'EPSG:900913'
1728 });
1729
1730 // @namespace SVG; @section
1731 // There are several static functions which can be called without instantiating L.SVG:
1732
1733 // @function create(name: String): SVGElement
1734 // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
1735 // corresponding to the class name passed. For example, using 'line' will return
1736 // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
1737 function svgCreate(name) {
1738         return document.createElementNS('http://www.w3.org/2000/svg', name);
1739 }
1740
1741 // @function pointsToPath(rings: Point[], closed: Boolean): String
1742 // Generates a SVG path string for multiple rings, with each ring turning
1743 // into "M..L..L.." instructions
1744 function pointsToPath(rings, closed) {
1745         var str = '',
1746         i, j, len, len2, points, p;
1747
1748         for (i = 0, len = rings.length; i < len; i++) {
1749                 points = rings[i];
1750
1751                 for (j = 0, len2 = points.length; j < len2; j++) {
1752                         p = points[j];
1753                         str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
1754                 }
1755
1756                 // closes the ring for polygons; "x" is VML syntax
1757                 str += closed ? (svg ? 'z' : 'x') : '';
1758         }
1759
1760         // SVG complains about empty path strings
1761         return str || 'M0 0';
1762 }
1763
1764 /*
1765  * @namespace Browser
1766  * @aka L.Browser
1767  *
1768  * A namespace with static properties for browser/feature detection used by Leaflet internally.
1769  *
1770  * @example
1771  *
1772  * ```js
1773  * if (L.Browser.ielt9) {
1774  *   alert('Upgrade your browser, dude!');
1775  * }
1776  * ```
1777  */
1778
1779 var style$1 = document.documentElement.style;
1780
1781 // @property ie: Boolean; `true` for all Internet Explorer versions (not Edge).
1782 var ie = 'ActiveXObject' in window;
1783
1784 // @property ielt9: Boolean; `true` for Internet Explorer versions less than 9.
1785 var ielt9 = ie && !document.addEventListener;
1786
1787 // @property edge: Boolean; `true` for the Edge web browser.
1788 var edge = 'msLaunchUri' in navigator && !('documentMode' in document);
1789
1790 // @property webkit: Boolean;
1791 // `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
1792 var webkit = userAgentContains('webkit');
1793
1794 // @property android: Boolean
1795 // `true` for any browser running on an Android platform.
1796 var android = userAgentContains('android');
1797
1798 // @property android23: Boolean; `true` for browsers running on Android 2 or Android 3.
1799 var android23 = userAgentContains('android 2') || userAgentContains('android 3');
1800
1801 // @property opera: Boolean; `true` for the Opera browser
1802 var opera = !!window.opera;
1803
1804 // @property chrome: Boolean; `true` for the Chrome browser.
1805 var chrome = userAgentContains('chrome');
1806
1807 // @property gecko: Boolean; `true` for gecko-based browsers like Firefox.
1808 var gecko = userAgentContains('gecko') && !webkit && !opera && !ie;
1809
1810 // @property safari: Boolean; `true` for the Safari browser.
1811 var safari = !chrome && userAgentContains('safari');
1812
1813 var phantom = userAgentContains('phantom');
1814
1815 // @property opera12: Boolean
1816 // `true` for the Opera browser supporting CSS transforms (version 12 or later).
1817 var opera12 = 'OTransition' in style$1;
1818
1819 // @property win: Boolean; `true` when the browser is running in a Windows platform
1820 var win = navigator.platform.indexOf('Win') === 0;
1821
1822 // @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms.
1823 var ie3d = ie && ('transition' in style$1);
1824
1825 // @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms.
1826 var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23;
1827
1828 // @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms.
1829 var gecko3d = 'MozPerspective' in style$1;
1830
1831 // @property any3d: Boolean
1832 // `true` for all browsers supporting CSS transforms.
1833 var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom;
1834
1835 // @property mobile: Boolean; `true` for all browsers running in a mobile device.
1836 var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile');
1837
1838 // @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device.
1839 var mobileWebkit = mobile && webkit;
1840
1841 // @property mobileWebkit3d: Boolean
1842 // `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
1843 var mobileWebkit3d = mobile && webkit3d;
1844
1845 // @property msPointer: Boolean
1846 // `true` for browsers implementing the Microsoft touch events model (notably IE10).
1847 var msPointer = !window.PointerEvent && window.MSPointerEvent;
1848
1849 // @property pointer: Boolean
1850 // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
1851 var pointer = !!(window.PointerEvent || msPointer);
1852
1853 // @property touch: Boolean
1854 // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
1855 // This does not necessarily mean that the browser is running in a computer with
1856 // a touchscreen, it only means that the browser is capable of understanding
1857 // touch events.
1858 var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
1859                 (window.DocumentTouch && document instanceof window.DocumentTouch));
1860
1861 // @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device.
1862 var mobileOpera = mobile && opera;
1863
1864 // @property mobileGecko: Boolean
1865 // `true` for gecko-based browsers running in a mobile device.
1866 var mobileGecko = mobile && gecko;
1867
1868 // @property retina: Boolean
1869 // `true` for browsers on a high-resolution "retina" screen.
1870 var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1;
1871
1872
1873 // @property canvas: Boolean
1874 // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
1875 var canvas = (function () {
1876         return !!document.createElement('canvas').getContext;
1877 }());
1878
1879 // @property svg: Boolean
1880 // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
1881 var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect);
1882
1883 // @property vml: Boolean
1884 // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
1885 var vml = !svg && (function () {
1886         try {
1887                 var div = document.createElement('div');
1888                 div.innerHTML = '<v:shape adj="1"/>';
1889
1890                 var shape = div.firstChild;
1891                 shape.style.behavior = 'url(#default#VML)';
1892
1893                 return shape && (typeof shape.adj === 'object');
1894
1895         } catch (e) {
1896                 return false;
1897         }
1898 }());
1899
1900
1901 function userAgentContains(str) {
1902         return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
1903 }
1904
1905
1906 var Browser = (Object.freeze || Object)({
1907         ie: ie,
1908         ielt9: ielt9,
1909         edge: edge,
1910         webkit: webkit,
1911         android: android,
1912         android23: android23,
1913         opera: opera,
1914         chrome: chrome,
1915         gecko: gecko,
1916         safari: safari,
1917         phantom: phantom,
1918         opera12: opera12,
1919         win: win,
1920         ie3d: ie3d,
1921         webkit3d: webkit3d,
1922         gecko3d: gecko3d,
1923         any3d: any3d,
1924         mobile: mobile,
1925         mobileWebkit: mobileWebkit,
1926         mobileWebkit3d: mobileWebkit3d,
1927         msPointer: msPointer,
1928         pointer: pointer,
1929         touch: touch,
1930         mobileOpera: mobileOpera,
1931         mobileGecko: mobileGecko,
1932         retina: retina,
1933         canvas: canvas,
1934         svg: svg,
1935         vml: vml
1936 });
1937
1938 /*
1939  * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
1940  */
1941
1942
1943 var POINTER_DOWN =   msPointer ? 'MSPointerDown'   : 'pointerdown';
1944 var POINTER_MOVE =   msPointer ? 'MSPointerMove'   : 'pointermove';
1945 var POINTER_UP =     msPointer ? 'MSPointerUp'     : 'pointerup';
1946 var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel';
1947 var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION'];
1948 var _pointers = {};
1949 var _pointerDocListener = false;
1950
1951 // DomEvent.DoubleTap needs to know about this
1952 var _pointersCount = 0;
1953
1954 // Provides a touch events wrapper for (ms)pointer events.
1955 // ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
1956
1957 function addPointerListener(obj, type, handler, id) {
1958         if (type === 'touchstart') {
1959                 _addPointerStart(obj, handler, id);
1960
1961         } else if (type === 'touchmove') {
1962                 _addPointerMove(obj, handler, id);
1963
1964         } else if (type === 'touchend') {
1965                 _addPointerEnd(obj, handler, id);
1966         }
1967
1968         return this;
1969 }
1970
1971 function removePointerListener(obj, type, id) {
1972         var handler = obj['_leaflet_' + type + id];
1973
1974         if (type === 'touchstart') {
1975                 obj.removeEventListener(POINTER_DOWN, handler, false);
1976
1977         } else if (type === 'touchmove') {
1978                 obj.removeEventListener(POINTER_MOVE, handler, false);
1979
1980         } else if (type === 'touchend') {
1981                 obj.removeEventListener(POINTER_UP, handler, false);
1982                 obj.removeEventListener(POINTER_CANCEL, handler, false);
1983         }
1984
1985         return this;
1986 }
1987
1988 function _addPointerStart(obj, handler, id) {
1989         var onDown = bind(function (e) {
1990                 if (e.pointerType !== 'mouse' && e.pointerType !== e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
1991                         // In IE11, some touch events needs to fire for form controls, or
1992                         // the controls will stop working. We keep a whitelist of tag names that
1993                         // need these events. For other target tags, we prevent default on the event.
1994                         if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
1995                                 preventDefault(e);
1996                         } else {
1997                                 return;
1998                         }
1999                 }
2000
2001                 _handlePointer(e, handler);
2002         });
2003
2004         obj['_leaflet_touchstart' + id] = onDown;
2005         obj.addEventListener(POINTER_DOWN, onDown, false);
2006
2007         // need to keep track of what pointers and how many are active to provide e.touches emulation
2008         if (!_pointerDocListener) {
2009                 // we listen documentElement as any drags that end by moving the touch off the screen get fired there
2010                 document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true);
2011                 document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true);
2012                 document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true);
2013                 document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
2014
2015                 _pointerDocListener = true;
2016         }
2017 }
2018
2019 function _globalPointerDown(e) {
2020         _pointers[e.pointerId] = e;
2021         _pointersCount++;
2022 }
2023
2024 function _globalPointerMove(e) {
2025         if (_pointers[e.pointerId]) {
2026                 _pointers[e.pointerId] = e;
2027         }
2028 }
2029
2030 function _globalPointerUp(e) {
2031         delete _pointers[e.pointerId];
2032         _pointersCount--;
2033 }
2034
2035 function _handlePointer(e, handler) {
2036         e.touches = [];
2037         for (var i in _pointers) {
2038                 e.touches.push(_pointers[i]);
2039         }
2040         e.changedTouches = [e];
2041
2042         handler(e);
2043 }
2044
2045 function _addPointerMove(obj, handler, id) {
2046         var onMove = function (e) {
2047                 // don't fire touch moves when mouse isn't down
2048                 if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
2049
2050                 _handlePointer(e, handler);
2051         };
2052
2053         obj['_leaflet_touchmove' + id] = onMove;
2054         obj.addEventListener(POINTER_MOVE, onMove, false);
2055 }
2056
2057 function _addPointerEnd(obj, handler, id) {
2058         var onUp = function (e) {
2059                 _handlePointer(e, handler);
2060         };
2061
2062         obj['_leaflet_touchend' + id] = onUp;
2063         obj.addEventListener(POINTER_UP, onUp, false);
2064         obj.addEventListener(POINTER_CANCEL, onUp, false);
2065 }
2066
2067 /*
2068  * Extends the event handling code with double tap support for mobile browsers.
2069  */
2070
2071 var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart';
2072 var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend';
2073 var _pre = '_leaflet_';
2074
2075 // inspired by Zepto touch code by Thomas Fuchs
2076 function addDoubleTapListener(obj, handler, id) {
2077         var last, touch$$1,
2078             doubleTap = false,
2079             delay = 250;
2080
2081         function onTouchStart(e) {
2082                 var count;
2083
2084                 if (pointer) {
2085                         if ((!edge) || e.pointerType === 'mouse') { return; }
2086                         count = _pointersCount;
2087                 } else {
2088                         count = e.touches.length;
2089                 }
2090
2091                 if (count > 1) { return; }
2092
2093                 var now = Date.now(),
2094                     delta = now - (last || now);
2095
2096                 touch$$1 = e.touches ? e.touches[0] : e;
2097                 doubleTap = (delta > 0 && delta <= delay);
2098                 last = now;
2099         }
2100
2101         function onTouchEnd(e) {
2102                 if (doubleTap && !touch$$1.cancelBubble) {
2103                         if (pointer) {
2104                                 if ((!edge) || e.pointerType === 'mouse') { return; }
2105                                 // work around .type being readonly with MSPointer* events
2106                                 var newTouch = {},
2107                                     prop, i;
2108
2109                                 for (i in touch$$1) {
2110                                         prop = touch$$1[i];
2111                                         newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop;
2112                                 }
2113                                 touch$$1 = newTouch;
2114                         }
2115                         touch$$1.type = 'dblclick';
2116                         handler(touch$$1);
2117                         last = null;
2118                 }
2119         }
2120
2121         obj[_pre + _touchstart + id] = onTouchStart;
2122         obj[_pre + _touchend + id] = onTouchEnd;
2123         obj[_pre + 'dblclick' + id] = handler;
2124
2125         obj.addEventListener(_touchstart, onTouchStart, false);
2126         obj.addEventListener(_touchend, onTouchEnd, false);
2127
2128         // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse),
2129         // the browser doesn't fire touchend/pointerup events but does fire
2130         // native dblclicks. See #4127.
2131         // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180.
2132         obj.addEventListener('dblclick', handler, false);
2133
2134         return this;
2135 }
2136
2137 function removeDoubleTapListener(obj, id) {
2138         var touchstart = obj[_pre + _touchstart + id],
2139             touchend = obj[_pre + _touchend + id],
2140             dblclick = obj[_pre + 'dblclick' + id];
2141
2142         obj.removeEventListener(_touchstart, touchstart, false);
2143         obj.removeEventListener(_touchend, touchend, false);
2144         if (!edge) {
2145                 obj.removeEventListener('dblclick', dblclick, false);
2146         }
2147
2148         return this;
2149 }
2150
2151 /*
2152  * @namespace DomEvent
2153  * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
2154  */
2155
2156 // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
2157
2158 // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
2159 // Adds a listener function (`fn`) to a particular DOM event type of the
2160 // element `el`. You can optionally specify the context of the listener
2161 // (object the `this` keyword will point to). You can also pass several
2162 // space-separated types (e.g. `'click dblclick'`).
2163
2164 // @alternative
2165 // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
2166 // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2167 function on(obj, types, fn, context) {
2168
2169         if (typeof types === 'object') {
2170                 for (var type in types) {
2171                         addOne(obj, type, types[type], fn);
2172                 }
2173         } else {
2174                 types = splitWords(types);
2175
2176                 for (var i = 0, len = types.length; i < len; i++) {
2177                         addOne(obj, types[i], fn, context);
2178                 }
2179         }
2180
2181         return this;
2182 }
2183
2184 var eventsKey = '_leaflet_events';
2185
2186 // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
2187 // Removes a previously added listener function. If no function is specified,
2188 // it will remove all the listeners of that particular DOM event from the element.
2189 // Note that if you passed a custom context to on, you must pass the same
2190 // context to `off` in order to remove the listener.
2191
2192 // @alternative
2193 // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
2194 // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2195
2196 // @alternative
2197 // @function off(el: HTMLElement): this
2198 // Removes all known event listeners
2199 function off(obj, types, fn, context) {
2200
2201         if (typeof types === 'object') {
2202                 for (var type in types) {
2203                         removeOne(obj, type, types[type], fn);
2204                 }
2205         } else if (types) {
2206                 types = splitWords(types);
2207
2208                 for (var i = 0, len = types.length; i < len; i++) {
2209                         removeOne(obj, types[i], fn, context);
2210                 }
2211         } else {
2212                 for (var j in obj[eventsKey]) {
2213                         removeOne(obj, j, obj[eventsKey][j]);
2214                 }
2215                 delete obj[eventsKey];
2216         }
2217 }
2218
2219 function addOne(obj, type, fn, context) {
2220         var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
2221
2222         if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
2223
2224         var handler = function (e) {
2225                 return fn.call(context || obj, e || window.event);
2226         };
2227
2228         var originalHandler = handler;
2229
2230         if (pointer && type.indexOf('touch') === 0) {
2231                 // Needs DomEvent.Pointer.js
2232                 addPointerListener(obj, type, handler, id);
2233
2234         } else if (touch && (type === 'dblclick') && addDoubleTapListener &&
2235                    !(pointer && chrome)) {
2236                 // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
2237                 // See #5180
2238                 addDoubleTapListener(obj, handler, id);
2239
2240         } else if ('addEventListener' in obj) {
2241
2242                 if (type === 'mousewheel') {
2243                         obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
2244
2245                 } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
2246                         handler = function (e) {
2247                                 e = e || window.event;
2248                                 if (isExternalTarget(obj, e)) {
2249                                         originalHandler(e);
2250                                 }
2251                         };
2252                         obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
2253
2254                 } else {
2255                         if (type === 'click' && android) {
2256                                 handler = function (e) {
2257                                         filterClick(e, originalHandler);
2258                                 };
2259                         }
2260                         obj.addEventListener(type, handler, false);
2261                 }
2262
2263         } else if ('attachEvent' in obj) {
2264                 obj.attachEvent('on' + type, handler);
2265         }
2266
2267         obj[eventsKey] = obj[eventsKey] || {};
2268         obj[eventsKey][id] = handler;
2269 }
2270
2271 function removeOne(obj, type, fn, context) {
2272
2273         var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''),
2274             handler = obj[eventsKey] && obj[eventsKey][id];
2275
2276         if (!handler) { return this; }
2277
2278         if (pointer && type.indexOf('touch') === 0) {
2279                 removePointerListener(obj, type, id);
2280
2281         } else if (touch && (type === 'dblclick') && removeDoubleTapListener) {
2282                 removeDoubleTapListener(obj, id);
2283
2284         } else if ('removeEventListener' in obj) {
2285
2286                 if (type === 'mousewheel') {
2287                         obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
2288
2289                 } else {
2290                         obj.removeEventListener(
2291                                 type === 'mouseenter' ? 'mouseover' :
2292                                 type === 'mouseleave' ? 'mouseout' : type, handler, false);
2293                 }
2294
2295         } else if ('detachEvent' in obj) {
2296                 obj.detachEvent('on' + type, handler);
2297         }
2298
2299         obj[eventsKey][id] = null;
2300 }
2301
2302 // @function stopPropagation(ev: DOMEvent): this
2303 // Stop the given event from propagation to parent elements. Used inside the listener functions:
2304 // ```js
2305 // L.DomEvent.on(div, 'click', function (ev) {
2306 //      L.DomEvent.stopPropagation(ev);
2307 // });
2308 // ```
2309 function stopPropagation(e) {
2310
2311         if (e.stopPropagation) {
2312                 e.stopPropagation();
2313         } else if (e.originalEvent) {  // In case of Leaflet event.
2314                 e.originalEvent._stopped = true;
2315         } else {
2316                 e.cancelBubble = true;
2317         }
2318         skipped(e);
2319
2320         return this;
2321 }
2322
2323 // @function disableScrollPropagation(el: HTMLElement): this
2324 // Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
2325 function disableScrollPropagation(el) {
2326         return addOne(el, 'mousewheel', stopPropagation);
2327 }
2328
2329 // @function disableClickPropagation(el: HTMLElement): this
2330 // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
2331 // `'mousedown'` and `'touchstart'` events (plus browser variants).
2332 function disableClickPropagation(el) {
2333         on(el, 'mousedown touchstart dblclick', stopPropagation);
2334         addOne(el, 'click', fakeStop);
2335         return this;
2336 }
2337
2338 // @function preventDefault(ev: DOMEvent): this
2339 // Prevents the default action of the DOM Event `ev` from happening (such as
2340 // following a link in the href of the a element, or doing a POST request
2341 // with page reload when a `<form>` is submitted).
2342 // Use it inside listener functions.
2343 function preventDefault(e) {
2344         if (e.preventDefault) {
2345                 e.preventDefault();
2346         } else {
2347                 e.returnValue = false;
2348         }
2349         return this;
2350 }
2351
2352 // @function stop(ev): this
2353 // Does `stopPropagation` and `preventDefault` at the same time.
2354 function stop(e) {
2355         preventDefault(e);
2356         stopPropagation(e);
2357         return this;
2358 }
2359
2360 // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
2361 // Gets normalized mouse position from a DOM event relative to the
2362 // `container` or to the whole page if not specified.
2363 function getMousePosition(e, container) {
2364         if (!container) {
2365                 return new Point(e.clientX, e.clientY);
2366         }
2367
2368         var rect = container.getBoundingClientRect();
2369
2370         return new Point(
2371                 e.clientX - rect.left - container.clientLeft,
2372                 e.clientY - rect.top - container.clientTop);
2373 }
2374
2375 // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
2376 // and Firefox scrolls device pixels, not CSS pixels
2377 var wheelPxFactor =
2378         (win && chrome) ? 2 * window.devicePixelRatio :
2379         gecko ? window.devicePixelRatio : 1;
2380
2381 // @function getWheelDelta(ev: DOMEvent): Number
2382 // Gets normalized wheel delta from a mousewheel DOM event, in vertical
2383 // pixels scrolled (negative if scrolling down).
2384 // Events from pointing devices without precise scrolling are mapped to
2385 // a best guess of 60 pixels.
2386 function getWheelDelta(e) {
2387         return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
2388                (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
2389                (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
2390                (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
2391                (e.deltaX || e.deltaZ) ? 0 :     // Skip horizontal/depth wheel events
2392                e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
2393                (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
2394                e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
2395                0;
2396 }
2397
2398 var skipEvents = {};
2399
2400 function fakeStop(e) {
2401         // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e)
2402         skipEvents[e.type] = true;
2403 }
2404
2405 function skipped(e) {
2406         var events = skipEvents[e.type];
2407         // reset when checking, as it's only used in map container and propagates outside of the map
2408         skipEvents[e.type] = false;
2409         return events;
2410 }
2411
2412 // check if element really left/entered the event target (for mouseenter/mouseleave)
2413 function isExternalTarget(el, e) {
2414
2415         var related = e.relatedTarget;
2416
2417         if (!related) { return true; }
2418
2419         try {
2420                 while (related && (related !== el)) {
2421                         related = related.parentNode;
2422                 }
2423         } catch (err) {
2424                 return false;
2425         }
2426         return (related !== el);
2427 }
2428
2429 var lastClick;
2430
2431 // this is a horrible workaround for a bug in Android where a single touch triggers two click events
2432 function filterClick(e, handler) {
2433         var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
2434             elapsed = lastClick && (timeStamp - lastClick);
2435
2436         // are they closer together than 500ms yet more than 100ms?
2437         // Android typically triggers them ~300ms apart while multiple listeners
2438         // on the same event should be triggered far faster;
2439         // or check if click is simulated on the element, and if it is, reject any non-simulated events
2440
2441         if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
2442                 stop(e);
2443                 return;
2444         }
2445         lastClick = timeStamp;
2446
2447         handler(e);
2448 }
2449
2450
2451
2452
2453 var DomEvent = (Object.freeze || Object)({
2454         on: on,
2455         off: off,
2456         stopPropagation: stopPropagation,
2457         disableScrollPropagation: disableScrollPropagation,
2458         disableClickPropagation: disableClickPropagation,
2459         preventDefault: preventDefault,
2460         stop: stop,
2461         getMousePosition: getMousePosition,
2462         getWheelDelta: getWheelDelta,
2463         fakeStop: fakeStop,
2464         skipped: skipped,
2465         isExternalTarget: isExternalTarget,
2466         addListener: on,
2467         removeListener: off
2468 });
2469
2470 /*
2471  * @namespace DomUtil
2472  *
2473  * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
2474  * tree, used by Leaflet internally.
2475  *
2476  * Most functions expecting or returning a `HTMLElement` also work for
2477  * SVG elements. The only difference is that classes refer to CSS classes
2478  * in HTML and SVG classes in SVG.
2479  */
2480
2481
2482 // @property TRANSFORM: String
2483 // Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
2484 var TRANSFORM = testProp(
2485         ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
2486
2487 // webkitTransition comes first because some browser versions that drop vendor prefix don't do
2488 // the same for the transitionend event, in particular the Android 4.1 stock browser
2489
2490 // @property TRANSITION: String
2491 // Vendor-prefixed transition style name.
2492 var TRANSITION = testProp(
2493         ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
2494
2495 // @property TRANSITION_END: String
2496 // Vendor-prefixed transitionend event name.
2497 var TRANSITION_END =
2498         TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
2499
2500
2501 // @function get(id: String|HTMLElement): HTMLElement
2502 // Returns an element given its DOM id, or returns the element itself
2503 // if it was passed directly.
2504 function get(id) {
2505         return typeof id === 'string' ? document.getElementById(id) : id;
2506 }
2507
2508 // @function getStyle(el: HTMLElement, styleAttrib: String): String
2509 // Returns the value for a certain style attribute on an element,
2510 // including computed values or values set through CSS.
2511 function getStyle(el, style) {
2512         var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
2513
2514         if ((!value || value === 'auto') && document.defaultView) {
2515                 var css = document.defaultView.getComputedStyle(el, null);
2516                 value = css ? css[style] : null;
2517         }
2518         return value === 'auto' ? null : value;
2519 }
2520
2521 // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
2522 // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
2523 function create$1(tagName, className, container) {
2524         var el = document.createElement(tagName);
2525         el.className = className || '';
2526
2527         if (container) {
2528                 container.appendChild(el);
2529         }
2530         return el;
2531 }
2532
2533 // @function remove(el: HTMLElement)
2534 // Removes `el` from its parent element
2535 function remove(el) {
2536         var parent = el.parentNode;
2537         if (parent) {
2538                 parent.removeChild(el);
2539         }
2540 }
2541
2542 // @function empty(el: HTMLElement)
2543 // Removes all of `el`'s children elements from `el`
2544 function empty(el) {
2545         while (el.firstChild) {
2546                 el.removeChild(el.firstChild);
2547         }
2548 }
2549
2550 // @function toFront(el: HTMLElement)
2551 // Makes `el` the last child of its parent, so it renders in front of the other children.
2552 function toFront(el) {
2553         var parent = el.parentNode;
2554         if (parent.lastChild !== el) {
2555                 parent.appendChild(el);
2556         }
2557 }
2558
2559 // @function toBack(el: HTMLElement)
2560 // Makes `el` the first child of its parent, so it renders behind the other children.
2561 function toBack(el) {
2562         var parent = el.parentNode;
2563         if (parent.firstChild !== el) {
2564                 parent.insertBefore(el, parent.firstChild);
2565         }
2566 }
2567
2568 // @function hasClass(el: HTMLElement, name: String): Boolean
2569 // Returns `true` if the element's class attribute contains `name`.
2570 function hasClass(el, name) {
2571         if (el.classList !== undefined) {
2572                 return el.classList.contains(name);
2573         }
2574         var className = getClass(el);
2575         return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
2576 }
2577
2578 // @function addClass(el: HTMLElement, name: String)
2579 // Adds `name` to the element's class attribute.
2580 function addClass(el, name) {
2581         if (el.classList !== undefined) {
2582                 var classes = splitWords(name);
2583                 for (var i = 0, len = classes.length; i < len; i++) {
2584                         el.classList.add(classes[i]);
2585                 }
2586         } else if (!hasClass(el, name)) {
2587                 var className = getClass(el);
2588                 setClass(el, (className ? className + ' ' : '') + name);
2589         }
2590 }
2591
2592 // @function removeClass(el: HTMLElement, name: String)
2593 // Removes `name` from the element's class attribute.
2594 function removeClass(el, name) {
2595         if (el.classList !== undefined) {
2596                 el.classList.remove(name);
2597         } else {
2598                 setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
2599         }
2600 }
2601
2602 // @function setClass(el: HTMLElement, name: String)
2603 // Sets the element's class.
2604 function setClass(el, name) {
2605         if (el.className.baseVal === undefined) {
2606                 el.className = name;
2607         } else {
2608                 // in case of SVG element
2609                 el.className.baseVal = name;
2610         }
2611 }
2612
2613 // @function getClass(el: HTMLElement): String
2614 // Returns the element's class.
2615 function getClass(el) {
2616         return el.className.baseVal === undefined ? el.className : el.className.baseVal;
2617 }
2618
2619 // @function setOpacity(el: HTMLElement, opacity: Number)
2620 // Set the opacity of an element (including old IE support).
2621 // `opacity` must be a number from `0` to `1`.
2622 function setOpacity(el, value) {
2623         if ('opacity' in el.style) {
2624                 el.style.opacity = value;
2625         } else if ('filter' in el.style) {
2626                 _setOpacityIE(el, value);
2627         }
2628 }
2629
2630 function _setOpacityIE(el, value) {
2631         var filter = false,
2632             filterName = 'DXImageTransform.Microsoft.Alpha';
2633
2634         // filters collection throws an error if we try to retrieve a filter that doesn't exist
2635         try {
2636                 filter = el.filters.item(filterName);
2637         } catch (e) {
2638                 // don't set opacity to 1 if we haven't already set an opacity,
2639                 // it isn't needed and breaks transparent pngs.
2640                 if (value === 1) { return; }
2641         }
2642
2643         value = Math.round(value * 100);
2644
2645         if (filter) {
2646                 filter.Enabled = (value !== 100);
2647                 filter.Opacity = value;
2648         } else {
2649                 el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
2650         }
2651 }
2652
2653 // @function testProp(props: String[]): String|false
2654 // Goes through the array of style names and returns the first name
2655 // that is a valid style name for an element. If no such name is found,
2656 // it returns false. Useful for vendor-prefixed styles like `transform`.
2657 function testProp(props) {
2658         var style = document.documentElement.style;
2659
2660         for (var i = 0; i < props.length; i++) {
2661                 if (props[i] in style) {
2662                         return props[i];
2663                 }
2664         }
2665         return false;
2666 }
2667
2668 // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
2669 // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
2670 // and optionally scaled by `scale`. Does not have an effect if the
2671 // browser doesn't support 3D CSS transforms.
2672 function setTransform(el, offset, scale) {
2673         var pos = offset || new Point(0, 0);
2674
2675         el.style[TRANSFORM] =
2676                 (ie3d ?
2677                         'translate(' + pos.x + 'px,' + pos.y + 'px)' :
2678                         'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
2679                 (scale ? ' scale(' + scale + ')' : '');
2680 }
2681
2682 // @function setPosition(el: HTMLElement, position: Point)
2683 // Sets the position of `el` to coordinates specified by `position`,
2684 // using CSS translate or top/left positioning depending on the browser
2685 // (used by Leaflet internally to position its layers).
2686 function setPosition(el, point) {
2687
2688         /*eslint-disable */
2689         el._leaflet_pos = point;
2690         /*eslint-enable */
2691
2692         if (any3d) {
2693                 setTransform(el, point);
2694         } else {
2695                 el.style.left = point.x + 'px';
2696                 el.style.top = point.y + 'px';
2697         }
2698 }
2699
2700 // @function getPosition(el: HTMLElement): Point
2701 // Returns the coordinates of an element previously positioned with setPosition.
2702 function getPosition(el) {
2703         // this method is only used for elements previously positioned using setPosition,
2704         // so it's safe to cache the position for performance
2705
2706         return el._leaflet_pos || new Point(0, 0);
2707 }
2708
2709 // @function disableTextSelection()
2710 // Prevents the user from generating `selectstart` DOM events, usually generated
2711 // when the user drags the mouse through a page with text. Used internally
2712 // by Leaflet to override the behaviour of any click-and-drag interaction on
2713 // the map. Affects drag interactions on the whole document.
2714
2715 // @function enableTextSelection()
2716 // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
2717 var disableTextSelection;
2718 var enableTextSelection;
2719 var _userSelect;
2720 if ('onselectstart' in document) {
2721         disableTextSelection = function () {
2722                 on(window, 'selectstart', preventDefault);
2723         };
2724         enableTextSelection = function () {
2725                 off(window, 'selectstart', preventDefault);
2726         };
2727 } else {
2728         var userSelectProperty = testProp(
2729                 ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
2730
2731         disableTextSelection = function () {
2732                 if (userSelectProperty) {
2733                         var style = document.documentElement.style;
2734                         _userSelect = style[userSelectProperty];
2735                         style[userSelectProperty] = 'none';
2736                 }
2737         };
2738         enableTextSelection = function () {
2739                 if (userSelectProperty) {
2740                         document.documentElement.style[userSelectProperty] = _userSelect;
2741                         _userSelect = undefined;
2742                 }
2743         };
2744 }
2745
2746 // @function disableImageDrag()
2747 // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
2748 // for `dragstart` DOM events, usually generated when the user drags an image.
2749 function disableImageDrag() {
2750         on(window, 'dragstart', preventDefault);
2751 }
2752
2753 // @function enableImageDrag()
2754 // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
2755 function enableImageDrag() {
2756         off(window, 'dragstart', preventDefault);
2757 }
2758
2759 var _outlineElement;
2760 var _outlineStyle;
2761 // @function preventOutline(el: HTMLElement)
2762 // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
2763 // of the element `el` invisible. Used internally by Leaflet to prevent
2764 // focusable elements from displaying an outline when the user performs a
2765 // drag interaction on them.
2766 function preventOutline(element) {
2767         while (element.tabIndex === -1) {
2768                 element = element.parentNode;
2769         }
2770         if (!element.style) { return; }
2771         restoreOutline();
2772         _outlineElement = element;
2773         _outlineStyle = element.style.outline;
2774         element.style.outline = 'none';
2775         on(window, 'keydown', restoreOutline);
2776 }
2777
2778 // @function restoreOutline()
2779 // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
2780 function restoreOutline() {
2781         if (!_outlineElement) { return; }
2782         _outlineElement.style.outline = _outlineStyle;
2783         _outlineElement = undefined;
2784         _outlineStyle = undefined;
2785         off(window, 'keydown', restoreOutline);
2786 }
2787
2788
2789 var DomUtil = (Object.freeze || Object)({
2790         TRANSFORM: TRANSFORM,
2791         TRANSITION: TRANSITION,
2792         TRANSITION_END: TRANSITION_END,
2793         get: get,
2794         getStyle: getStyle,
2795         create: create$1,
2796         remove: remove,
2797         empty: empty,
2798         toFront: toFront,
2799         toBack: toBack,
2800         hasClass: hasClass,
2801         addClass: addClass,
2802         removeClass: removeClass,
2803         setClass: setClass,
2804         getClass: getClass,
2805         setOpacity: setOpacity,
2806         testProp: testProp,
2807         setTransform: setTransform,
2808         setPosition: setPosition,
2809         getPosition: getPosition,
2810         disableTextSelection: disableTextSelection,
2811         enableTextSelection: enableTextSelection,
2812         disableImageDrag: disableImageDrag,
2813         enableImageDrag: enableImageDrag,
2814         preventOutline: preventOutline,
2815         restoreOutline: restoreOutline
2816 });
2817
2818 /*
2819  * @class PosAnimation
2820  * @aka L.PosAnimation
2821  * @inherits Evented
2822  * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
2823  *
2824  * @example
2825  * ```js
2826  * var fx = new L.PosAnimation();
2827  * fx.run(el, [300, 500], 0.5);
2828  * ```
2829  *
2830  * @constructor L.PosAnimation()
2831  * Creates a `PosAnimation` object.
2832  *
2833  */
2834
2835 var PosAnimation = Evented.extend({
2836
2837         // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
2838         // Run an animation of a given element to a new position, optionally setting
2839         // duration in seconds (`0.25` by default) and easing linearity factor (3rd
2840         // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
2841         // `0.5` by default).
2842         run: function (el, newPos, duration, easeLinearity) {
2843                 this.stop();
2844
2845                 this._el = el;
2846                 this._inProgress = true;
2847                 this._duration = duration || 0.25;
2848                 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
2849
2850                 this._startPos = getPosition(el);
2851                 this._offset = newPos.subtract(this._startPos);
2852                 this._startTime = +new Date();
2853
2854                 // @event start: Event
2855                 // Fired when the animation starts
2856                 this.fire('start');
2857
2858                 this._animate();
2859         },
2860
2861         // @method stop()
2862         // Stops the animation (if currently running).
2863         stop: function () {
2864                 if (!this._inProgress) { return; }
2865
2866                 this._step(true);
2867                 this._complete();
2868         },
2869
2870         _animate: function () {
2871                 // animation loop
2872                 this._animId = requestAnimFrame(this._animate, this);
2873                 this._step();
2874         },
2875
2876         _step: function (round) {
2877                 var elapsed = (+new Date()) - this._startTime,
2878                     duration = this._duration * 1000;
2879
2880                 if (elapsed < duration) {
2881                         this._runFrame(this._easeOut(elapsed / duration), round);
2882                 } else {
2883                         this._runFrame(1);
2884                         this._complete();
2885                 }
2886         },
2887
2888         _runFrame: function (progress, round) {
2889                 var pos = this._startPos.add(this._offset.multiplyBy(progress));
2890                 if (round) {
2891                         pos._round();
2892                 }
2893                 setPosition(this._el, pos);
2894
2895                 // @event step: Event
2896                 // Fired continuously during the animation.
2897                 this.fire('step');
2898         },
2899
2900         _complete: function () {
2901                 cancelAnimFrame(this._animId);
2902
2903                 this._inProgress = false;
2904                 // @event end: Event
2905                 // Fired when the animation ends.
2906                 this.fire('end');
2907         },
2908
2909         _easeOut: function (t) {
2910                 return 1 - Math.pow(1 - t, this._easeOutPower);
2911         }
2912 });
2913
2914 /*
2915  * @class Map
2916  * @aka L.Map
2917  * @inherits Evented
2918  *
2919  * The central class of the API — it is used to create a map on a page and manipulate it.
2920  *
2921  * @example
2922  *
2923  * ```js
2924  * // initialize the map on the "map" div with a given center and zoom
2925  * var map = L.map('map', {
2926  *      center: [51.505, -0.09],
2927  *      zoom: 13
2928  * });
2929  * ```
2930  *
2931  */
2932
2933 var Map = Evented.extend({
2934
2935         options: {
2936                 // @section Map State Options
2937                 // @option crs: CRS = L.CRS.EPSG3857
2938                 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
2939                 // sure what it means.
2940                 crs: EPSG3857,
2941
2942                 // @option center: LatLng = undefined
2943                 // Initial geographic center of the map
2944                 center: undefined,
2945
2946                 // @option zoom: Number = undefined
2947                 // Initial map zoom level
2948                 zoom: undefined,
2949
2950                 // @option minZoom: Number = *
2951                 // Minimum zoom level of the map.
2952                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
2953                 // the lowest of their `minZoom` options will be used instead.
2954                 minZoom: undefined,
2955
2956                 // @option maxZoom: Number = *
2957                 // Maximum zoom level of the map.
2958                 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
2959                 // the highest of their `maxZoom` options will be used instead.
2960                 maxZoom: undefined,
2961
2962                 // @option layers: Layer[] = []
2963                 // Array of layers that will be added to the map initially
2964                 layers: [],
2965
2966                 // @option maxBounds: LatLngBounds = null
2967                 // When this option is set, the map restricts the view to the given
2968                 // geographical bounds, bouncing the user back if the user tries to pan
2969                 // outside the view. To set the restriction dynamically, use
2970                 // [`setMaxBounds`](#map-setmaxbounds) method.
2971                 maxBounds: undefined,
2972
2973                 // @option renderer: Renderer = *
2974                 // The default method for drawing vector layers on the map. `L.SVG`
2975                 // or `L.Canvas` by default depending on browser support.
2976                 renderer: undefined,
2977
2978
2979                 // @section Animation Options
2980                 // @option zoomAnimation: Boolean = true
2981                 // Whether the map zoom animation is enabled. By default it's enabled
2982                 // in all browsers that support CSS3 Transitions except Android.
2983                 zoomAnimation: true,
2984
2985                 // @option zoomAnimationThreshold: Number = 4
2986                 // Won't animate zoom if the zoom difference exceeds this value.
2987                 zoomAnimationThreshold: 4,
2988
2989                 // @option fadeAnimation: Boolean = true
2990                 // Whether the tile fade animation is enabled. By default it's enabled
2991                 // in all browsers that support CSS3 Transitions except Android.
2992                 fadeAnimation: true,
2993
2994                 // @option markerZoomAnimation: Boolean = true
2995                 // Whether markers animate their zoom with the zoom animation, if disabled
2996                 // they will disappear for the length of the animation. By default it's
2997                 // enabled in all browsers that support CSS3 Transitions except Android.
2998                 markerZoomAnimation: true,
2999
3000                 // @option transform3DLimit: Number = 2^23
3001                 // Defines the maximum size of a CSS translation transform. The default
3002                 // value should not be changed unless a web browser positions layers in
3003                 // the wrong place after doing a large `panBy`.
3004                 transform3DLimit: 8388608, // Precision limit of a 32-bit float
3005
3006                 // @section Interaction Options
3007                 // @option zoomSnap: Number = 1
3008                 // Forces the map's zoom level to always be a multiple of this, particularly
3009                 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
3010                 // By default, the zoom level snaps to the nearest integer; lower values
3011                 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
3012                 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
3013                 zoomSnap: 1,
3014
3015                 // @option zoomDelta: Number = 1
3016                 // Controls how much the map's zoom level will change after a
3017                 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
3018                 // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
3019                 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
3020                 zoomDelta: 1,
3021
3022                 // @option trackResize: Boolean = true
3023                 // Whether the map automatically handles browser window resize to update itself.
3024                 trackResize: true
3025         },
3026
3027         initialize: function (id, options) { // (HTMLElement or String, Object)
3028                 options = setOptions(this, options);
3029
3030                 this._initContainer(id);
3031                 this._initLayout();
3032
3033                 // hack for https://github.com/Leaflet/Leaflet/issues/1980
3034                 this._onResize = bind(this._onResize, this);
3035
3036                 this._initEvents();
3037
3038                 if (options.maxBounds) {
3039                         this.setMaxBounds(options.maxBounds);
3040                 }
3041
3042                 if (options.zoom !== undefined) {
3043                         this._zoom = this._limitZoom(options.zoom);
3044                 }
3045
3046                 if (options.center && options.zoom !== undefined) {
3047                         this.setView(toLatLng(options.center), options.zoom, {reset: true});
3048                 }
3049
3050                 this._handlers = [];
3051                 this._layers = {};
3052                 this._zoomBoundLayers = {};
3053                 this._sizeChanged = true;
3054
3055                 this.callInitHooks();
3056
3057                 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
3058                 this._zoomAnimated = TRANSITION && any3d && !mobileOpera &&
3059                                 this.options.zoomAnimation;
3060
3061                 // zoom transitions run with the same duration for all layers, so if one of transitionend events
3062                 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
3063                 if (this._zoomAnimated) {
3064                         this._createAnimProxy();
3065                         on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
3066                 }
3067
3068                 this._addLayers(this.options.layers);
3069         },
3070
3071
3072         // @section Methods for modifying map state
3073
3074         // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
3075         // Sets the view of the map (geographical center and zoom) with the given
3076         // animation options.
3077         setView: function (center, zoom, options) {
3078
3079                 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
3080                 center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
3081                 options = options || {};
3082
3083                 this._stop();
3084
3085                 if (this._loaded && !options.reset && options !== true) {
3086
3087                         if (options.animate !== undefined) {
3088                                 options.zoom = extend({animate: options.animate}, options.zoom);
3089                                 options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
3090                         }
3091
3092                         // try animating pan or zoom
3093                         var moved = (this._zoom !== zoom) ?
3094                                 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
3095                                 this._tryAnimatedPan(center, options.pan);
3096
3097                         if (moved) {
3098                                 // prevent resize handler call, the view will refresh after animation anyway
3099                                 clearTimeout(this._sizeTimer);
3100                                 return this;
3101                         }
3102                 }
3103
3104                 // animation didn't start, just reset the map view
3105                 this._resetView(center, zoom);
3106
3107                 return this;
3108         },
3109
3110         // @method setZoom(zoom: Number, options?: Zoom/pan options): this
3111         // Sets the zoom of the map.
3112         setZoom: function (zoom, options) {
3113                 if (!this._loaded) {
3114                         this._zoom = zoom;
3115                         return this;
3116                 }
3117                 return this.setView(this.getCenter(), zoom, {zoom: options});
3118         },
3119
3120         // @method zoomIn(delta?: Number, options?: Zoom options): this
3121         // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3122         zoomIn: function (delta, options) {
3123                 delta = delta || (any3d ? this.options.zoomDelta : 1);
3124                 return this.setZoom(this._zoom + delta, options);
3125         },
3126
3127         // @method zoomOut(delta?: Number, options?: Zoom options): this
3128         // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3129         zoomOut: function (delta, options) {
3130                 delta = delta || (any3d ? this.options.zoomDelta : 1);
3131                 return this.setZoom(this._zoom - delta, options);
3132         },
3133
3134         // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
3135         // Zooms the map while keeping a specified geographical point on the map
3136         // stationary (e.g. used internally for scroll zoom and double-click zoom).
3137         // @alternative
3138         // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
3139         // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
3140         setZoomAround: function (latlng, zoom, options) {
3141                 var scale = this.getZoomScale(zoom),
3142                     viewHalf = this.getSize().divideBy(2),
3143                     containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
3144
3145                     centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
3146                     newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
3147
3148                 return this.setView(newCenter, zoom, {zoom: options});
3149         },
3150
3151         _getBoundsCenterZoom: function (bounds, options) {
3152
3153                 options = options || {};
3154                 bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
3155
3156                 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3157                     paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3158
3159                     zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
3160
3161                 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
3162
3163                 if (zoom === Infinity) {
3164                         return {
3165                                 center: bounds.getCenter(),
3166                                 zoom: zoom
3167                         };
3168                 }
3169
3170                 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
3171
3172                     swPoint = this.project(bounds.getSouthWest(), zoom),
3173                     nePoint = this.project(bounds.getNorthEast(), zoom),
3174                     center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
3175
3176                 return {
3177                         center: center,
3178                         zoom: zoom
3179                 };
3180         },
3181
3182         // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
3183         // Sets a map view that contains the given geographical bounds with the
3184         // maximum zoom level possible.
3185         fitBounds: function (bounds, options) {
3186
3187                 bounds = toLatLngBounds(bounds);
3188
3189                 if (!bounds.isValid()) {
3190                         throw new Error('Bounds are not valid.');
3191                 }
3192
3193                 var target = this._getBoundsCenterZoom(bounds, options);
3194                 return this.setView(target.center, target.zoom, options);
3195         },
3196
3197         // @method fitWorld(options?: fitBounds options): this
3198         // Sets a map view that mostly contains the whole world with the maximum
3199         // zoom level possible.
3200         fitWorld: function (options) {
3201                 return this.fitBounds([[-90, -180], [90, 180]], options);
3202         },
3203
3204         // @method panTo(latlng: LatLng, options?: Pan options): this
3205         // Pans the map to a given center.
3206         panTo: function (center, options) { // (LatLng)
3207                 return this.setView(center, this._zoom, {pan: options});
3208         },
3209
3210         // @method panBy(offset: Point, options?: Pan options): this
3211         // Pans the map by a given number of pixels (animated).
3212         panBy: function (offset, options) {
3213                 offset = toPoint(offset).round();
3214                 options = options || {};
3215
3216                 if (!offset.x && !offset.y) {
3217                         return this.fire('moveend');
3218                 }
3219                 // If we pan too far, Chrome gets issues with tiles
3220                 // and makes them disappear or appear in the wrong place (slightly offset) #2602
3221                 if (options.animate !== true && !this.getSize().contains(offset)) {
3222                         this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
3223                         return this;
3224                 }
3225
3226                 if (!this._panAnim) {
3227                         this._panAnim = new PosAnimation();
3228
3229                         this._panAnim.on({
3230                                 'step': this._onPanTransitionStep,
3231                                 'end': this._onPanTransitionEnd
3232                         }, this);
3233                 }
3234
3235                 // don't fire movestart if animating inertia
3236                 if (!options.noMoveStart) {
3237                         this.fire('movestart');
3238                 }
3239
3240                 // animate pan unless animate: false specified
3241                 if (options.animate !== false) {
3242                         addClass(this._mapPane, 'leaflet-pan-anim');
3243
3244                         var newPos = this._getMapPanePos().subtract(offset).round();
3245                         this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
3246                 } else {
3247                         this._rawPanBy(offset);
3248                         this.fire('move').fire('moveend');
3249                 }
3250
3251                 return this;
3252         },
3253
3254         // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
3255         // Sets the view of the map (geographical center and zoom) performing a smooth
3256         // pan-zoom animation.
3257         flyTo: function (targetCenter, targetZoom, options) {
3258
3259                 options = options || {};
3260                 if (options.animate === false || !any3d) {
3261                         return this.setView(targetCenter, targetZoom, options);
3262                 }
3263
3264                 this._stop();
3265
3266                 var from = this.project(this.getCenter()),
3267                     to = this.project(targetCenter),
3268                     size = this.getSize(),
3269                     startZoom = this._zoom;
3270
3271                 targetCenter = toLatLng(targetCenter);
3272                 targetZoom = targetZoom === undefined ? startZoom : targetZoom;
3273
3274                 var w0 = Math.max(size.x, size.y),
3275                     w1 = w0 * this.getZoomScale(startZoom, targetZoom),
3276                     u1 = (to.distanceTo(from)) || 1,
3277                     rho = 1.42,
3278                     rho2 = rho * rho;
3279
3280                 function r(i) {
3281                         var s1 = i ? -1 : 1,
3282                             s2 = i ? w1 : w0,
3283                             t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
3284                             b1 = 2 * s2 * rho2 * u1,
3285                             b = t1 / b1,
3286                             sq = Math.sqrt(b * b + 1) - b;
3287
3288                             // workaround for floating point precision bug when sq = 0, log = -Infinite,
3289                             // thus triggering an infinite loop in flyTo
3290                             var log = sq < 0.000000001 ? -18 : Math.log(sq);
3291
3292                         return log;
3293                 }
3294
3295                 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
3296                 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
3297                 function tanh(n) { return sinh(n) / cosh(n); }
3298
3299                 var r0 = r(0);
3300
3301                 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
3302                 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
3303
3304                 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
3305
3306                 var start = Date.now(),
3307                     S = (r(1) - r0) / rho,
3308                     duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
3309
3310                 function frame() {
3311                         var t = (Date.now() - start) / duration,
3312                             s = easeOut(t) * S;
3313
3314                         if (t <= 1) {
3315                                 this._flyToFrame = requestAnimFrame(frame, this);
3316
3317                                 this._move(
3318                                         this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
3319                                         this.getScaleZoom(w0 / w(s), startZoom),
3320                                         {flyTo: true});
3321
3322                         } else {
3323                                 this
3324                                         ._move(targetCenter, targetZoom)
3325                                         ._moveEnd(true);
3326                         }
3327                 }
3328
3329                 this._moveStart(true);
3330
3331                 frame.call(this);
3332                 return this;
3333         },
3334
3335         // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
3336         // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
3337         // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
3338         flyToBounds: function (bounds, options) {
3339                 var target = this._getBoundsCenterZoom(bounds, options);
3340                 return this.flyTo(target.center, target.zoom, options);
3341         },
3342
3343         // @method setMaxBounds(bounds: Bounds): this
3344         // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
3345         setMaxBounds: function (bounds) {
3346                 bounds = toLatLngBounds(bounds);
3347
3348                 if (!bounds.isValid()) {
3349                         this.options.maxBounds = null;
3350                         return this.off('moveend', this._panInsideMaxBounds);
3351                 } else if (this.options.maxBounds) {
3352                         this.off('moveend', this._panInsideMaxBounds);
3353                 }
3354
3355                 this.options.maxBounds = bounds;
3356
3357                 if (this._loaded) {
3358                         this._panInsideMaxBounds();
3359                 }
3360
3361                 return this.on('moveend', this._panInsideMaxBounds);
3362         },
3363
3364         // @method setMinZoom(zoom: Number): this
3365         // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
3366         setMinZoom: function (zoom) {
3367                 this.options.minZoom = zoom;
3368
3369                 if (this._loaded && this.getZoom() < this.options.minZoom) {
3370                         return this.setZoom(zoom);
3371                 }
3372
3373                 return this;
3374         },
3375
3376         // @method setMaxZoom(zoom: Number): this
3377         // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
3378         setMaxZoom: function (zoom) {
3379                 this.options.maxZoom = zoom;
3380
3381                 if (this._loaded && (this.getZoom() > this.options.maxZoom)) {
3382                         return this.setZoom(zoom);
3383                 }
3384
3385                 return this;
3386         },
3387
3388         // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
3389         // Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any.
3390         panInsideBounds: function (bounds, options) {
3391                 this._enforcingBounds = true;
3392                 var center = this.getCenter(),
3393                     newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
3394
3395                 if (!center.equals(newCenter)) {
3396                         this.panTo(newCenter, options);
3397                 }
3398
3399                 this._enforcingBounds = false;
3400                 return this;
3401         },
3402
3403         // @method invalidateSize(options: Zoom/Pan options): this
3404         // Checks if the map container size changed and updates the map if so —
3405         // call it after you've changed the map size dynamically, also animating
3406         // pan by default. If `options.pan` is `false`, panning will not occur.
3407         // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
3408         // that it doesn't happen often even if the method is called many
3409         // times in a row.
3410
3411         // @alternative
3412         // @method invalidateSize(animate: Boolean): this
3413         // Checks if the map container size changed and updates the map if so —
3414         // call it after you've changed the map size dynamically, also animating
3415         // pan by default.
3416         invalidateSize: function (options) {
3417                 if (!this._loaded) { return this; }
3418
3419                 options = extend({
3420                         animate: false,
3421                         pan: true
3422                 }, options === true ? {animate: true} : options);
3423
3424                 var oldSize = this.getSize();
3425                 this._sizeChanged = true;
3426                 this._lastCenter = null;
3427
3428                 var newSize = this.getSize(),
3429                     oldCenter = oldSize.divideBy(2).round(),
3430                     newCenter = newSize.divideBy(2).round(),
3431                     offset = oldCenter.subtract(newCenter);
3432
3433                 if (!offset.x && !offset.y) { return this; }
3434
3435                 if (options.animate && options.pan) {
3436                         this.panBy(offset);
3437
3438                 } else {
3439                         if (options.pan) {
3440                                 this._rawPanBy(offset);
3441                         }
3442
3443                         this.fire('move');
3444
3445                         if (options.debounceMoveend) {
3446                                 clearTimeout(this._sizeTimer);
3447                                 this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
3448                         } else {
3449                                 this.fire('moveend');
3450                         }
3451                 }
3452
3453                 // @section Map state change events
3454                 // @event resize: ResizeEvent
3455                 // Fired when the map is resized.
3456                 return this.fire('resize', {
3457                         oldSize: oldSize,
3458                         newSize: newSize
3459                 });
3460         },
3461
3462         // @section Methods for modifying map state
3463         // @method stop(): this
3464         // Stops the currently running `panTo` or `flyTo` animation, if any.
3465         stop: function () {
3466                 this.setZoom(this._limitZoom(this._zoom));
3467                 if (!this.options.zoomSnap) {
3468                         this.fire('viewreset');
3469                 }
3470                 return this._stop();
3471         },
3472
3473         // @section Geolocation methods
3474         // @method locate(options?: Locate options): this
3475         // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
3476         // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
3477         // and optionally sets the map view to the user's location with respect to
3478         // detection accuracy (or to the world view if geolocation failed).
3479         // Note that, if your page doesn't use HTTPS, this method will fail in
3480         // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
3481         // See `Locate options` for more details.
3482         locate: function (options) {
3483
3484                 options = this._locateOptions = extend({
3485                         timeout: 10000,
3486                         watch: false
3487                         // setView: false
3488                         // maxZoom: <Number>
3489                         // maximumAge: 0
3490                         // enableHighAccuracy: false
3491                 }, options);
3492
3493                 if (!('geolocation' in navigator)) {
3494                         this._handleGeolocationError({
3495                                 code: 0,
3496                                 message: 'Geolocation not supported.'
3497                         });
3498                         return this;
3499                 }
3500
3501                 var onResponse = bind(this._handleGeolocationResponse, this),
3502                     onError = bind(this._handleGeolocationError, this);
3503
3504                 if (options.watch) {
3505                         this._locationWatchId =
3506                                 navigator.geolocation.watchPosition(onResponse, onError, options);
3507                 } else {
3508                         navigator.geolocation.getCurrentPosition(onResponse, onError, options);
3509                 }
3510                 return this;
3511         },
3512
3513         // @method stopLocate(): this
3514         // Stops watching location previously initiated by `map.locate({watch: true})`
3515         // and aborts resetting the map view if map.locate was called with
3516         // `{setView: true}`.
3517         stopLocate: function () {
3518                 if (navigator.geolocation && navigator.geolocation.clearWatch) {
3519                         navigator.geolocation.clearWatch(this._locationWatchId);
3520                 }
3521                 if (this._locateOptions) {
3522                         this._locateOptions.setView = false;
3523                 }
3524                 return this;
3525         },
3526
3527         _handleGeolocationError: function (error) {
3528                 var c = error.code,
3529                     message = error.message ||
3530                             (c === 1 ? 'permission denied' :
3531                             (c === 2 ? 'position unavailable' : 'timeout'));
3532
3533                 if (this._locateOptions.setView && !this._loaded) {
3534                         this.fitWorld();
3535                 }
3536
3537                 // @section Location events
3538                 // @event locationerror: ErrorEvent
3539                 // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
3540                 this.fire('locationerror', {
3541                         code: c,
3542                         message: 'Geolocation error: ' + message + '.'
3543                 });
3544         },
3545
3546         _handleGeolocationResponse: function (pos) {
3547                 var lat = pos.coords.latitude,
3548                     lng = pos.coords.longitude,
3549                     latlng = new LatLng(lat, lng),
3550                     bounds = latlng.toBounds(pos.coords.accuracy),
3551                     options = this._locateOptions;
3552
3553                 if (options.setView) {
3554                         var zoom = this.getBoundsZoom(bounds);
3555                         this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
3556                 }
3557
3558                 var data = {
3559                         latlng: latlng,
3560                         bounds: bounds,
3561                         timestamp: pos.timestamp
3562                 };
3563
3564                 for (var i in pos.coords) {
3565                         if (typeof pos.coords[i] === 'number') {
3566                                 data[i] = pos.coords[i];
3567                         }
3568                 }
3569
3570                 // @event locationfound: LocationEvent
3571                 // Fired when geolocation (using the [`locate`](#map-locate) method)
3572                 // went successfully.
3573                 this.fire('locationfound', data);
3574         },
3575
3576         // TODO handler.addTo
3577         // TODO Appropiate docs section?
3578         // @section Other Methods
3579         // @method addHandler(name: String, HandlerClass: Function): this
3580         // Adds a new `Handler` to the map, given its name and constructor function.
3581         addHandler: function (name, HandlerClass) {
3582                 if (!HandlerClass) { return this; }
3583
3584                 var handler = this[name] = new HandlerClass(this);
3585
3586                 this._handlers.push(handler);
3587
3588                 if (this.options[name]) {
3589                         handler.enable();
3590                 }
3591
3592                 return this;
3593         },
3594
3595         // @method remove(): this
3596         // Destroys the map and clears all related event listeners.
3597         remove: function () {
3598
3599                 this._initEvents(true);
3600
3601                 if (this._containerId !== this._container._leaflet_id) {
3602                         throw new Error('Map container is being reused by another instance');
3603                 }
3604
3605                 try {
3606                         // throws error in IE6-8
3607                         delete this._container._leaflet_id;
3608                         delete this._containerId;
3609                 } catch (e) {
3610                         /*eslint-disable */
3611                         this._container._leaflet_id = undefined;
3612                         /*eslint-enable */
3613                         this._containerId = undefined;
3614                 }
3615
3616                 remove(this._mapPane);
3617
3618                 if (this._clearControlPos) {
3619                         this._clearControlPos();
3620                 }
3621
3622                 this._clearHandlers();
3623
3624                 if (this._loaded) {
3625                         // @section Map state change events
3626                         // @event unload: Event
3627                         // Fired when the map is destroyed with [remove](#map-remove) method.
3628                         this.fire('unload');
3629                 }
3630
3631                 var i;
3632                 for (i in this._layers) {
3633                         this._layers[i].remove();
3634                 }
3635                 for (i in this._panes) {
3636                         remove(this._panes[i]);
3637                 }
3638
3639                 this._layers = [];
3640                 this._panes = [];
3641                 delete this._mapPane;
3642                 delete this._renderer;
3643
3644                 return this;
3645         },
3646
3647         // @section Other Methods
3648         // @method createPane(name: String, container?: HTMLElement): HTMLElement
3649         // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
3650         // then returns it. The pane is created as a child of `container`, or
3651         // as a child of the main map pane if not set.
3652         createPane: function (name, container) {
3653                 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
3654                     pane = create$1('div', className, container || this._mapPane);
3655
3656                 if (name) {
3657                         this._panes[name] = pane;
3658                 }
3659                 return pane;
3660         },
3661
3662         // @section Methods for Getting Map State
3663
3664         // @method getCenter(): LatLng
3665         // Returns the geographical center of the map view
3666         getCenter: function () {
3667                 this._checkIfLoaded();
3668
3669                 if (this._lastCenter && !this._moved()) {
3670                         return this._lastCenter;
3671                 }
3672                 return this.layerPointToLatLng(this._getCenterLayerPoint());
3673         },
3674
3675         // @method getZoom(): Number
3676         // Returns the current zoom level of the map view
3677         getZoom: function () {
3678                 return this._zoom;
3679         },
3680
3681         // @method getBounds(): LatLngBounds
3682         // Returns the geographical bounds visible in the current map view
3683         getBounds: function () {
3684                 var bounds = this.getPixelBounds(),
3685                     sw = this.unproject(bounds.getBottomLeft()),
3686                     ne = this.unproject(bounds.getTopRight());
3687
3688                 return new LatLngBounds(sw, ne);
3689         },
3690
3691         // @method getMinZoom(): Number
3692         // Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default.
3693         getMinZoom: function () {
3694                 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
3695         },
3696
3697         // @method getMaxZoom(): Number
3698         // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
3699         getMaxZoom: function () {
3700                 return this.options.maxZoom === undefined ?
3701                         (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
3702                         this.options.maxZoom;
3703         },
3704
3705         // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean): Number
3706         // Returns the maximum zoom level on which the given bounds fit to the map
3707         // view in its entirety. If `inside` (optional) is set to `true`, the method
3708         // instead returns the minimum zoom level on which the map view fits into
3709         // the given bounds in its entirety.
3710         getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
3711                 bounds = toLatLngBounds(bounds);
3712                 padding = toPoint(padding || [0, 0]);
3713
3714                 var zoom = this.getZoom() || 0,
3715                     min = this.getMinZoom(),
3716                     max = this.getMaxZoom(),
3717                     nw = bounds.getNorthWest(),
3718                     se = bounds.getSouthEast(),
3719                     size = this.getSize().subtract(padding),
3720                     boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
3721                     snap = any3d ? this.options.zoomSnap : 1,
3722                     scalex = size.x / boundsSize.x,
3723                     scaley = size.y / boundsSize.y,
3724                     scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
3725
3726                 zoom = this.getScaleZoom(scale, zoom);
3727
3728                 if (snap) {
3729                         zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
3730                         zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
3731                 }
3732
3733                 return Math.max(min, Math.min(max, zoom));
3734         },
3735
3736         // @method getSize(): Point
3737         // Returns the current size of the map container (in pixels).
3738         getSize: function () {
3739                 if (!this._size || this._sizeChanged) {
3740                         this._size = new Point(
3741                                 this._container.clientWidth || 0,
3742                                 this._container.clientHeight || 0);
3743
3744                         this._sizeChanged = false;
3745                 }
3746                 return this._size.clone();
3747         },
3748
3749         // @method getPixelBounds(): Bounds
3750         // Returns the bounds of the current map view in projected pixel
3751         // coordinates (sometimes useful in layer and overlay implementations).
3752         getPixelBounds: function (center, zoom) {
3753                 var topLeftPoint = this._getTopLeftPoint(center, zoom);
3754                 return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
3755         },
3756
3757         // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
3758         // the map pane? "left point of the map layer" can be confusing, specially
3759         // since there can be negative offsets.
3760         // @method getPixelOrigin(): Point
3761         // Returns the projected pixel coordinates of the top left point of
3762         // the map layer (useful in custom layer and overlay implementations).
3763         getPixelOrigin: function () {
3764                 this._checkIfLoaded();
3765                 return this._pixelOrigin;
3766         },
3767
3768         // @method getPixelWorldBounds(zoom?: Number): Bounds
3769         // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
3770         // If `zoom` is omitted, the map's current zoom level is used.
3771         getPixelWorldBounds: function (zoom) {
3772                 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
3773         },
3774
3775         // @section Other Methods
3776
3777         // @method getPane(pane: String|HTMLElement): HTMLElement
3778         // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
3779         getPane: function (pane) {
3780                 return typeof pane === 'string' ? this._panes[pane] : pane;
3781         },
3782
3783         // @method getPanes(): Object
3784         // Returns a plain object containing the names of all [panes](#map-pane) as keys and
3785         // the panes as values.
3786         getPanes: function () {
3787                 return this._panes;
3788         },
3789
3790         // @method getContainer: HTMLElement
3791         // Returns the HTML element that contains the map.
3792         getContainer: function () {
3793                 return this._container;
3794         },
3795
3796
3797         // @section Conversion Methods
3798
3799         // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
3800         // Returns the scale factor to be applied to a map transition from zoom level
3801         // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
3802         getZoomScale: function (toZoom, fromZoom) {
3803                 // TODO replace with universal implementation after refactoring projections
3804                 var crs = this.options.crs;
3805                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3806                 return crs.scale(toZoom) / crs.scale(fromZoom);
3807         },
3808
3809         // @method getScaleZoom(scale: Number, fromZoom: Number): Number
3810         // Returns the zoom level that the map would end up at, if it is at `fromZoom`
3811         // level and everything is scaled by a factor of `scale`. Inverse of
3812         // [`getZoomScale`](#map-getZoomScale).
3813         getScaleZoom: function (scale, fromZoom) {
3814                 var crs = this.options.crs;
3815                 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3816                 var zoom = crs.zoom(scale * crs.scale(fromZoom));
3817                 return isNaN(zoom) ? Infinity : zoom;
3818         },
3819
3820         // @method project(latlng: LatLng, zoom: Number): Point
3821         // Projects a geographical coordinate `LatLng` according to the projection
3822         // of the map's CRS, then scales it according to `zoom` and the CRS's
3823         // `Transformation`. The result is pixel coordinate relative to
3824         // the CRS origin.
3825         project: function (latlng, zoom) {
3826                 zoom = zoom === undefined ? this._zoom : zoom;
3827                 return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
3828         },
3829
3830         // @method unproject(point: Point, zoom: Number): LatLng
3831         // Inverse of [`project`](#map-project).
3832         unproject: function (point, zoom) {
3833                 zoom = zoom === undefined ? this._zoom : zoom;
3834                 return this.options.crs.pointToLatLng(toPoint(point), zoom);
3835         },
3836
3837         // @method layerPointToLatLng(point: Point): LatLng
3838         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3839         // returns the corresponding geographical coordinate (for the current zoom level).
3840         layerPointToLatLng: function (point) {
3841                 var projectedPoint = toPoint(point).add(this.getPixelOrigin());
3842                 return this.unproject(projectedPoint);
3843         },
3844
3845         // @method latLngToLayerPoint(latlng: LatLng): Point
3846         // Given a geographical coordinate, returns the corresponding pixel coordinate
3847         // relative to the [origin pixel](#map-getpixelorigin).
3848         latLngToLayerPoint: function (latlng) {
3849                 var projectedPoint = this.project(toLatLng(latlng))._round();
3850                 return projectedPoint._subtract(this.getPixelOrigin());
3851         },
3852
3853         // @method wrapLatLng(latlng: LatLng): LatLng
3854         // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
3855         // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
3856         // CRS's bounds.
3857         // By default this means longitude is wrapped around the dateline so its
3858         // value is between -180 and +180 degrees.
3859         wrapLatLng: function (latlng) {
3860                 return this.options.crs.wrapLatLng(toLatLng(latlng));
3861         },
3862
3863         // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
3864         // Returns a `LatLngBounds` with the same size as the given one, ensuring that
3865         // its center is within the CRS's bounds.
3866         // By default this means the center longitude is wrapped around the dateline so its
3867         // value is between -180 and +180 degrees, and the majority of the bounds
3868         // overlaps the CRS's bounds.
3869         wrapLatLngBounds: function (latlng) {
3870                 return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
3871         },
3872
3873         // @method distance(latlng1: LatLng, latlng2: LatLng): Number
3874         // Returns the distance between two geographical coordinates according to
3875         // the map's CRS. By default this measures distance in meters.
3876         distance: function (latlng1, latlng2) {
3877                 return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
3878         },
3879
3880         // @method containerPointToLayerPoint(point: Point): Point
3881         // Given a pixel coordinate relative to the map container, returns the corresponding
3882         // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
3883         containerPointToLayerPoint: function (point) { // (Point)
3884                 return toPoint(point).subtract(this._getMapPanePos());
3885         },
3886
3887         // @method layerPointToContainerPoint(point: Point): Point
3888         // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3889         // returns the corresponding pixel coordinate relative to the map container.
3890         layerPointToContainerPoint: function (point) { // (Point)
3891                 return toPoint(point).add(this._getMapPanePos());
3892         },
3893
3894         // @method containerPointToLatLng(point: Point): LatLng
3895         // Given a pixel coordinate relative to the map container, returns
3896         // the corresponding geographical coordinate (for the current zoom level).
3897         containerPointToLatLng: function (point) {
3898                 var layerPoint = this.containerPointToLayerPoint(toPoint(point));
3899                 return this.layerPointToLatLng(layerPoint);
3900         },
3901
3902         // @method latLngToContainerPoint(latlng: LatLng): Point
3903         // Given a geographical coordinate, returns the corresponding pixel coordinate
3904         // relative to the map container.
3905         latLngToContainerPoint: function (latlng) {
3906                 return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
3907         },
3908
3909         // @method mouseEventToContainerPoint(ev: MouseEvent): Point
3910         // Given a MouseEvent object, returns the pixel coordinate relative to the
3911         // map container where the event took place.
3912         mouseEventToContainerPoint: function (e) {
3913                 return getMousePosition(e, this._container);
3914         },
3915
3916         // @method mouseEventToLayerPoint(ev: MouseEvent): Point
3917         // Given a MouseEvent object, returns the pixel coordinate relative to
3918         // the [origin pixel](#map-getpixelorigin) where the event took place.
3919         mouseEventToLayerPoint: function (e) {
3920                 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
3921         },
3922
3923         // @method mouseEventToLatLng(ev: MouseEvent): LatLng
3924         // Given a MouseEvent object, returns geographical coordinate where the
3925         // event took place.
3926         mouseEventToLatLng: function (e) { // (MouseEvent)
3927                 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
3928         },
3929
3930
3931         // map initialization methods
3932
3933         _initContainer: function (id) {
3934                 var container = this._container = get(id);
3935
3936                 if (!container) {
3937                         throw new Error('Map container not found.');
3938                 } else if (container._leaflet_id) {
3939                         throw new Error('Map container is already initialized.');
3940                 }
3941
3942                 on(container, 'scroll', this._onScroll, this);
3943                 this._containerId = stamp(container);
3944         },
3945
3946         _initLayout: function () {
3947                 var container = this._container;
3948
3949                 this._fadeAnimated = this.options.fadeAnimation && any3d;
3950
3951                 addClass(container, 'leaflet-container' +
3952                         (touch ? ' leaflet-touch' : '') +
3953                         (retina ? ' leaflet-retina' : '') +
3954                         (ielt9 ? ' leaflet-oldie' : '') +
3955                         (safari ? ' leaflet-safari' : '') +
3956                         (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
3957
3958                 var position = getStyle(container, 'position');
3959
3960                 if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
3961                         container.style.position = 'relative';
3962                 }
3963
3964                 this._initPanes();
3965
3966                 if (this._initControlPos) {
3967                         this._initControlPos();
3968                 }
3969         },
3970
3971         _initPanes: function () {
3972                 var panes = this._panes = {};
3973                 this._paneRenderers = {};
3974
3975                 // @section
3976                 //
3977                 // Panes are DOM elements used to control the ordering of layers on the map. You
3978                 // can access panes with [`map.getPane`](#map-getpane) or
3979                 // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
3980                 // [`map.createPane`](#map-createpane) method.
3981                 //
3982                 // Every map has the following default panes that differ only in zIndex.
3983                 //
3984                 // @pane mapPane: HTMLElement = 'auto'
3985                 // Pane that contains all other map panes
3986
3987                 this._mapPane = this.createPane('mapPane', this._container);
3988                 setPosition(this._mapPane, new Point(0, 0));
3989
3990                 // @pane tilePane: HTMLElement = 200
3991                 // Pane for `GridLayer`s and `TileLayer`s
3992                 this.createPane('tilePane');
3993                 // @pane overlayPane: HTMLElement = 400
3994                 // Pane for vector overlays (`Path`s), like `Polyline`s and `Polygon`s
3995                 this.createPane('shadowPane');
3996                 // @pane shadowPane: HTMLElement = 500
3997                 // Pane for overlay shadows (e.g. `Marker` shadows)
3998                 this.createPane('overlayPane');
3999                 // @pane markerPane: HTMLElement = 600
4000                 // Pane for `Icon`s of `Marker`s
4001                 this.createPane('markerPane');
4002                 // @pane tooltipPane: HTMLElement = 650
4003                 // Pane for tooltip.
4004                 this.createPane('tooltipPane');
4005                 // @pane popupPane: HTMLElement = 700
4006                 // Pane for `Popup`s.
4007                 this.createPane('popupPane');
4008
4009                 if (!this.options.markerZoomAnimation) {
4010                         addClass(panes.markerPane, 'leaflet-zoom-hide');
4011                         addClass(panes.shadowPane, 'leaflet-zoom-hide');
4012                 }
4013         },
4014
4015
4016         // private methods that modify map state
4017
4018         // @section Map state change events
4019         _resetView: function (center, zoom) {
4020                 setPosition(this._mapPane, new Point(0, 0));
4021
4022                 var loading = !this._loaded;
4023                 this._loaded = true;
4024                 zoom = this._limitZoom(zoom);
4025
4026                 this.fire('viewprereset');
4027
4028                 var zoomChanged = this._zoom !== zoom;
4029                 this
4030                         ._moveStart(zoomChanged)
4031                         ._move(center, zoom)
4032                         ._moveEnd(zoomChanged);
4033
4034                 // @event viewreset: Event
4035                 // Fired when the map needs to redraw its content (this usually happens
4036                 // on map zoom or load). Very useful for creating custom overlays.
4037                 this.fire('viewreset');
4038
4039                 // @event load: Event
4040                 // Fired when the map is initialized (when its center and zoom are set
4041                 // for the first time).
4042                 if (loading) {
4043                         this.fire('load');
4044                 }
4045         },
4046
4047         _moveStart: function (zoomChanged) {
4048                 // @event zoomstart: Event
4049                 // Fired when the map zoom is about to change (e.g. before zoom animation).
4050                 // @event movestart: Event
4051                 // Fired when the view of the map starts changing (e.g. user starts dragging the map).
4052                 if (zoomChanged) {
4053                         this.fire('zoomstart');
4054                 }
4055                 return this.fire('movestart');
4056         },
4057
4058         _move: function (center, zoom, data) {
4059                 if (zoom === undefined) {
4060                         zoom = this._zoom;
4061                 }
4062                 var zoomChanged = this._zoom !== zoom;
4063
4064                 this._zoom = zoom;
4065                 this._lastCenter = center;
4066                 this._pixelOrigin = this._getNewPixelOrigin(center);
4067
4068                 // @event zoom: Event
4069                 // Fired repeatedly during any change in zoom level, including zoom
4070                 // and fly animations.
4071                 if (zoomChanged || (data && data.pinch)) {      // Always fire 'zoom' if pinching because #3530
4072                         this.fire('zoom', data);
4073                 }
4074
4075                 // @event move: Event
4076                 // Fired repeatedly during any movement of the map, including pan and
4077                 // fly animations.
4078                 return this.fire('move', data);
4079         },
4080
4081         _moveEnd: function (zoomChanged) {
4082                 // @event zoomend: Event
4083                 // Fired when the map has changed, after any animations.
4084                 if (zoomChanged) {
4085                         this.fire('zoomend');
4086                 }
4087
4088                 // @event moveend: Event
4089                 // Fired when the center of the map stops changing (e.g. user stopped
4090                 // dragging the map).
4091                 return this.fire('moveend');
4092         },
4093
4094         _stop: function () {
4095                 cancelAnimFrame(this._flyToFrame);
4096                 if (this._panAnim) {
4097                         this._panAnim.stop();
4098                 }
4099                 return this;
4100         },
4101
4102         _rawPanBy: function (offset) {
4103                 setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
4104         },
4105
4106         _getZoomSpan: function () {
4107                 return this.getMaxZoom() - this.getMinZoom();
4108         },
4109
4110         _panInsideMaxBounds: function () {
4111                 if (!this._enforcingBounds) {
4112                         this.panInsideBounds(this.options.maxBounds);
4113                 }
4114         },
4115
4116         _checkIfLoaded: function () {
4117                 if (!this._loaded) {
4118                         throw new Error('Set map center and zoom first.');
4119                 }
4120         },
4121
4122         // DOM event handling
4123
4124         // @section Interaction events
4125         _initEvents: function (remove$$1) {
4126                 this._targets = {};
4127                 this._targets[stamp(this._container)] = this;
4128
4129                 var onOff = remove$$1 ? off : on;
4130
4131                 // @event click: MouseEvent
4132                 // Fired when the user clicks (or taps) the map.
4133                 // @event dblclick: MouseEvent
4134                 // Fired when the user double-clicks (or double-taps) the map.
4135                 // @event mousedown: MouseEvent
4136                 // Fired when the user pushes the mouse button on the map.
4137                 // @event mouseup: MouseEvent
4138                 // Fired when the user releases the mouse button on the map.
4139                 // @event mouseover: MouseEvent
4140                 // Fired when the mouse enters the map.
4141                 // @event mouseout: MouseEvent
4142                 // Fired when the mouse leaves the map.
4143                 // @event mousemove: MouseEvent
4144                 // Fired while the mouse moves over the map.
4145                 // @event contextmenu: MouseEvent
4146                 // Fired when the user pushes the right mouse button on the map, prevents
4147                 // default browser context menu from showing if there are listeners on
4148                 // this event. Also fired on mobile when the user holds a single touch
4149                 // for a second (also called long press).
4150                 // @event keypress: KeyboardEvent
4151                 // Fired when the user presses a key from the keyboard while the map is focused.
4152                 onOff(this._container, 'click dblclick mousedown mouseup ' +
4153                         'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
4154
4155                 if (this.options.trackResize) {
4156                         onOff(window, 'resize', this._onResize, this);
4157                 }
4158
4159                 if (any3d && this.options.transform3DLimit) {
4160                         (remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
4161                 }
4162         },
4163
4164         _onResize: function () {
4165                 cancelAnimFrame(this._resizeRequest);
4166                 this._resizeRequest = requestAnimFrame(
4167                         function () { this.invalidateSize({debounceMoveend: true}); }, this);
4168         },
4169
4170         _onScroll: function () {
4171                 this._container.scrollTop  = 0;
4172                 this._container.scrollLeft = 0;
4173         },
4174
4175         _onMoveEnd: function () {
4176                 var pos = this._getMapPanePos();
4177                 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
4178                         // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
4179                         // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
4180                         this._resetView(this.getCenter(), this.getZoom());
4181                 }
4182         },
4183
4184         _findEventTargets: function (e, type) {
4185                 var targets = [],
4186                     target,
4187                     isHover = type === 'mouseout' || type === 'mouseover',
4188                     src = e.target || e.srcElement,
4189                     dragging = false;
4190
4191                 while (src) {
4192                         target = this._targets[stamp(src)];
4193                         if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
4194                                 // Prevent firing click after you just dragged an object.
4195                                 dragging = true;
4196                                 break;
4197                         }
4198                         if (target && target.listens(type, true)) {
4199                                 if (isHover && !isExternalTarget(src, e)) { break; }
4200                                 targets.push(target);
4201                                 if (isHover) { break; }
4202                         }
4203                         if (src === this._container) { break; }
4204                         src = src.parentNode;
4205                 }
4206                 if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) {
4207                         targets = [this];
4208                 }
4209                 return targets;
4210         },
4211
4212         _handleDOMEvent: function (e) {
4213                 if (!this._loaded || skipped(e)) { return; }
4214
4215                 var type = e.type;
4216
4217                 if (type === 'mousedown' || type === 'keypress') {
4218                         // prevents outline when clicking on keyboard-focusable element
4219                         preventOutline(e.target || e.srcElement);
4220                 }
4221
4222                 this._fireDOMEvent(e, type);
4223         },
4224
4225         _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
4226
4227         _fireDOMEvent: function (e, type, targets) {
4228
4229                 if (e.type === 'click') {
4230                         // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
4231                         // @event preclick: MouseEvent
4232                         // Fired before mouse click on the map (sometimes useful when you
4233                         // want something to happen on click before any existing click
4234                         // handlers start running).
4235                         var synth = extend({}, e);
4236                         synth.type = 'preclick';
4237                         this._fireDOMEvent(synth, synth.type, targets);
4238                 }
4239
4240                 if (e._stopped) { return; }
4241
4242                 // Find the layer the event is propagating from and its parents.
4243                 targets = (targets || []).concat(this._findEventTargets(e, type));
4244
4245                 if (!targets.length) { return; }
4246
4247                 var target = targets[0];
4248                 if (type === 'contextmenu' && target.listens(type, true)) {
4249                         preventDefault(e);
4250                 }
4251
4252                 var data = {
4253                         originalEvent: e
4254                 };
4255
4256                 if (e.type !== 'keypress') {
4257                         var isMarker = (target.options && 'icon' in target.options);
4258                         data.containerPoint = isMarker ?
4259                                         this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
4260                         data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
4261                         data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
4262                 }
4263
4264                 for (var i = 0; i < targets.length; i++) {
4265                         targets[i].fire(type, data, true);
4266                         if (data.originalEvent._stopped ||
4267                                 (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
4268                 }
4269         },
4270
4271         _draggableMoved: function (obj) {
4272                 obj = obj.dragging && obj.dragging.enabled() ? obj : this;
4273                 return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
4274         },
4275
4276         _clearHandlers: function () {
4277                 for (var i = 0, len = this._handlers.length; i < len; i++) {
4278                         this._handlers[i].disable();
4279                 }
4280         },
4281
4282         // @section Other Methods
4283
4284         // @method whenReady(fn: Function, context?: Object): this
4285         // Runs the given function `fn` when the map gets initialized with
4286         // a view (center and zoom) and at least one layer, or immediately
4287         // if it's already initialized, optionally passing a function context.
4288         whenReady: function (callback, context) {
4289                 if (this._loaded) {
4290                         callback.call(context || this, {target: this});
4291                 } else {
4292                         this.on('load', callback, context);
4293                 }
4294                 return this;
4295         },
4296
4297
4298         // private methods for getting map state
4299
4300         _getMapPanePos: function () {
4301                 return getPosition(this._mapPane) || new Point(0, 0);
4302         },
4303
4304         _moved: function () {
4305                 var pos = this._getMapPanePos();
4306                 return pos && !pos.equals([0, 0]);
4307         },
4308
4309         _getTopLeftPoint: function (center, zoom) {
4310                 var pixelOrigin = center && zoom !== undefined ?
4311                         this._getNewPixelOrigin(center, zoom) :
4312                         this.getPixelOrigin();
4313                 return pixelOrigin.subtract(this._getMapPanePos());
4314         },
4315
4316         _getNewPixelOrigin: function (center, zoom) {
4317                 var viewHalf = this.getSize()._divideBy(2);
4318                 return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
4319         },
4320
4321         _latLngToNewLayerPoint: function (latlng, zoom, center) {
4322                 var topLeft = this._getNewPixelOrigin(center, zoom);
4323                 return this.project(latlng, zoom)._subtract(topLeft);
4324         },
4325
4326         _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
4327                 var topLeft = this._getNewPixelOrigin(center, zoom);
4328                 return toBounds([
4329                         this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
4330                         this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
4331                         this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
4332                         this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
4333                 ]);
4334         },
4335
4336         // layer point of the current center
4337         _getCenterLayerPoint: function () {
4338                 return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
4339         },
4340
4341         // offset of the specified place to the current center in pixels
4342         _getCenterOffset: function (latlng) {
4343                 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
4344         },
4345
4346         // adjust center for view to get inside bounds
4347         _limitCenter: function (center, zoom, bounds) {
4348
4349                 if (!bounds) { return center; }
4350
4351                 var centerPoint = this.project(center, zoom),
4352                     viewHalf = this.getSize().divideBy(2),
4353                     viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
4354                     offset = this._getBoundsOffset(viewBounds, bounds, zoom);
4355
4356                 // If offset is less than a pixel, ignore.
4357                 // This prevents unstable projections from getting into
4358                 // an infinite loop of tiny offsets.
4359                 if (offset.round().equals([0, 0])) {
4360                         return center;
4361                 }
4362
4363                 return this.unproject(centerPoint.add(offset), zoom);
4364         },
4365
4366         // adjust offset for view to get inside bounds
4367         _limitOffset: function (offset, bounds) {
4368                 if (!bounds) { return offset; }
4369
4370                 var viewBounds = this.getPixelBounds(),
4371                     newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
4372
4373                 return offset.add(this._getBoundsOffset(newBounds, bounds));
4374         },
4375
4376         // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
4377         _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
4378                 var projectedMaxBounds = toBounds(
4379                         this.project(maxBounds.getNorthEast(), zoom),
4380                         this.project(maxBounds.getSouthWest(), zoom)
4381                     ),
4382                     minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
4383                     maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
4384
4385                     dx = this._rebound(minOffset.x, -maxOffset.x),
4386                     dy = this._rebound(minOffset.y, -maxOffset.y);
4387
4388                 return new Point(dx, dy);
4389         },
4390
4391         _rebound: function (left, right) {
4392                 return left + right > 0 ?
4393                         Math.round(left - right) / 2 :
4394                         Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
4395         },
4396
4397         _limitZoom: function (zoom) {
4398                 var min = this.getMinZoom(),
4399                     max = this.getMaxZoom(),
4400                     snap = any3d ? this.options.zoomSnap : 1;
4401                 if (snap) {
4402                         zoom = Math.round(zoom / snap) * snap;
4403                 }
4404                 return Math.max(min, Math.min(max, zoom));
4405         },
4406
4407         _onPanTransitionStep: function () {
4408                 this.fire('move');
4409         },
4410
4411         _onPanTransitionEnd: function () {
4412                 removeClass(this._mapPane, 'leaflet-pan-anim');
4413                 this.fire('moveend');
4414         },
4415
4416         _tryAnimatedPan: function (center, options) {
4417                 // difference between the new and current centers in pixels
4418                 var offset = this._getCenterOffset(center)._floor();
4419
4420                 // don't animate too far unless animate: true specified in options
4421                 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
4422
4423                 this.panBy(offset, options);
4424
4425                 return true;
4426         },
4427
4428         _createAnimProxy: function () {
4429
4430                 var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
4431                 this._panes.mapPane.appendChild(proxy);
4432
4433                 this.on('zoomanim', function (e) {
4434                         var prop = TRANSFORM,
4435                             transform = this._proxy.style[prop];
4436
4437                         setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
4438
4439                         // workaround for case when transform is the same and so transitionend event is not fired
4440                         if (transform === this._proxy.style[prop] && this._animatingZoom) {
4441                                 this._onZoomTransitionEnd();
4442                         }
4443                 }, this);
4444
4445                 this.on('load moveend', function () {
4446                         var c = this.getCenter(),
4447                             z = this.getZoom();
4448                         setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
4449                 }, this);
4450
4451                 this._on('unload', this._destroyAnimProxy, this);
4452         },
4453
4454         _destroyAnimProxy: function () {
4455                 remove(this._proxy);
4456                 delete this._proxy;
4457         },
4458
4459         _catchTransitionEnd: function (e) {
4460                 if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
4461                         this._onZoomTransitionEnd();
4462                 }
4463         },
4464
4465         _nothingToAnimate: function () {
4466                 return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
4467         },
4468
4469         _tryAnimatedZoom: function (center, zoom, options) {
4470
4471                 if (this._animatingZoom) { return true; }
4472
4473                 options = options || {};
4474
4475                 // don't animate if disabled, not supported or zoom difference is too large
4476                 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
4477                         Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
4478
4479                 // offset is the pixel coords of the zoom origin relative to the current center
4480                 var scale = this.getZoomScale(zoom),
4481                     offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
4482
4483                 // don't animate if the zoom origin isn't within one screen from the current center, unless forced
4484                 if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
4485
4486                 requestAnimFrame(function () {
4487                         this
4488                             ._moveStart(true)
4489                             ._animateZoom(center, zoom, true);
4490                 }, this);
4491
4492                 return true;
4493         },
4494
4495         _animateZoom: function (center, zoom, startAnim, noUpdate) {
4496                 if (startAnim) {
4497                         this._animatingZoom = true;
4498
4499                         // remember what center/zoom to set after animation
4500                         this._animateToCenter = center;
4501                         this._animateToZoom = zoom;
4502
4503                         addClass(this._mapPane, 'leaflet-zoom-anim');
4504                 }
4505
4506                 // @event zoomanim: ZoomAnimEvent
4507                 // Fired on every frame of a zoom animation
4508                 this.fire('zoomanim', {
4509                         center: center,
4510                         zoom: zoom,
4511                         noUpdate: noUpdate
4512                 });
4513
4514                 // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
4515                 setTimeout(bind(this._onZoomTransitionEnd, this), 250);
4516         },
4517
4518         _onZoomTransitionEnd: function () {
4519                 if (!this._animatingZoom) { return; }
4520
4521                 removeClass(this._mapPane, 'leaflet-zoom-anim');
4522
4523                 this._animatingZoom = false;
4524
4525                 this._move(this._animateToCenter, this._animateToZoom);
4526
4527                 // This anim frame should prevent an obscure iOS webkit tile loading race condition.
4528                 requestAnimFrame(function () {
4529                         this._moveEnd(true);
4530                 }, this);
4531         }
4532 });
4533
4534 // @section
4535
4536 // @factory L.map(id: String, options?: Map options)
4537 // Instantiates a map object given the DOM ID of a `<div>` element
4538 // and optionally an object literal with `Map options`.
4539 //
4540 // @alternative
4541 // @factory L.map(el: HTMLElement, options?: Map options)
4542 // Instantiates a map object given an instance of a `<div>` HTML element
4543 // and optionally an object literal with `Map options`.
4544 function createMap(id, options) {
4545         return new Map(id, options);
4546 }
4547
4548 /*
4549  * @class Control
4550  * @aka L.Control
4551  * @inherits Class
4552  *
4553  * L.Control is a base class for implementing map controls. Handles positioning.
4554  * All other controls extend from this class.
4555  */
4556
4557 var Control = Class.extend({
4558         // @section
4559         // @aka Control options
4560         options: {
4561                 // @option position: String = 'topright'
4562                 // The position of the control (one of the map corners). Possible values are `'topleft'`,
4563                 // `'topright'`, `'bottomleft'` or `'bottomright'`
4564                 position: 'topright'
4565         },
4566
4567         initialize: function (options) {
4568                 setOptions(this, options);
4569         },
4570
4571         /* @section
4572          * Classes extending L.Control will inherit the following methods:
4573          *
4574          * @method getPosition: string
4575          * Returns the position of the control.
4576          */
4577         getPosition: function () {
4578                 return this.options.position;
4579         },
4580
4581         // @method setPosition(position: string): this
4582         // Sets the position of the control.
4583         setPosition: function (position) {
4584                 var map = this._map;
4585
4586                 if (map) {
4587                         map.removeControl(this);
4588                 }
4589
4590                 this.options.position = position;
4591
4592                 if (map) {
4593                         map.addControl(this);
4594                 }
4595
4596                 return this;
4597         },
4598
4599         // @method getContainer: HTMLElement
4600         // Returns the HTMLElement that contains the control.
4601         getContainer: function () {
4602                 return this._container;
4603         },
4604
4605         // @method addTo(map: Map): this
4606         // Adds the control to the given map.
4607         addTo: function (map) {
4608                 this.remove();
4609                 this._map = map;
4610
4611                 var container = this._container = this.onAdd(map),
4612                     pos = this.getPosition(),
4613                     corner = map._controlCorners[pos];
4614
4615                 addClass(container, 'leaflet-control');
4616
4617                 if (pos.indexOf('bottom') !== -1) {
4618                         corner.insertBefore(container, corner.firstChild);
4619                 } else {
4620                         corner.appendChild(container);
4621                 }
4622
4623                 return this;
4624         },
4625
4626         // @method remove: this
4627         // Removes the control from the map it is currently active on.
4628         remove: function () {
4629                 if (!this._map) {
4630                         return this;
4631                 }
4632
4633                 remove(this._container);
4634
4635                 if (this.onRemove) {
4636                         this.onRemove(this._map);
4637                 }
4638
4639                 this._map = null;
4640
4641                 return this;
4642         },
4643
4644         _refocusOnMap: function (e) {
4645                 // if map exists and event is not a keyboard event
4646                 if (this._map && e && e.screenX > 0 && e.screenY > 0) {
4647                         this._map.getContainer().focus();
4648                 }
4649         }
4650 });
4651
4652 var control = function (options) {
4653         return new Control(options);
4654 };
4655
4656 /* @section Extension methods
4657  * @uninheritable
4658  *
4659  * Every control should extend from `L.Control` and (re-)implement the following methods.
4660  *
4661  * @method onAdd(map: Map): HTMLElement
4662  * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
4663  *
4664  * @method onRemove(map: Map)
4665  * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
4666  */
4667
4668 /* @namespace Map
4669  * @section Methods for Layers and Controls
4670  */
4671 Map.include({
4672         // @method addControl(control: Control): this
4673         // Adds the given control to the map
4674         addControl: function (control) {
4675                 control.addTo(this);
4676                 return this;
4677         },
4678
4679         // @method removeControl(control: Control): this
4680         // Removes the given control from the map
4681         removeControl: function (control) {
4682                 control.remove();
4683                 return this;
4684         },
4685
4686         _initControlPos: function () {
4687                 var corners = this._controlCorners = {},
4688                     l = 'leaflet-',
4689                     container = this._controlContainer =
4690                             create$1('div', l + 'control-container', this._container);
4691
4692                 function createCorner(vSide, hSide) {
4693                         var className = l + vSide + ' ' + l + hSide;
4694
4695                         corners[vSide + hSide] = create$1('div', className, container);
4696                 }
4697
4698                 createCorner('top', 'left');
4699                 createCorner('top', 'right');
4700                 createCorner('bottom', 'left');
4701                 createCorner('bottom', 'right');
4702         },
4703
4704         _clearControlPos: function () {
4705                 for (var i in this._controlCorners) {
4706                         remove(this._controlCorners[i]);
4707                 }
4708                 remove(this._controlContainer);
4709                 delete this._controlCorners;
4710                 delete this._controlContainer;
4711         }
4712 });
4713
4714 /*
4715  * @class Control.Layers
4716  * @aka L.Control.Layers
4717  * @inherits Control
4718  *
4719  * 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`.
4720  *
4721  * @example
4722  *
4723  * ```js
4724  * var baseLayers = {
4725  *      "Mapbox": mapbox,
4726  *      "OpenStreetMap": osm
4727  * };
4728  *
4729  * var overlays = {
4730  *      "Marker": marker,
4731  *      "Roads": roadsLayer
4732  * };
4733  *
4734  * L.control.layers(baseLayers, overlays).addTo(map);
4735  * ```
4736  *
4737  * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
4738  *
4739  * ```js
4740  * {
4741  *     "<someName1>": layer1,
4742  *     "<someName2>": layer2
4743  * }
4744  * ```
4745  *
4746  * The layer names can contain HTML, which allows you to add additional styling to the items:
4747  *
4748  * ```js
4749  * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
4750  * ```
4751  */
4752
4753 var Layers = Control.extend({
4754         // @section
4755         // @aka Control.Layers options
4756         options: {
4757                 // @option collapsed: Boolean = true
4758                 // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
4759                 collapsed: true,
4760                 position: 'topright',
4761
4762                 // @option autoZIndex: Boolean = true
4763                 // 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.
4764                 autoZIndex: true,
4765
4766                 // @option hideSingleBase: Boolean = false
4767                 // If `true`, the base layers in the control will be hidden when there is only one.
4768                 hideSingleBase: false,
4769
4770                 // @option sortLayers: Boolean = false
4771                 // Whether to sort the layers. When `false`, layers will keep the order
4772                 // in which they were added to the control.
4773                 sortLayers: false,
4774
4775                 // @option sortFunction: Function = *
4776                 // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
4777                 // that will be used for sorting the layers, when `sortLayers` is `true`.
4778                 // The function receives both the `L.Layer` instances and their names, as in
4779                 // `sortFunction(layerA, layerB, nameA, nameB)`.
4780                 // By default, it sorts layers alphabetically by their name.
4781                 sortFunction: function (layerA, layerB, nameA, nameB) {
4782                         return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
4783                 }
4784         },
4785
4786         initialize: function (baseLayers, overlays, options) {
4787                 setOptions(this, options);
4788
4789                 this._layerControlInputs = [];
4790                 this._layers = [];
4791                 this._lastZIndex = 0;
4792                 this._handlingClick = false;
4793
4794                 for (var i in baseLayers) {
4795                         this._addLayer(baseLayers[i], i);
4796                 }
4797
4798                 for (i in overlays) {
4799                         this._addLayer(overlays[i], i, true);
4800                 }
4801         },
4802
4803         onAdd: function (map) {
4804                 this._initLayout();
4805                 this._update();
4806
4807                 this._map = map;
4808                 map.on('zoomend', this._checkDisabledLayers, this);
4809
4810                 for (var i = 0; i < this._layers.length; i++) {
4811                         this._layers[i].layer.on('add remove', this._onLayerChange, this);
4812                 }
4813
4814                 return this._container;
4815         },
4816
4817         addTo: function (map) {
4818                 Control.prototype.addTo.call(this, map);
4819                 // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
4820                 return this._expandIfNotCollapsed();
4821         },
4822
4823         onRemove: function () {
4824                 this._map.off('zoomend', this._checkDisabledLayers, this);
4825
4826                 for (var i = 0; i < this._layers.length; i++) {
4827                         this._layers[i].layer.off('add remove', this._onLayerChange, this);
4828                 }
4829         },
4830
4831         // @method addBaseLayer(layer: Layer, name: String): this
4832         // Adds a base layer (radio button entry) with the given name to the control.
4833         addBaseLayer: function (layer, name) {
4834                 this._addLayer(layer, name);
4835                 return (this._map) ? this._update() : this;
4836         },
4837
4838         // @method addOverlay(layer: Layer, name: String): this
4839         // Adds an overlay (checkbox entry) with the given name to the control.
4840         addOverlay: function (layer, name) {
4841                 this._addLayer(layer, name, true);
4842                 return (this._map) ? this._update() : this;
4843         },
4844
4845         // @method removeLayer(layer: Layer): this
4846         // Remove the given layer from the control.
4847         removeLayer: function (layer) {
4848                 layer.off('add remove', this._onLayerChange, this);
4849
4850                 var obj = this._getLayer(stamp(layer));
4851                 if (obj) {
4852                         this._layers.splice(this._layers.indexOf(obj), 1);
4853                 }
4854                 return (this._map) ? this._update() : this;
4855         },
4856
4857         // @method expand(): this
4858         // Expand the control container if collapsed.
4859         expand: function () {
4860                 addClass(this._container, 'leaflet-control-layers-expanded');
4861                 this._form.style.height = null;
4862                 var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
4863                 if (acceptableHeight < this._form.clientHeight) {
4864                         addClass(this._form, 'leaflet-control-layers-scrollbar');
4865                         this._form.style.height = acceptableHeight + 'px';
4866                 } else {
4867                         removeClass(this._form, 'leaflet-control-layers-scrollbar');
4868                 }
4869                 this._checkDisabledLayers();
4870                 return this;
4871         },
4872
4873         // @method collapse(): this
4874         // Collapse the control container if expanded.
4875         collapse: function () {
4876                 removeClass(this._container, 'leaflet-control-layers-expanded');
4877                 return this;
4878         },
4879
4880         _initLayout: function () {
4881                 var className = 'leaflet-control-layers',
4882                     container = this._container = create$1('div', className),
4883                     collapsed = this.options.collapsed;
4884
4885                 // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
4886                 container.setAttribute('aria-haspopup', true);
4887
4888                 disableClickPropagation(container);
4889                 disableScrollPropagation(container);
4890
4891                 var form = this._form = create$1('form', className + '-list');
4892
4893                 if (collapsed) {
4894                         this._map.on('click', this.collapse, this);
4895
4896                         if (!android) {
4897                                 on(container, {
4898                                         mouseenter: this.expand,
4899                                         mouseleave: this.collapse
4900                                 }, this);
4901                         }
4902                 }
4903
4904                 var link = this._layersLink = create$1('a', className + '-toggle', container);
4905                 link.href = '#';
4906                 link.title = 'Layers';
4907
4908                 if (touch) {
4909                         on(link, 'click', stop);
4910                         on(link, 'click', this.expand, this);
4911                 } else {
4912                         on(link, 'focus', this.expand, this);
4913                 }
4914
4915                 // work around for Firefox Android issue https://github.com/Leaflet/Leaflet/issues/2033
4916                 on(form, 'click', function () {
4917                         setTimeout(bind(this._onInputClick, this), 0);
4918                 }, this);
4919
4920                 // TODO keyboard accessibility
4921
4922                 if (!collapsed) {
4923                         this.expand();
4924                 }
4925
4926                 this._baseLayersList = create$1('div', className + '-base', form);
4927                 this._separator = create$1('div', className + '-separator', form);
4928                 this._overlaysList = create$1('div', className + '-overlays', form);
4929
4930                 container.appendChild(form);
4931         },
4932
4933         _getLayer: function (id) {
4934                 for (var i = 0; i < this._layers.length; i++) {
4935
4936                         if (this._layers[i] && stamp(this._layers[i].layer) === id) {
4937                                 return this._layers[i];
4938                         }
4939                 }
4940         },
4941
4942         _addLayer: function (layer, name, overlay) {
4943                 if (this._map) {
4944                         layer.on('add remove', this._onLayerChange, this);
4945                 }
4946
4947                 this._layers.push({
4948                         layer: layer,
4949                         name: name,
4950                         overlay: overlay
4951                 });
4952
4953                 if (this.options.sortLayers) {
4954                         this._layers.sort(L.bind(function (a, b) {
4955                                 return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
4956                         }, this));
4957                 }
4958
4959                 if (this.options.autoZIndex && layer.setZIndex) {
4960                         this._lastZIndex++;
4961                         layer.setZIndex(this._lastZIndex);
4962                 }
4963
4964                 this._expandIfNotCollapsed();
4965         },
4966
4967         _update: function () {
4968                 if (!this._container) { return this; }
4969
4970                 empty(this._baseLayersList);
4971                 empty(this._overlaysList);
4972
4973                 this._layerControlInputs = [];
4974                 var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
4975
4976                 for (i = 0; i < this._layers.length; i++) {
4977                         obj = this._layers[i];
4978                         this._addItem(obj);
4979                         overlaysPresent = overlaysPresent || obj.overlay;
4980                         baseLayersPresent = baseLayersPresent || !obj.overlay;
4981                         baseLayersCount += !obj.overlay ? 1 : 0;
4982                 }
4983
4984                 // Hide base layers section if there's only one layer.
4985                 if (this.options.hideSingleBase) {
4986                         baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
4987                         this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
4988                 }
4989
4990                 this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
4991
4992                 return this;
4993         },
4994
4995         _onLayerChange: function (e) {
4996                 if (!this._handlingClick) {
4997                         this._update();
4998                 }
4999
5000                 var obj = this._getLayer(stamp(e.target));
5001
5002                 // @namespace Map
5003                 // @section Layer events
5004                 // @event baselayerchange: LayersControlEvent
5005                 // Fired when the base layer is changed through the [layer control](#control-layers).
5006                 // @event overlayadd: LayersControlEvent
5007                 // Fired when an overlay is selected through the [layer control](#control-layers).
5008                 // @event overlayremove: LayersControlEvent
5009                 // Fired when an overlay is deselected through the [layer control](#control-layers).
5010                 // @namespace Control.Layers
5011                 var type = obj.overlay ?
5012                         (e.type === 'add' ? 'overlayadd' : 'overlayremove') :
5013                         (e.type === 'add' ? 'baselayerchange' : null);
5014
5015                 if (type) {
5016                         this._map.fire(type, obj);
5017                 }
5018         },
5019
5020         // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
5021         _createRadioElement: function (name, checked) {
5022
5023                 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
5024                                 name + '"' + (checked ? ' checked="checked"' : '') + '/>';
5025
5026                 var radioFragment = document.createElement('div');
5027                 radioFragment.innerHTML = radioHtml;
5028
5029                 return radioFragment.firstChild;
5030         },
5031
5032         _addItem: function (obj) {
5033                 var label = document.createElement('label'),
5034                     checked = this._map.hasLayer(obj.layer),
5035                     input;
5036
5037                 if (obj.overlay) {
5038                         input = document.createElement('input');
5039                         input.type = 'checkbox';
5040                         input.className = 'leaflet-control-layers-selector';
5041                         input.defaultChecked = checked;
5042                 } else {
5043                         input = this._createRadioElement('leaflet-base-layers', checked);
5044                 }
5045
5046                 this._layerControlInputs.push(input);
5047                 input.layerId = stamp(obj.layer);
5048
5049                 on(input, 'click', this._onInputClick, this);
5050
5051                 var name = document.createElement('span');
5052                 name.innerHTML = ' ' + obj.name;
5053
5054                 // Helps from preventing layer control flicker when checkboxes are disabled
5055                 // https://github.com/Leaflet/Leaflet/issues/2771
5056                 var holder = document.createElement('div');
5057
5058                 label.appendChild(holder);
5059                 holder.appendChild(input);
5060                 holder.appendChild(name);
5061
5062                 var container = obj.overlay ? this._overlaysList : this._baseLayersList;
5063                 container.appendChild(label);
5064
5065                 this._checkDisabledLayers();
5066                 return label;
5067         },
5068
5069         _onInputClick: function () {
5070                 var inputs = this._layerControlInputs,
5071                     input, layer, hasLayer;
5072                 var addedLayers = [],
5073                     removedLayers = [];
5074
5075                 this._handlingClick = true;
5076
5077                 for (var i = inputs.length - 1; i >= 0; i--) {
5078                         input = inputs[i];
5079                         layer = this._getLayer(input.layerId).layer;
5080                         hasLayer = this._map.hasLayer(layer);
5081
5082                         if (input.checked && !hasLayer) {
5083                                 addedLayers.push(layer);
5084
5085                         } else if (!input.checked && hasLayer) {
5086                                 removedLayers.push(layer);
5087                         }
5088                 }
5089
5090                 // Bugfix issue 2318: Should remove all old layers before readding new ones
5091                 for (i = 0; i < removedLayers.length; i++) {
5092                         this._map.removeLayer(removedLayers[i]);
5093                 }
5094                 for (i = 0; i < addedLayers.length; i++) {
5095                         this._map.addLayer(addedLayers[i]);
5096                 }
5097
5098                 this._handlingClick = false;
5099
5100                 this._refocusOnMap();
5101         },
5102
5103         _checkDisabledLayers: function () {
5104                 var inputs = this._layerControlInputs,
5105                     input,
5106                     layer,
5107                     zoom = this._map.getZoom();
5108
5109                 for (var i = inputs.length - 1; i >= 0; i--) {
5110                         input = inputs[i];
5111                         layer = this._getLayer(input.layerId).layer;
5112                         input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
5113                                          (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
5114
5115                 }
5116         },
5117
5118         _expandIfNotCollapsed: function () {
5119                 if (this._map && !this.options.collapsed) {
5120                         this.expand();
5121                 }
5122                 return this;
5123         },
5124
5125         _expand: function () {
5126                 // Backward compatibility, remove me in 1.1.
5127                 return this.expand();
5128         },
5129
5130         _collapse: function () {
5131                 // Backward compatibility, remove me in 1.1.
5132                 return this.collapse();
5133         }
5134
5135 });
5136
5137
5138 // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
5139 // 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.
5140 var layers = function (baseLayers, overlays, options) {
5141         return new Layers(baseLayers, overlays, options);
5142 };
5143
5144 /*
5145  * @class Control.Zoom
5146  * @aka L.Control.Zoom
5147  * @inherits Control
5148  *
5149  * 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`.
5150  */
5151
5152 var Zoom = Control.extend({
5153         // @section
5154         // @aka Control.Zoom options
5155         options: {
5156                 position: 'topleft',
5157
5158                 // @option zoomInText: String = '+'
5159                 // The text set on the 'zoom in' button.
5160                 zoomInText: '+',
5161
5162                 // @option zoomInTitle: String = 'Zoom in'
5163                 // The title set on the 'zoom in' button.
5164                 zoomInTitle: 'Zoom in',
5165
5166                 // @option zoomOutText: String = '&#x2212;'
5167                 // The text set on the 'zoom out' button.
5168                 zoomOutText: '&#x2212;',
5169
5170                 // @option zoomOutTitle: String = 'Zoom out'
5171                 // The title set on the 'zoom out' button.
5172                 zoomOutTitle: 'Zoom out'
5173         },
5174
5175         onAdd: function (map) {
5176                 var zoomName = 'leaflet-control-zoom',
5177                     container = create$1('div', zoomName + ' leaflet-bar'),
5178                     options = this.options;
5179
5180                 this._zoomInButton  = this._createButton(options.zoomInText, options.zoomInTitle,
5181                         zoomName + '-in',  container, this._zoomIn);
5182                 this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
5183                         zoomName + '-out', container, this._zoomOut);
5184
5185                 this._updateDisabled();
5186                 map.on('zoomend zoomlevelschange', this._updateDisabled, this);
5187
5188                 return container;
5189         },
5190
5191         onRemove: function (map) {
5192                 map.off('zoomend zoomlevelschange', this._updateDisabled, this);
5193         },
5194
5195         disable: function () {
5196                 this._disabled = true;
5197                 this._updateDisabled();
5198                 return this;
5199         },
5200
5201         enable: function () {
5202                 this._disabled = false;
5203                 this._updateDisabled();
5204                 return this;
5205         },
5206
5207         _zoomIn: function (e) {
5208                 if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
5209                         this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5210                 }
5211         },
5212
5213         _zoomOut: function (e) {
5214                 if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
5215                         this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5216                 }
5217         },
5218
5219         _createButton: function (html, title, className, container, fn) {
5220                 var link = create$1('a', className, container);
5221                 link.innerHTML = html;
5222                 link.href = '#';
5223                 link.title = title;
5224
5225                 /*
5226                  * Will force screen readers like VoiceOver to read this as "Zoom in - button"
5227                  */
5228                 link.setAttribute('role', 'button');
5229                 link.setAttribute('aria-label', title);
5230
5231                 disableClickPropagation(link);
5232                 on(link, 'click', stop);
5233                 on(link, 'click', fn, this);
5234                 on(link, 'click', this._refocusOnMap, this);
5235
5236                 return link;
5237         },
5238
5239         _updateDisabled: function () {
5240                 var map = this._map,
5241                     className = 'leaflet-disabled';
5242
5243                 removeClass(this._zoomInButton, className);
5244                 removeClass(this._zoomOutButton, className);
5245
5246                 if (this._disabled || map._zoom === map.getMinZoom()) {
5247                         addClass(this._zoomOutButton, className);
5248                 }
5249                 if (this._disabled || map._zoom === map.getMaxZoom()) {
5250                         addClass(this._zoomInButton, className);
5251                 }
5252         }
5253 });
5254
5255 // @namespace Map
5256 // @section Control options
5257 // @option zoomControl: Boolean = true
5258 // Whether a [zoom control](#control-zoom) is added to the map by default.
5259 Map.mergeOptions({
5260         zoomControl: true
5261 });
5262
5263 Map.addInitHook(function () {
5264         if (this.options.zoomControl) {
5265                 this.zoomControl = new Zoom();
5266                 this.addControl(this.zoomControl);
5267         }
5268 });
5269
5270 // @namespace Control.Zoom
5271 // @factory L.control.zoom(options: Control.Zoom options)
5272 // Creates a zoom control
5273 var zoom = function (options) {
5274         return new Zoom(options);
5275 };
5276
5277 /*
5278  * @class Control.Scale
5279  * @aka L.Control.Scale
5280  * @inherits Control
5281  *
5282  * 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`.
5283  *
5284  * @example
5285  *
5286  * ```js
5287  * L.control.scale().addTo(map);
5288  * ```
5289  */
5290
5291 var Scale = Control.extend({
5292         // @section
5293         // @aka Control.Scale options
5294         options: {
5295                 position: 'bottomleft',
5296
5297                 // @option maxWidth: Number = 100
5298                 // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
5299                 maxWidth: 100,
5300
5301                 // @option metric: Boolean = True
5302                 // Whether to show the metric scale line (m/km).
5303                 metric: true,
5304
5305                 // @option imperial: Boolean = True
5306                 // Whether to show the imperial scale line (mi/ft).
5307                 imperial: true
5308
5309                 // @option updateWhenIdle: Boolean = false
5310                 // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
5311         },
5312
5313         onAdd: function (map) {
5314                 var className = 'leaflet-control-scale',
5315                     container = create$1('div', className),
5316                     options = this.options;
5317
5318                 this._addScales(options, className + '-line', container);
5319
5320                 map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5321                 map.whenReady(this._update, this);
5322
5323                 return container;
5324         },
5325
5326         onRemove: function (map) {
5327                 map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5328         },
5329
5330         _addScales: function (options, className, container) {
5331                 if (options.metric) {
5332                         this._mScale = create$1('div', className, container);
5333                 }
5334                 if (options.imperial) {
5335                         this._iScale = create$1('div', className, container);
5336                 }
5337         },
5338
5339         _update: function () {
5340                 var map = this._map,
5341                     y = map.getSize().y / 2;
5342
5343                 var maxMeters = map.distance(
5344                                 map.containerPointToLatLng([0, y]),
5345                                 map.containerPointToLatLng([this.options.maxWidth, y]));
5346
5347                 this._updateScales(maxMeters);
5348         },
5349
5350         _updateScales: function (maxMeters) {
5351                 if (this.options.metric && maxMeters) {
5352                         this._updateMetric(maxMeters);
5353                 }
5354                 if (this.options.imperial && maxMeters) {
5355                         this._updateImperial(maxMeters);
5356                 }
5357         },
5358
5359         _updateMetric: function (maxMeters) {
5360                 var meters = this._getRoundNum(maxMeters),
5361                     label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
5362
5363                 this._updateScale(this._mScale, label, meters / maxMeters);
5364         },
5365
5366         _updateImperial: function (maxMeters) {
5367                 var maxFeet = maxMeters * 3.2808399,
5368                     maxMiles, miles, feet;
5369
5370                 if (maxFeet > 5280) {
5371                         maxMiles = maxFeet / 5280;
5372                         miles = this._getRoundNum(maxMiles);
5373                         this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
5374
5375                 } else {
5376                         feet = this._getRoundNum(maxFeet);
5377                         this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
5378                 }
5379         },
5380
5381         _updateScale: function (scale, text, ratio) {
5382                 scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
5383                 scale.innerHTML = text;
5384         },
5385
5386         _getRoundNum: function (num) {
5387                 var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
5388                     d = num / pow10;
5389
5390                 d = d >= 10 ? 10 :
5391                     d >= 5 ? 5 :
5392                     d >= 3 ? 3 :
5393                     d >= 2 ? 2 : 1;
5394
5395                 return pow10 * d;
5396         }
5397 });
5398
5399
5400 // @factory L.control.scale(options?: Control.Scale options)
5401 // Creates an scale control with the given options.
5402 var scale = function (options) {
5403         return new Scale(options);
5404 };
5405
5406 /*
5407  * @class Control.Attribution
5408  * @aka L.Control.Attribution
5409  * @inherits Control
5410  *
5411  * 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.
5412  */
5413
5414 var Attribution = Control.extend({
5415         // @section
5416         // @aka Control.Attribution options
5417         options: {
5418                 position: 'bottomright',
5419
5420                 // @option prefix: String = 'Leaflet'
5421                 // The HTML text shown before the attributions. Pass `false` to disable.
5422                 prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
5423         },
5424
5425         initialize: function (options) {
5426                 setOptions(this, options);
5427
5428                 this._attributions = {};
5429         },
5430
5431         onAdd: function (map) {
5432                 map.attributionControl = this;
5433                 this._container = create$1('div', 'leaflet-control-attribution');
5434                 disableClickPropagation(this._container);
5435
5436                 // TODO ugly, refactor
5437                 for (var i in map._layers) {
5438                         if (map._layers[i].getAttribution) {
5439                                 this.addAttribution(map._layers[i].getAttribution());
5440                         }
5441                 }
5442
5443                 this._update();
5444
5445                 return this._container;
5446         },
5447
5448         // @method setPrefix(prefix: String): this
5449         // Sets the text before the attributions.
5450         setPrefix: function (prefix) {
5451                 this.options.prefix = prefix;
5452                 this._update();
5453                 return this;
5454         },
5455
5456         // @method addAttribution(text: String): this
5457         // Adds an attribution text (e.g. `'Vector data &copy; Mapbox'`).
5458         addAttribution: function (text) {
5459                 if (!text) { return this; }
5460
5461                 if (!this._attributions[text]) {
5462                         this._attributions[text] = 0;
5463                 }
5464                 this._attributions[text]++;
5465
5466                 this._update();
5467
5468                 return this;
5469         },
5470
5471         // @method removeAttribution(text: String): this
5472         // Removes an attribution text.
5473         removeAttribution: function (text) {
5474                 if (!text) { return this; }
5475
5476                 if (this._attributions[text]) {
5477                         this._attributions[text]--;
5478                         this._update();
5479                 }
5480
5481                 return this;
5482         },
5483
5484         _update: function () {
5485                 if (!this._map) { return; }
5486
5487                 var attribs = [];
5488
5489                 for (var i in this._attributions) {
5490                         if (this._attributions[i]) {
5491                                 attribs.push(i);
5492                         }
5493                 }
5494
5495                 var prefixAndAttribs = [];
5496
5497                 if (this.options.prefix) {
5498                         prefixAndAttribs.push(this.options.prefix);
5499                 }
5500                 if (attribs.length) {
5501                         prefixAndAttribs.push(attribs.join(', '));
5502                 }
5503
5504                 this._container.innerHTML = prefixAndAttribs.join(' | ');
5505         }
5506 });
5507
5508 // @namespace Map
5509 // @section Control options
5510 // @option attributionControl: Boolean = true
5511 // Whether a [attribution control](#control-attribution) is added to the map by default.
5512 Map.mergeOptions({
5513         attributionControl: true
5514 });
5515
5516 Map.addInitHook(function () {
5517         if (this.options.attributionControl) {
5518                 new Attribution().addTo(this);
5519         }
5520 });
5521
5522 // @namespace Control.Attribution
5523 // @factory L.control.attribution(options: Control.Attribution options)
5524 // Creates an attribution control.
5525 var attribution = function (options) {
5526         return new Attribution(options);
5527 };
5528
5529 Control.Layers = Layers;
5530 Control.Zoom = Zoom;
5531 Control.Scale = Scale;
5532 Control.Attribution = Attribution;
5533
5534 control.layers = layers;
5535 control.zoom = zoom;
5536 control.scale = scale;
5537 control.attribution = attribution;
5538
5539 /*
5540         L.Handler is a base class for handler classes that are used internally to inject
5541         interaction features like dragging to classes like Map and Marker.
5542 */
5543
5544 // @class Handler
5545 // @aka L.Handler
5546 // Abstract class for map interaction handlers
5547
5548 var Handler = Class.extend({
5549         initialize: function (map) {
5550                 this._map = map;
5551         },
5552
5553         // @method enable(): this
5554         // Enables the handler
5555         enable: function () {
5556                 if (this._enabled) { return this; }
5557
5558                 this._enabled = true;
5559                 this.addHooks();
5560                 return this;
5561         },
5562
5563         // @method disable(): this
5564         // Disables the handler
5565         disable: function () {
5566                 if (!this._enabled) { return this; }
5567
5568                 this._enabled = false;
5569                 this.removeHooks();
5570                 return this;
5571         },
5572
5573         // @method enabled(): Boolean
5574         // Returns `true` if the handler is enabled
5575         enabled: function () {
5576                 return !!this._enabled;
5577         }
5578
5579         // @section Extension methods
5580         // Classes inheriting from `Handler` must implement the two following methods:
5581         // @method addHooks()
5582         // Called when the handler is enabled, should add event hooks.
5583         // @method removeHooks()
5584         // Called when the handler is disabled, should remove the event hooks added previously.
5585 });
5586
5587 var Mixin = {Events: Events};
5588
5589 /*
5590  * @class Draggable
5591  * @aka L.Draggable
5592  * @inherits Evented
5593  *
5594  * A class for making DOM elements draggable (including touch support).
5595  * Used internally for map and marker dragging. Only works for elements
5596  * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
5597  *
5598  * @example
5599  * ```js
5600  * var draggable = new L.Draggable(elementToDrag);
5601  * draggable.enable();
5602  * ```
5603  */
5604
5605 var _dragging = false;
5606 var START = touch ? 'touchstart mousedown' : 'mousedown';
5607 var END = {
5608         mousedown: 'mouseup',
5609         touchstart: 'touchend',
5610         pointerdown: 'touchend',
5611         MSPointerDown: 'touchend'
5612 };
5613 var MOVE = {
5614         mousedown: 'mousemove',
5615         touchstart: 'touchmove',
5616         pointerdown: 'touchmove',
5617         MSPointerDown: 'touchmove'
5618 };
5619
5620
5621 var Draggable = Evented.extend({
5622
5623         options: {
5624                 // @section
5625                 // @aka Draggable options
5626                 // @option clickTolerance: Number = 3
5627                 // The max number of pixels a user can shift the mouse pointer during a click
5628                 // for it to be considered a valid click (as opposed to a mouse drag).
5629                 clickTolerance: 3
5630         },
5631
5632         // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
5633         // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
5634         initialize: function (element, dragStartTarget, preventOutline$$1, options) {
5635                 setOptions(this, options);
5636
5637                 this._element = element;
5638                 this._dragStartTarget = dragStartTarget || element;
5639                 this._preventOutline = preventOutline$$1;
5640         },
5641
5642         // @method enable()
5643         // Enables the dragging ability
5644         enable: function () {
5645                 if (this._enabled) { return; }
5646
5647                 on(this._dragStartTarget, START, this._onDown, this);
5648
5649                 this._enabled = true;
5650         },
5651
5652         // @method disable()
5653         // Disables the dragging ability
5654         disable: function () {
5655                 if (!this._enabled) { return; }
5656
5657                 // If we're currently dragging this draggable,
5658                 // disabling it counts as first ending the drag.
5659                 if (L.Draggable._dragging === this) {
5660                         this.finishDrag();
5661                 }
5662
5663                 off(this._dragStartTarget, START, this._onDown, this);
5664
5665                 this._enabled = false;
5666                 this._moved = false;
5667         },
5668
5669         _onDown: function (e) {
5670                 // Ignore simulated events, since we handle both touch and
5671                 // mouse explicitly; otherwise we risk getting duplicates of
5672                 // touch events, see #4315.
5673                 // Also ignore the event if disabled; this happens in IE11
5674                 // under some circumstances, see #3666.
5675                 if (e._simulated || !this._enabled) { return; }
5676
5677                 this._moved = false;
5678
5679                 if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
5680
5681                 if (_dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
5682                 _dragging = this;  // Prevent dragging multiple objects at once.
5683
5684                 if (this._preventOutline) {
5685                         preventOutline(this._element);
5686                 }
5687
5688                 disableImageDrag();
5689                 disableTextSelection();
5690
5691                 if (this._moving) { return; }
5692
5693                 // @event down: Event
5694                 // Fired when a drag is about to start.
5695                 this.fire('down');
5696
5697                 var first = e.touches ? e.touches[0] : e;
5698
5699                 this._startPoint = new Point(first.clientX, first.clientY);
5700
5701                 on(document, MOVE[e.type], this._onMove, this);
5702                 on(document, END[e.type], this._onUp, this);
5703         },
5704
5705         _onMove: function (e) {
5706                 // Ignore simulated events, since we handle both touch and
5707                 // mouse explicitly; otherwise we risk getting duplicates of
5708                 // touch events, see #4315.
5709                 // Also ignore the event if disabled; this happens in IE11
5710                 // under some circumstances, see #3666.
5711                 if (e._simulated || !this._enabled) { return; }
5712
5713                 if (e.touches && e.touches.length > 1) {
5714                         this._moved = true;
5715                         return;
5716                 }
5717
5718                 var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
5719                     newPoint = new Point(first.clientX, first.clientY),
5720                     offset = newPoint.subtract(this._startPoint);
5721
5722                 if (!offset.x && !offset.y) { return; }
5723                 if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
5724
5725                 preventDefault(e);
5726
5727                 if (!this._moved) {
5728                         // @event dragstart: Event
5729                         // Fired when a drag starts
5730                         this.fire('dragstart');
5731
5732                         this._moved = true;
5733                         this._startPos = getPosition(this._element).subtract(offset);
5734
5735                         addClass(document.body, 'leaflet-dragging');
5736
5737                         this._lastTarget = e.target || e.srcElement;
5738                         // IE and Edge do not give the <use> element, so fetch it
5739                         // if necessary
5740                         if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
5741                                 this._lastTarget = this._lastTarget.correspondingUseElement;
5742                         }
5743                         addClass(this._lastTarget, 'leaflet-drag-target');
5744                 }
5745
5746                 this._newPos = this._startPos.add(offset);
5747                 this._moving = true;
5748
5749                 cancelAnimFrame(this._animRequest);
5750                 this._lastEvent = e;
5751                 this._animRequest = requestAnimFrame(this._updatePosition, this, true);
5752         },
5753
5754         _updatePosition: function () {
5755                 var e = {originalEvent: this._lastEvent};
5756
5757                 // @event predrag: Event
5758                 // Fired continuously during dragging *before* each corresponding
5759                 // update of the element's position.
5760                 this.fire('predrag', e);
5761                 setPosition(this._element, this._newPos);
5762
5763                 // @event drag: Event
5764                 // Fired continuously during dragging.
5765                 this.fire('drag', e);
5766         },
5767
5768         _onUp: function (e) {
5769                 // Ignore simulated events, since we handle both touch and
5770                 // mouse explicitly; otherwise we risk getting duplicates of
5771                 // touch events, see #4315.
5772                 // Also ignore the event if disabled; this happens in IE11
5773                 // under some circumstances, see #3666.
5774                 if (e._simulated || !this._enabled) { return; }
5775                 this.finishDrag();
5776         },
5777
5778         finishDrag: function () {
5779                 removeClass(document.body, 'leaflet-dragging');
5780
5781                 if (this._lastTarget) {
5782                         removeClass(this._lastTarget, 'leaflet-drag-target');
5783                         this._lastTarget = null;
5784                 }
5785
5786                 for (var i in MOVE) {
5787                         off(document, MOVE[i], this._onMove, this);
5788                         off(document, END[i], this._onUp, this);
5789                 }
5790
5791                 enableImageDrag();
5792                 enableTextSelection();
5793
5794                 if (this._moved && this._moving) {
5795                         // ensure drag is not fired after dragend
5796                         cancelAnimFrame(this._animRequest);
5797
5798                         // @event dragend: DragEndEvent
5799                         // Fired when the drag ends.
5800                         this.fire('dragend', {
5801                                 distance: this._newPos.distanceTo(this._startPos)
5802                         });
5803                 }
5804
5805                 this._moving = false;
5806                 _dragging = false;
5807         }
5808
5809 });
5810
5811 /*
5812  * @namespace LineUtil
5813  *
5814  * Various utility functions for polyine points processing, used by Leaflet internally to make polylines lightning-fast.
5815  */
5816
5817 // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
5818 // Improves rendering performance dramatically by lessening the number of points to draw.
5819
5820 // @function simplify(points: Point[], tolerance: Number): Point[]
5821 // Dramatically reduces the number of points in a polyline while retaining
5822 // its shape and returns a new array of simplified points, using the
5823 // [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
5824 // Used for a huge performance boost when processing/displaying Leaflet polylines for
5825 // each zoom level and also reducing visual noise. tolerance affects the amount of
5826 // simplification (lesser value means higher quality but slower and with more points).
5827 // Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
5828 function simplify(points, tolerance) {
5829         if (!tolerance || !points.length) {
5830                 return points.slice();
5831         }
5832
5833         var sqTolerance = tolerance * tolerance;
5834
5835             // stage 1: vertex reduction
5836             points = _reducePoints(points, sqTolerance);
5837
5838             // stage 2: Douglas-Peucker simplification
5839             points = _simplifyDP(points, sqTolerance);
5840
5841         return points;
5842 }
5843
5844 // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
5845 // Returns the distance between point `p` and segment `p1` to `p2`.
5846 function pointToSegmentDistance(p, p1, p2) {
5847         return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
5848 }
5849
5850 // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
5851 // Returns the closest point from a point `p` on a segment `p1` to `p2`.
5852 function closestPointOnSegment(p, p1, p2) {
5853         return _sqClosestPointOnSegment(p, p1, p2);
5854 }
5855
5856 // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
5857 function _simplifyDP(points, sqTolerance) {
5858
5859         var len = points.length,
5860             ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
5861             markers = new ArrayConstructor(len);
5862
5863             markers[0] = markers[len - 1] = 1;
5864
5865         _simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
5866
5867         var i,
5868             newPoints = [];
5869
5870         for (i = 0; i < len; i++) {
5871                 if (markers[i]) {
5872                         newPoints.push(points[i]);
5873                 }
5874         }
5875
5876         return newPoints;
5877 }
5878
5879 function _simplifyDPStep(points, markers, sqTolerance, first, last) {
5880
5881         var maxSqDist = 0,
5882         index, i, sqDist;
5883
5884         for (i = first + 1; i <= last - 1; i++) {
5885                 sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
5886
5887                 if (sqDist > maxSqDist) {
5888                         index = i;
5889                         maxSqDist = sqDist;
5890                 }
5891         }
5892
5893         if (maxSqDist > sqTolerance) {
5894                 markers[index] = 1;
5895
5896                 _simplifyDPStep(points, markers, sqTolerance, first, index);
5897                 _simplifyDPStep(points, markers, sqTolerance, index, last);
5898         }
5899 }
5900
5901 // reduce points that are too close to each other to a single point
5902 function _reducePoints(points, sqTolerance) {
5903         var reducedPoints = [points[0]];
5904
5905         for (var i = 1, prev = 0, len = points.length; i < len; i++) {
5906                 if (_sqDist(points[i], points[prev]) > sqTolerance) {
5907                         reducedPoints.push(points[i]);
5908                         prev = i;
5909                 }
5910         }
5911         if (prev < len - 1) {
5912                 reducedPoints.push(points[len - 1]);
5913         }
5914         return reducedPoints;
5915 }
5916
5917 var _lastCode;
5918
5919 // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
5920 // Clips the segment a to b by rectangular bounds with the
5921 // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
5922 // (modifying the segment points directly!). Used by Leaflet to only show polyline
5923 // points that are on the screen or near, increasing performance.
5924 function clipSegment(a, b, bounds, useLastCode, round) {
5925         var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
5926             codeB = _getBitCode(b, bounds),
5927
5928             codeOut, p, newCode;
5929
5930             // save 2nd code to avoid calculating it on the next segment
5931             _lastCode = codeB;
5932
5933         while (true) {
5934                 // if a,b is inside the clip window (trivial accept)
5935                 if (!(codeA | codeB)) {
5936                         return [a, b];
5937                 }
5938
5939                 // if a,b is outside the clip window (trivial reject)
5940                 if (codeA & codeB) {
5941                         return false;
5942                 }
5943
5944                 // other cases
5945                 codeOut = codeA || codeB;
5946                 p = _getEdgeIntersection(a, b, codeOut, bounds, round);
5947                 newCode = _getBitCode(p, bounds);
5948
5949                 if (codeOut === codeA) {
5950                         a = p;
5951                         codeA = newCode;
5952                 } else {
5953                         b = p;
5954                         codeB = newCode;
5955                 }
5956         }
5957 }
5958
5959 function _getEdgeIntersection(a, b, code, bounds, round) {
5960         var dx = b.x - a.x,
5961             dy = b.y - a.y,
5962             min = bounds.min,
5963             max = bounds.max,
5964             x, y;
5965
5966         if (code & 8) { // top
5967                 x = a.x + dx * (max.y - a.y) / dy;
5968                 y = max.y;
5969
5970         } else if (code & 4) { // bottom
5971                 x = a.x + dx * (min.y - a.y) / dy;
5972                 y = min.y;
5973
5974         } else if (code & 2) { // right
5975                 x = max.x;
5976                 y = a.y + dy * (max.x - a.x) / dx;
5977
5978         } else if (code & 1) { // left
5979                 x = min.x;
5980                 y = a.y + dy * (min.x - a.x) / dx;
5981         }
5982
5983         return new Point(x, y, round);
5984 }
5985
5986 function _getBitCode(p, bounds) {
5987         var code = 0;
5988
5989         if (p.x < bounds.min.x) { // left
5990                 code |= 1;
5991         } else if (p.x > bounds.max.x) { // right
5992                 code |= 2;
5993         }
5994
5995         if (p.y < bounds.min.y) { // bottom
5996                 code |= 4;
5997         } else if (p.y > bounds.max.y) { // top
5998                 code |= 8;
5999         }
6000
6001         return code;
6002 }
6003
6004 // square distance (to avoid unnecessary Math.sqrt calls)
6005 function _sqDist(p1, p2) {
6006         var dx = p2.x - p1.x,
6007             dy = p2.y - p1.y;
6008         return dx * dx + dy * dy;
6009 }
6010
6011 // return closest point on segment or distance to that point
6012 function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
6013         var x = p1.x,
6014             y = p1.y,
6015             dx = p2.x - x,
6016             dy = p2.y - y,
6017             dot = dx * dx + dy * dy,
6018             t;
6019
6020         if (dot > 0) {
6021                 t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
6022
6023                 if (t > 1) {
6024                         x = p2.x;
6025                         y = p2.y;
6026                 } else if (t > 0) {
6027                         x += dx * t;
6028                         y += dy * t;
6029                 }
6030         }
6031
6032         dx = p.x - x;
6033         dy = p.y - y;
6034
6035         return sqDist ? dx * dx + dy * dy : new Point(x, y);
6036 }
6037
6038
6039 function _flat(latlngs) {
6040         // true if it's a flat array of latlngs; false if nested
6041         return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
6042 }
6043
6044
6045 var LineUtil = (Object.freeze || Object)({
6046         simplify: simplify,
6047         pointToSegmentDistance: pointToSegmentDistance,
6048         closestPointOnSegment: closestPointOnSegment,
6049         clipSegment: clipSegment,
6050         _getEdgeIntersection: _getEdgeIntersection,
6051         _getBitCode: _getBitCode,
6052         _sqClosestPointOnSegment: _sqClosestPointOnSegment,
6053         _flat: _flat
6054 });
6055
6056 /*
6057  * @namespace PolyUtil
6058  * Various utility functions for polygon geometries.
6059  */
6060
6061 /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
6062  * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgeman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)).
6063  * Used by Leaflet to only show polygon points that are on the screen or near, increasing
6064  * performance. Note that polygon points needs different algorithm for clipping
6065  * than polyline, so there's a seperate method for it.
6066  */
6067 function clipPolygon(points, bounds, round) {
6068         var clippedPoints,
6069             edges = [1, 4, 2, 8],
6070             i, j, k,
6071             a, b,
6072             len, edge, p;
6073
6074         for (i = 0, len = points.length; i < len; i++) {
6075                 points[i]._code = _getBitCode(points[i], bounds);
6076         }
6077
6078         // for each edge (left, bottom, right, top)
6079         for (k = 0; k < 4; k++) {
6080                 edge = edges[k];
6081                 clippedPoints = [];
6082
6083                 for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
6084                         a = points[i];
6085                         b = points[j];
6086
6087                         // if a is inside the clip window
6088                         if (!(a._code & edge)) {
6089                                 // if b is outside the clip window (a->b goes out of screen)
6090                                 if (b._code & edge) {
6091                                         p = _getEdgeIntersection(b, a, edge, bounds, round);
6092                                         p._code = _getBitCode(p, bounds);
6093                                         clippedPoints.push(p);
6094                                 }
6095                                 clippedPoints.push(a);
6096
6097                         // else if b is inside the clip window (a->b enters the screen)
6098                         } else if (!(b._code & edge)) {
6099                                 p = _getEdgeIntersection(b, a, edge, bounds, round);
6100                                 p._code = _getBitCode(p, bounds);
6101                                 clippedPoints.push(p);
6102                         }
6103                 }
6104                 points = clippedPoints;
6105         }
6106
6107         return points;
6108 }
6109
6110
6111 var PolyUtil = (Object.freeze || Object)({
6112         clipPolygon: clipPolygon
6113 });
6114
6115 /*
6116  * @namespace Projection
6117  * @section
6118  * Leaflet comes with a set of already defined Projections out of the box:
6119  *
6120  * @projection L.Projection.LonLat
6121  *
6122  * Equirectangular, or Plate Carree projection — the most simple projection,
6123  * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
6124  * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
6125  * `EPSG:4326` and `Simple` CRS.
6126  */
6127
6128 var LonLat = {
6129         project: function (latlng) {
6130                 return new Point(latlng.lng, latlng.lat);
6131         },
6132
6133         unproject: function (point) {
6134                 return new LatLng(point.y, point.x);
6135         },
6136
6137         bounds: new Bounds([-180, -90], [180, 90])
6138 };
6139
6140 /*
6141  * @namespace Projection
6142  * @projection L.Projection.Mercator
6143  *
6144  * 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.
6145  */
6146
6147 var Mercator = {
6148         R: 6378137,
6149         R_MINOR: 6356752.314245179,
6150
6151         bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
6152
6153         project: function (latlng) {
6154                 var d = Math.PI / 180,
6155                     r = this.R,
6156                     y = latlng.lat * d,
6157                     tmp = this.R_MINOR / r,
6158                     e = Math.sqrt(1 - tmp * tmp),
6159                     con = e * Math.sin(y);
6160
6161                 var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
6162                 y = -r * Math.log(Math.max(ts, 1E-10));
6163
6164                 return new Point(latlng.lng * d * r, y);
6165         },
6166
6167         unproject: function (point) {
6168                 var d = 180 / Math.PI,
6169                     r = this.R,
6170                     tmp = this.R_MINOR / r,
6171                     e = Math.sqrt(1 - tmp * tmp),
6172                     ts = Math.exp(-point.y / r),
6173                     phi = Math.PI / 2 - 2 * Math.atan(ts);
6174
6175                 for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
6176                         con = e * Math.sin(phi);
6177                         con = Math.pow((1 - con) / (1 + con), e / 2);
6178                         dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
6179                         phi += dphi;
6180                 }
6181
6182                 return new LatLng(phi * d, point.x * d / r);
6183         }
6184 };
6185
6186 /*
6187  * @class Projection
6188
6189  * An object with methods for projecting geographical coordinates of the world onto
6190  * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection).
6191
6192  * @property bounds: Bounds
6193  * The bounds (specified in CRS units) where the projection is valid
6194
6195  * @method project(latlng: LatLng): Point
6196  * Projects geographical coordinates into a 2D point.
6197  * Only accepts actual `L.LatLng` instances, not arrays.
6198
6199  * @method unproject(point: Point): LatLng
6200  * The inverse of `project`. Projects a 2D point into a geographical location.
6201  * Only accepts actual `L.Point` instances, not arrays.
6202
6203  */
6204
6205
6206
6207
6208 var index = (Object.freeze || Object)({
6209         LonLat: LonLat,
6210         Mercator: Mercator,
6211         SphericalMercator: SphericalMercator
6212 });
6213
6214 /*
6215  * @namespace CRS
6216  * @crs L.CRS.EPSG3395
6217  *
6218  * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
6219  */
6220 var EPSG3395 = extend({}, Earth, {
6221         code: 'EPSG:3395',
6222         projection: Mercator,
6223
6224         transformation: (function () {
6225                 var scale = 0.5 / (Math.PI * Mercator.R);
6226                 return toTransformation(scale, 0.5, -scale, 0.5);
6227         }())
6228 });
6229
6230 /*
6231  * @namespace CRS
6232  * @crs L.CRS.EPSG4326
6233  *
6234  * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
6235  *
6236  * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
6237  * which is a breaking change from 0.7.x behaviour.  If you are using a `TileLayer`
6238  * with this CRS, ensure that there are two 256x256 pixel tiles covering the
6239  * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
6240  * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
6241  */
6242
6243 var EPSG4326 = extend({}, Earth, {
6244         code: 'EPSG:4326',
6245         projection: LonLat,
6246         transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
6247 });
6248
6249 /*
6250  * @namespace CRS
6251  * @crs L.CRS.Simple
6252  *
6253  * A simple CRS that maps longitude and latitude into `x` and `y` directly.
6254  * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
6255  * axis should still be inverted (going from bottom to top). `distance()` returns
6256  * simple euclidean distance.
6257  */
6258
6259 var Simple = extend({}, CRS, {
6260         projection: LonLat,
6261         transformation: toTransformation(1, 0, -1, 0),
6262
6263         scale: function (zoom) {
6264                 return Math.pow(2, zoom);
6265         },
6266
6267         zoom: function (scale) {
6268                 return Math.log(scale) / Math.LN2;
6269         },
6270
6271         distance: function (latlng1, latlng2) {
6272                 var dx = latlng2.lng - latlng1.lng,
6273                     dy = latlng2.lat - latlng1.lat;
6274
6275                 return Math.sqrt(dx * dx + dy * dy);
6276         },
6277
6278         infinite: true
6279 });
6280
6281 CRS.Earth = Earth;
6282 CRS.EPSG3395 = EPSG3395;
6283 CRS.EPSG3857 = EPSG3857;
6284 CRS.EPSG900913 = EPSG900913;
6285 CRS.EPSG4326 = EPSG4326;
6286 CRS.Simple = Simple;
6287
6288 /*
6289  * @class Layer
6290  * @inherits Evented
6291  * @aka L.Layer
6292  * @aka ILayer
6293  *
6294  * A set of methods from the Layer base class that all Leaflet layers use.
6295  * Inherits all methods, options and events from `L.Evented`.
6296  *
6297  * @example
6298  *
6299  * ```js
6300  * var layer = L.Marker(latlng).addTo(map);
6301  * layer.addTo(map);
6302  * layer.remove();
6303  * ```
6304  *
6305  * @event add: Event
6306  * Fired after the layer is added to a map
6307  *
6308  * @event remove: Event
6309  * Fired after the layer is removed from a map
6310  */
6311
6312
6313 var Layer = Evented.extend({
6314
6315         // Classes extending `L.Layer` will inherit the following options:
6316         options: {
6317                 // @option pane: String = 'overlayPane'
6318                 // 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.
6319                 pane: 'overlayPane',
6320
6321                 // @option attribution: String = null
6322                 // String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox".
6323                 attribution: null,
6324
6325                 bubblingMouseEvents: true
6326         },
6327
6328         /* @section
6329          * Classes extending `L.Layer` will inherit the following methods:
6330          *
6331          * @method addTo(map: Map): this
6332          * Adds the layer to the given map
6333          */
6334         addTo: function (map) {
6335                 map.addLayer(this);
6336                 return this;
6337         },
6338
6339         // @method remove: this
6340         // Removes the layer from the map it is currently active on.
6341         remove: function () {
6342                 return this.removeFrom(this._map || this._mapToAdd);
6343         },
6344
6345         // @method removeFrom(map: Map): this
6346         // Removes the layer from the given map
6347         removeFrom: function (obj) {
6348                 if (obj) {
6349                         obj.removeLayer(this);
6350                 }
6351                 return this;
6352         },
6353
6354         // @method getPane(name? : String): HTMLElement
6355         // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
6356         getPane: function (name) {
6357                 return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
6358         },
6359
6360         addInteractiveTarget: function (targetEl) {
6361                 this._map._targets[stamp(targetEl)] = this;
6362                 return this;
6363         },
6364
6365         removeInteractiveTarget: function (targetEl) {
6366                 delete this._map._targets[stamp(targetEl)];
6367                 return this;
6368         },
6369
6370         // @method getAttribution: String
6371         // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
6372         getAttribution: function () {
6373                 return this.options.attribution;
6374         },
6375
6376         _layerAdd: function (e) {
6377                 var map = e.target;
6378
6379                 // check in case layer gets added and then removed before the map is ready
6380                 if (!map.hasLayer(this)) { return; }
6381
6382                 this._map = map;
6383                 this._zoomAnimated = map._zoomAnimated;
6384
6385                 if (this.getEvents) {
6386                         var events = this.getEvents();
6387                         map.on(events, this);
6388                         this.once('remove', function () {
6389                                 map.off(events, this);
6390                         }, this);
6391                 }
6392
6393                 this.onAdd(map);
6394
6395                 if (this.getAttribution && map.attributionControl) {
6396                         map.attributionControl.addAttribution(this.getAttribution());
6397                 }
6398
6399                 this.fire('add');
6400                 map.fire('layeradd', {layer: this});
6401         }
6402 });
6403
6404 /* @section Extension methods
6405  * @uninheritable
6406  *
6407  * Every layer should extend from `L.Layer` and (re-)implement the following methods.
6408  *
6409  * @method onAdd(map: Map): this
6410  * 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).
6411  *
6412  * @method onRemove(map: Map): this
6413  * 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).
6414  *
6415  * @method getEvents(): Object
6416  * 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.
6417  *
6418  * @method getAttribution(): String
6419  * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
6420  *
6421  * @method beforeAdd(map: Map): this
6422  * 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.
6423  */
6424
6425
6426 /* @namespace Map
6427  * @section Layer events
6428  *
6429  * @event layeradd: LayerEvent
6430  * Fired when a new layer is added to the map.
6431  *
6432  * @event layerremove: LayerEvent
6433  * Fired when some layer is removed from the map
6434  *
6435  * @section Methods for Layers and Controls
6436  */
6437 Map.include({
6438         // @method addLayer(layer: Layer): this
6439         // Adds the given layer to the map
6440         addLayer: function (layer) {
6441                 var id = stamp(layer);
6442                 if (this._layers[id]) { return this; }
6443                 this._layers[id] = layer;
6444
6445                 layer._mapToAdd = this;
6446
6447                 if (layer.beforeAdd) {
6448                         layer.beforeAdd(this);
6449                 }
6450
6451                 this.whenReady(layer._layerAdd, layer);
6452
6453                 return this;
6454         },
6455
6456         // @method removeLayer(layer: Layer): this
6457         // Removes the given layer from the map.
6458         removeLayer: function (layer) {
6459                 var id = stamp(layer);
6460
6461                 if (!this._layers[id]) { return this; }
6462
6463                 if (this._loaded) {
6464                         layer.onRemove(this);
6465                 }
6466
6467                 if (layer.getAttribution && this.attributionControl) {
6468                         this.attributionControl.removeAttribution(layer.getAttribution());
6469                 }
6470
6471                 delete this._layers[id];
6472
6473                 if (this._loaded) {
6474                         this.fire('layerremove', {layer: layer});
6475                         layer.fire('remove');
6476                 }
6477
6478                 layer._map = layer._mapToAdd = null;
6479
6480                 return this;
6481         },
6482
6483         // @method hasLayer(layer: Layer): Boolean
6484         // Returns `true` if the given layer is currently added to the map
6485         hasLayer: function (layer) {
6486                 return !!layer && (stamp(layer) in this._layers);
6487         },
6488
6489         /* @method eachLayer(fn: Function, context?: Object): this
6490          * Iterates over the layers of the map, optionally specifying context of the iterator function.
6491          * ```
6492          * map.eachLayer(function(layer){
6493          *     layer.bindPopup('Hello');
6494          * });
6495          * ```
6496          */
6497         eachLayer: function (method, context) {
6498                 for (var i in this._layers) {
6499                         method.call(context, this._layers[i]);
6500                 }
6501                 return this;
6502         },
6503
6504         _addLayers: function (layers) {
6505                 layers = layers ? (isArray(layers) ? layers : [layers]) : [];
6506
6507                 for (var i = 0, len = layers.length; i < len; i++) {
6508                         this.addLayer(layers[i]);
6509                 }
6510         },
6511
6512         _addZoomLimit: function (layer) {
6513                 if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
6514                         this._zoomBoundLayers[stamp(layer)] = layer;
6515                         this._updateZoomLevels();
6516                 }
6517         },
6518
6519         _removeZoomLimit: function (layer) {
6520                 var id = stamp(layer);
6521
6522                 if (this._zoomBoundLayers[id]) {
6523                         delete this._zoomBoundLayers[id];
6524                         this._updateZoomLevels();
6525                 }
6526         },
6527
6528         _updateZoomLevels: function () {
6529                 var minZoom = Infinity,
6530                     maxZoom = -Infinity,
6531                     oldZoomSpan = this._getZoomSpan();
6532
6533                 for (var i in this._zoomBoundLayers) {
6534                         var options = this._zoomBoundLayers[i].options;
6535
6536                         minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
6537                         maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
6538                 }
6539
6540                 this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
6541                 this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
6542
6543                 // @section Map state change events
6544                 // @event zoomlevelschange: Event
6545                 // Fired when the number of zoomlevels on the map is changed due
6546                 // to adding or removing a layer.
6547                 if (oldZoomSpan !== this._getZoomSpan()) {
6548                         this.fire('zoomlevelschange');
6549                 }
6550
6551                 if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
6552                         this.setZoom(this._layersMaxZoom);
6553                 }
6554                 if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
6555                         this.setZoom(this._layersMinZoom);
6556                 }
6557         }
6558 });
6559
6560 /*
6561  * @class LayerGroup
6562  * @aka L.LayerGroup
6563  * @inherits Layer
6564  *
6565  * Used to group several layers and handle them as one. If you add it to the map,
6566  * any layers added or removed from the group will be added/removed on the map as
6567  * well. Extends `Layer`.
6568  *
6569  * @example
6570  *
6571  * ```js
6572  * L.layerGroup([marker1, marker2])
6573  *      .addLayer(polyline)
6574  *      .addTo(map);
6575  * ```
6576  */
6577
6578 var LayerGroup = Layer.extend({
6579
6580         initialize: function (layers) {
6581                 this._layers = {};
6582
6583                 var i, len;
6584
6585                 if (layers) {
6586                         for (i = 0, len = layers.length; i < len; i++) {
6587                                 this.addLayer(layers[i]);
6588                         }
6589                 }
6590         },
6591
6592         // @method addLayer(layer: Layer): this
6593         // Adds the given layer to the group.
6594         addLayer: function (layer) {
6595                 var id = this.getLayerId(layer);
6596
6597                 this._layers[id] = layer;
6598
6599                 if (this._map) {
6600                         this._map.addLayer(layer);
6601                 }
6602
6603                 return this;
6604         },
6605
6606         // @method removeLayer(layer: Layer): this
6607         // Removes the given layer from the group.
6608         // @alternative
6609         // @method removeLayer(id: Number): this
6610         // Removes the layer with the given internal ID from the group.
6611         removeLayer: function (layer) {
6612                 var id = layer in this._layers ? layer : this.getLayerId(layer);
6613
6614                 if (this._map && this._layers[id]) {
6615                         this._map.removeLayer(this._layers[id]);
6616                 }
6617
6618                 delete this._layers[id];
6619
6620                 return this;
6621         },
6622
6623         // @method hasLayer(layer: Layer): Boolean
6624         // Returns `true` if the given layer is currently added to the group.
6625         // @alternative
6626         // @method hasLayer(id: Number): Boolean
6627         // Returns `true` if the given internal ID is currently added to the group.
6628         hasLayer: function (layer) {
6629                 return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
6630         },
6631
6632         // @method clearLayers(): this
6633         // Removes all the layers from the group.
6634         clearLayers: function () {
6635                 for (var i in this._layers) {
6636                         this.removeLayer(this._layers[i]);
6637                 }
6638                 return this;
6639         },
6640
6641         // @method invoke(methodName: String, …): this
6642         // Calls `methodName` on every layer contained in this group, passing any
6643         // additional parameters. Has no effect if the layers contained do not
6644         // implement `methodName`.
6645         invoke: function (methodName) {
6646                 var args = Array.prototype.slice.call(arguments, 1),
6647                     i, layer;
6648
6649                 for (i in this._layers) {
6650                         layer = this._layers[i];
6651
6652                         if (layer[methodName]) {
6653                                 layer[methodName].apply(layer, args);
6654                         }
6655                 }
6656
6657                 return this;
6658         },
6659
6660         onAdd: function (map) {
6661                 for (var i in this._layers) {
6662                         map.addLayer(this._layers[i]);
6663                 }
6664         },
6665
6666         onRemove: function (map) {
6667                 for (var i in this._layers) {
6668                         map.removeLayer(this._layers[i]);
6669                 }
6670         },
6671
6672         // @method eachLayer(fn: Function, context?: Object): this
6673         // Iterates over the layers of the group, optionally specifying context of the iterator function.
6674         // ```js
6675         // group.eachLayer(function (layer) {
6676         //      layer.bindPopup('Hello');
6677         // });
6678         // ```
6679         eachLayer: function (method, context) {
6680                 for (var i in this._layers) {
6681                         method.call(context, this._layers[i]);
6682                 }
6683                 return this;
6684         },
6685
6686         // @method getLayer(id: Number): Layer
6687         // Returns the layer with the given internal ID.
6688         getLayer: function (id) {
6689                 return this._layers[id];
6690         },
6691
6692         // @method getLayers(): Layer[]
6693         // Returns an array of all the layers added to the group.
6694         getLayers: function () {
6695                 var layers = [];
6696
6697                 for (var i in this._layers) {
6698                         layers.push(this._layers[i]);
6699                 }
6700                 return layers;
6701         },
6702
6703         // @method setZIndex(zIndex: Number): this
6704         // Calls `setZIndex` on every layer contained in this group, passing the z-index.
6705         setZIndex: function (zIndex) {
6706                 return this.invoke('setZIndex', zIndex);
6707         },
6708
6709         // @method getLayerId(layer: Layer): Number
6710         // Returns the internal ID for a layer
6711         getLayerId: function (layer) {
6712                 return stamp(layer);
6713         }
6714 });
6715
6716
6717 // @factory L.layerGroup(layers: Layer[])
6718 // Create a layer group, optionally given an initial set of layers.
6719 var layerGroup = function (layers) {
6720         return new LayerGroup(layers);
6721 };
6722
6723 /*
6724  * @class FeatureGroup
6725  * @aka L.FeatureGroup
6726  * @inherits LayerGroup
6727  *
6728  * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
6729  *  * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
6730  *  * Events are propagated to the `FeatureGroup`, so if the group has an event
6731  * handler, it will handle events from any of the layers. This includes mouse events
6732  * and custom events.
6733  *  * Has `layeradd` and `layerremove` events
6734  *
6735  * @example
6736  *
6737  * ```js
6738  * L.featureGroup([marker1, marker2, polyline])
6739  *      .bindPopup('Hello world!')
6740  *      .on('click', function() { alert('Clicked on a member of the group!'); })
6741  *      .addTo(map);
6742  * ```
6743  */
6744
6745 var FeatureGroup = LayerGroup.extend({
6746
6747         addLayer: function (layer) {
6748                 if (this.hasLayer(layer)) {
6749                         return this;
6750                 }
6751
6752                 layer.addEventParent(this);
6753
6754                 LayerGroup.prototype.addLayer.call(this, layer);
6755
6756                 // @event layeradd: LayerEvent
6757                 // Fired when a layer is added to this `FeatureGroup`
6758                 return this.fire('layeradd', {layer: layer});
6759         },
6760
6761         removeLayer: function (layer) {
6762                 if (!this.hasLayer(layer)) {
6763                         return this;
6764                 }
6765                 if (layer in this._layers) {
6766                         layer = this._layers[layer];
6767                 }
6768
6769                 layer.removeEventParent(this);
6770
6771                 LayerGroup.prototype.removeLayer.call(this, layer);
6772
6773                 // @event layerremove: LayerEvent
6774                 // Fired when a layer is removed from this `FeatureGroup`
6775                 return this.fire('layerremove', {layer: layer});
6776         },
6777
6778         // @method setStyle(style: Path options): this
6779         // Sets the given path options to each layer of the group that has a `setStyle` method.
6780         setStyle: function (style) {
6781                 return this.invoke('setStyle', style);
6782         },
6783
6784         // @method bringToFront(): this
6785         // Brings the layer group to the top of all other layers
6786         bringToFront: function () {
6787                 return this.invoke('bringToFront');
6788         },
6789
6790         // @method bringToBack(): this
6791         // Brings the layer group to the top of all other layers
6792         bringToBack: function () {
6793                 return this.invoke('bringToBack');
6794         },
6795
6796         // @method getBounds(): LatLngBounds
6797         // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
6798         getBounds: function () {
6799                 var bounds = new LatLngBounds();
6800
6801                 for (var id in this._layers) {
6802                         var layer = this._layers[id];
6803                         bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
6804                 }
6805                 return bounds;
6806         }
6807 });
6808
6809 // @factory L.featureGroup(layers: Layer[])
6810 // Create a feature group, optionally given an initial set of layers.
6811 var featureGroup = function (layers) {
6812         return new FeatureGroup(layers);
6813 };
6814
6815 /*
6816  * @class Icon
6817  * @aka L.Icon
6818  *
6819  * Represents an icon to provide when creating a marker.
6820  *
6821  * @example
6822  *
6823  * ```js
6824  * var myIcon = L.icon({
6825  *     iconUrl: 'my-icon.png',
6826  *     iconRetinaUrl: 'my-icon@2x.png',
6827  *     iconSize: [38, 95],
6828  *     iconAnchor: [22, 94],
6829  *     popupAnchor: [-3, -76],
6830  *     shadowUrl: 'my-icon-shadow.png',
6831  *     shadowRetinaUrl: 'my-icon-shadow@2x.png',
6832  *     shadowSize: [68, 95],
6833  *     shadowAnchor: [22, 94]
6834  * });
6835  *
6836  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
6837  * ```
6838  *
6839  * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
6840  *
6841  */
6842
6843 var Icon = Class.extend({
6844
6845         /* @section
6846          * @aka Icon options
6847          *
6848          * @option iconUrl: String = null
6849          * **(required)** The URL to the icon image (absolute or relative to your script path).
6850          *
6851          * @option iconRetinaUrl: String = null
6852          * The URL to a retina sized version of the icon image (absolute or relative to your
6853          * script path). Used for Retina screen devices.
6854          *
6855          * @option iconSize: Point = null
6856          * Size of the icon image in pixels.
6857          *
6858          * @option iconAnchor: Point = null
6859          * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
6860          * will be aligned so that this point is at the marker's geographical location. Centered
6861          * by default if size is specified, also can be set in CSS with negative margins.
6862          *
6863          * @option popupAnchor: Point = null
6864          * The coordinates of the point from which popups will "open", relative to the icon anchor.
6865          *
6866          * @option shadowUrl: String = null
6867          * The URL to the icon shadow image. If not specified, no shadow image will be created.
6868          *
6869          * @option shadowRetinaUrl: String = null
6870          *
6871          * @option shadowSize: Point = null
6872          * Size of the shadow image in pixels.
6873          *
6874          * @option shadowAnchor: Point = null
6875          * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
6876          * as iconAnchor if not specified).
6877          *
6878          * @option className: String = ''
6879          * A custom class name to assign to both icon and shadow images. Empty by default.
6880          */
6881
6882         initialize: function (options) {
6883                 setOptions(this, options);
6884         },
6885
6886         // @method createIcon(oldIcon?: HTMLElement): HTMLElement
6887         // Called internally when the icon has to be shown, returns a `<img>` HTML element
6888         // styled according to the options.
6889         createIcon: function (oldIcon) {
6890                 return this._createIcon('icon', oldIcon);
6891         },
6892
6893         // @method createShadow(oldIcon?: HTMLElement): HTMLElement
6894         // As `createIcon`, but for the shadow beneath it.
6895         createShadow: function (oldIcon) {
6896                 return this._createIcon('shadow', oldIcon);
6897         },
6898
6899         _createIcon: function (name, oldIcon) {
6900                 var src = this._getIconUrl(name);
6901
6902                 if (!src) {
6903                         if (name === 'icon') {
6904                                 throw new Error('iconUrl not set in Icon options (see the docs).');
6905                         }
6906                         return null;
6907                 }
6908
6909                 var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
6910                 this._setIconStyles(img, name);
6911
6912                 return img;
6913         },
6914
6915         _setIconStyles: function (img, name) {
6916                 var options = this.options;
6917                 var sizeOption = options[name + 'Size'];
6918
6919                 if (typeof sizeOption === 'number') {
6920                         sizeOption = [sizeOption, sizeOption];
6921                 }
6922
6923                 var size = toPoint(sizeOption),
6924                     anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
6925                             size && size.divideBy(2, true));
6926
6927                 img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
6928
6929                 if (anchor) {
6930                         img.style.marginLeft = (-anchor.x) + 'px';
6931                         img.style.marginTop  = (-anchor.y) + 'px';
6932                 }
6933
6934                 if (size) {
6935                         img.style.width  = size.x + 'px';
6936                         img.style.height = size.y + 'px';
6937                 }
6938         },
6939
6940         _createImg: function (src, el) {
6941                 el = el || document.createElement('img');
6942                 el.src = src;
6943                 return el;
6944         },
6945
6946         _getIconUrl: function (name) {
6947                 return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
6948         }
6949 });
6950
6951
6952 // @factory L.icon(options: Icon options)
6953 // Creates an icon instance with the given options.
6954 function icon(options) {
6955         return new Icon(options);
6956 }
6957
6958 /*
6959  * @miniclass Icon.Default (Icon)
6960  * @aka L.Icon.Default
6961  * @section
6962  *
6963  * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
6964  * no icon is specified. Points to the blue marker image distributed with Leaflet
6965  * releases.
6966  *
6967  * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
6968  * (which is a set of `Icon options`).
6969  *
6970  * If you want to _completely_ replace the default icon, override the
6971  * `L.Marker.prototype.options.icon` with your own icon instead.
6972  */
6973
6974 var IconDefault = Icon.extend({
6975
6976         options: {
6977                 iconUrl:       'marker-icon.png',
6978                 iconRetinaUrl: 'marker-icon-2x.png',
6979                 shadowUrl:     'marker-shadow.png',
6980                 iconSize:    [25, 41],
6981                 iconAnchor:  [12, 41],
6982                 popupAnchor: [1, -34],
6983                 tooltipAnchor: [16, -28],
6984                 shadowSize:  [41, 41]
6985         },
6986
6987         _getIconUrl: function (name) {
6988                 if (!IconDefault.imagePath) {   // Deprecated, backwards-compatibility only
6989                         IconDefault.imagePath = this._detectIconPath();
6990                 }
6991
6992                 // @option imagePath: String
6993                 // `Icon.Default` will try to auto-detect the absolute location of the
6994                 // blue icon images. If you are placing these images in a non-standard
6995                 // way, set this option to point to the right absolute path.
6996                 return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
6997         },
6998
6999         _detectIconPath: function () {
7000                 var el = create$1('div',  'leaflet-default-icon-path', document.body);
7001                 var path = getStyle(el, 'background-image') ||
7002                            getStyle(el, 'backgroundImage');     // IE8
7003
7004                 document.body.removeChild(el);
7005
7006                 if (path === null || path.indexOf('url') !== 0) {
7007                         path = '';
7008                 } else {
7009                         path = path.replace(/^url\([\"\']?/, '').replace(/marker-icon\.png[\"\']?\)$/, '');
7010                 }
7011
7012                 return path;
7013         }
7014 });
7015
7016 /*
7017  * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
7018  */
7019
7020
7021 /* @namespace Marker
7022  * @section Interaction handlers
7023  *
7024  * 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:
7025  *
7026  * ```js
7027  * marker.dragging.disable();
7028  * ```
7029  *
7030  * @property dragging: Handler
7031  * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
7032  */
7033
7034 var MarkerDrag = Handler.extend({
7035         initialize: function (marker) {
7036                 this._marker = marker;
7037         },
7038
7039         addHooks: function () {
7040                 var icon = this._marker._icon;
7041
7042                 if (!this._draggable) {
7043                         this._draggable = new Draggable(icon, icon, true);
7044                 }
7045
7046                 this._draggable.on({
7047                         dragstart: this._onDragStart,
7048                         drag: this._onDrag,
7049                         dragend: this._onDragEnd
7050                 }, this).enable();
7051
7052                 addClass(icon, 'leaflet-marker-draggable');
7053         },
7054
7055         removeHooks: function () {
7056                 this._draggable.off({
7057                         dragstart: this._onDragStart,
7058                         drag: this._onDrag,
7059                         dragend: this._onDragEnd
7060                 }, this).disable();
7061
7062                 if (this._marker._icon) {
7063                         removeClass(this._marker._icon, 'leaflet-marker-draggable');
7064                 }
7065         },
7066
7067         moved: function () {
7068                 return this._draggable && this._draggable._moved;
7069         },
7070
7071         _onDragStart: function () {
7072                 // @section Dragging events
7073                 // @event dragstart: Event
7074                 // Fired when the user starts dragging the marker.
7075
7076                 // @event movestart: Event
7077                 // Fired when the marker starts moving (because of dragging).
7078
7079                 this._oldLatLng = this._marker.getLatLng();
7080                 this._marker
7081                     .closePopup()
7082                     .fire('movestart')
7083                     .fire('dragstart');
7084         },
7085
7086         _onDrag: function (e) {
7087                 var marker = this._marker,
7088                     shadow = marker._shadow,
7089                 iconPos = getPosition(marker._icon),
7090                     latlng = marker._map.layerPointToLatLng(iconPos);
7091
7092                 // update shadow position
7093                 if (shadow) {
7094                         setPosition(shadow, iconPos);
7095                 }
7096
7097                 marker._latlng = latlng;
7098                 e.latlng = latlng;
7099                 e.oldLatLng = this._oldLatLng;
7100
7101                 // @event drag: Event
7102                 // Fired repeatedly while the user drags the marker.
7103                 marker
7104                     .fire('move', e)
7105                     .fire('drag', e);
7106         },
7107
7108         _onDragEnd: function (e) {
7109                 // @event dragend: DragEndEvent
7110                 // Fired when the user stops dragging the marker.
7111
7112                 // @event moveend: Event
7113                 // Fired when the marker stops moving (because of dragging).
7114                 delete this._oldLatLng;
7115                 this._marker
7116                     .fire('moveend')
7117                     .fire('dragend', e);
7118         }
7119 });
7120
7121 /*
7122  * @class Marker
7123  * @inherits Interactive layer
7124  * @aka L.Marker
7125  * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
7126  *
7127  * @example
7128  *
7129  * ```js
7130  * L.marker([50.5, 30.5]).addTo(map);
7131  * ```
7132  */
7133
7134 var Marker = Layer.extend({
7135
7136         // @section
7137         // @aka Marker options
7138         options: {
7139                 // @option icon: Icon = *
7140                 // Icon instance to use for rendering the marker.
7141                 // See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
7142                 // If not specified, a common instance of `L.Icon.Default` is used.
7143                 icon: new IconDefault(),
7144
7145                 // Option inherited from "Interactive layer" abstract class
7146                 interactive: true,
7147
7148                 // @option draggable: Boolean = false
7149                 // Whether the marker is draggable with mouse/touch or not.
7150                 draggable: false,
7151
7152                 // @option keyboard: Boolean = true
7153                 // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
7154                 keyboard: true,
7155
7156                 // @option title: String = ''
7157                 // Text for the browser tooltip that appear on marker hover (no tooltip by default).
7158                 title: '',
7159
7160                 // @option alt: String = ''
7161                 // Text for the `alt` attribute of the icon image (useful for accessibility).
7162                 alt: '',
7163
7164                 // @option zIndexOffset: Number = 0
7165                 // 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).
7166                 zIndexOffset: 0,
7167
7168                 // @option opacity: Number = 1.0
7169                 // The opacity of the marker.
7170                 opacity: 1,
7171
7172                 // @option riseOnHover: Boolean = false
7173                 // If `true`, the marker will get on top of others when you hover the mouse over it.
7174                 riseOnHover: false,
7175
7176                 // @option riseOffset: Number = 250
7177                 // The z-index offset used for the `riseOnHover` feature.
7178                 riseOffset: 250,
7179
7180                 // @option pane: String = 'markerPane'
7181                 // `Map pane` where the markers icon will be added.
7182                 pane: 'markerPane',
7183
7184                 // @option bubblingMouseEvents: Boolean = false
7185                 // When `true`, a mouse event on this marker will trigger the same event on the map
7186                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7187                 bubblingMouseEvents: false
7188         },
7189
7190         /* @section
7191          *
7192          * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
7193          */
7194
7195         initialize: function (latlng, options) {
7196                 setOptions(this, options);
7197                 this._latlng = toLatLng(latlng);
7198         },
7199
7200         onAdd: function (map) {
7201                 this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
7202
7203                 if (this._zoomAnimated) {
7204                         map.on('zoomanim', this._animateZoom, this);
7205                 }
7206
7207                 this._initIcon();
7208                 this.update();
7209         },
7210
7211         onRemove: function (map) {
7212                 if (this.dragging && this.dragging.enabled()) {
7213                         this.options.draggable = true;
7214                         this.dragging.removeHooks();
7215                 }
7216                 delete this.dragging;
7217
7218                 if (this._zoomAnimated) {
7219                         map.off('zoomanim', this._animateZoom, this);
7220                 }
7221
7222                 this._removeIcon();
7223                 this._removeShadow();
7224         },
7225
7226         getEvents: function () {
7227                 return {
7228                         zoom: this.update,
7229                         viewreset: this.update
7230                 };
7231         },
7232
7233         // @method getLatLng: LatLng
7234         // Returns the current geographical position of the marker.
7235         getLatLng: function () {
7236                 return this._latlng;
7237         },
7238
7239         // @method setLatLng(latlng: LatLng): this
7240         // Changes the marker position to the given point.
7241         setLatLng: function (latlng) {
7242                 var oldLatLng = this._latlng;
7243                 this._latlng = toLatLng(latlng);
7244                 this.update();
7245
7246                 // @event move: Event
7247                 // 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`.
7248                 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
7249         },
7250
7251         // @method setZIndexOffset(offset: Number): this
7252         // Changes the [zIndex offset](#marker-zindexoffset) of the marker.
7253         setZIndexOffset: function (offset) {
7254                 this.options.zIndexOffset = offset;
7255                 return this.update();
7256         },
7257
7258         // @method setIcon(icon: Icon): this
7259         // Changes the marker icon.
7260         setIcon: function (icon) {
7261
7262                 this.options.icon = icon;
7263
7264                 if (this._map) {
7265                         this._initIcon();
7266                         this.update();
7267                 }
7268
7269                 if (this._popup) {
7270                         this.bindPopup(this._popup, this._popup.options);
7271                 }
7272
7273                 return this;
7274         },
7275
7276         getElement: function () {
7277                 return this._icon;
7278         },
7279
7280         update: function () {
7281
7282                 if (this._icon) {
7283                         var pos = this._map.latLngToLayerPoint(this._latlng).round();
7284                         this._setPos(pos);
7285                 }
7286
7287                 return this;
7288         },
7289
7290         _initIcon: function () {
7291                 var options = this.options,
7292                     classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
7293
7294                 var icon = options.icon.createIcon(this._icon),
7295                     addIcon = false;
7296
7297                 // if we're not reusing the icon, remove the old one and init new one
7298                 if (icon !== this._icon) {
7299                         if (this._icon) {
7300                                 this._removeIcon();
7301                         }
7302                         addIcon = true;
7303
7304                         if (options.title) {
7305                                 icon.title = options.title;
7306                         }
7307                         if (options.alt) {
7308                                 icon.alt = options.alt;
7309                         }
7310                 }
7311
7312                 addClass(icon, classToAdd);
7313
7314                 if (options.keyboard) {
7315                         icon.tabIndex = '0';
7316                 }
7317
7318                 this._icon = icon;
7319
7320                 if (options.riseOnHover) {
7321                         this.on({
7322                                 mouseover: this._bringToFront,
7323                                 mouseout: this._resetZIndex
7324                         });
7325                 }
7326
7327                 var newShadow = options.icon.createShadow(this._shadow),
7328                     addShadow = false;
7329
7330                 if (newShadow !== this._shadow) {
7331                         this._removeShadow();
7332                         addShadow = true;
7333                 }
7334
7335                 if (newShadow) {
7336                         addClass(newShadow, classToAdd);
7337                         newShadow.alt = '';
7338                 }
7339                 this._shadow = newShadow;
7340
7341
7342                 if (options.opacity < 1) {
7343                         this._updateOpacity();
7344                 }
7345
7346
7347                 if (addIcon) {
7348                         this.getPane().appendChild(this._icon);
7349                 }
7350                 this._initInteraction();
7351                 if (newShadow && addShadow) {
7352                         this.getPane('shadowPane').appendChild(this._shadow);
7353                 }
7354         },
7355
7356         _removeIcon: function () {
7357                 if (this.options.riseOnHover) {
7358                         this.off({
7359                                 mouseover: this._bringToFront,
7360                                 mouseout: this._resetZIndex
7361                         });
7362                 }
7363
7364                 remove(this._icon);
7365                 this.removeInteractiveTarget(this._icon);
7366
7367                 this._icon = null;
7368         },
7369
7370         _removeShadow: function () {
7371                 if (this._shadow) {
7372                         remove(this._shadow);
7373                 }
7374                 this._shadow = null;
7375         },
7376
7377         _setPos: function (pos) {
7378                 setPosition(this._icon, pos);
7379
7380                 if (this._shadow) {
7381                         setPosition(this._shadow, pos);
7382                 }
7383
7384                 this._zIndex = pos.y + this.options.zIndexOffset;
7385
7386                 this._resetZIndex();
7387         },
7388
7389         _updateZIndex: function (offset) {
7390                 this._icon.style.zIndex = this._zIndex + offset;
7391         },
7392
7393         _animateZoom: function (opt) {
7394                 var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
7395
7396                 this._setPos(pos);
7397         },
7398
7399         _initInteraction: function () {
7400
7401                 if (!this.options.interactive) { return; }
7402
7403                 addClass(this._icon, 'leaflet-interactive');
7404
7405                 this.addInteractiveTarget(this._icon);
7406
7407                 if (MarkerDrag) {
7408                         var draggable = this.options.draggable;
7409                         if (this.dragging) {
7410                                 draggable = this.dragging.enabled();
7411                                 this.dragging.disable();
7412                         }
7413
7414                         this.dragging = new MarkerDrag(this);
7415
7416                         if (draggable) {
7417                                 this.dragging.enable();
7418                         }
7419                 }
7420         },
7421
7422         // @method setOpacity(opacity: Number): this
7423         // Changes the opacity of the marker.
7424         setOpacity: function (opacity) {
7425                 this.options.opacity = opacity;
7426                 if (this._map) {
7427                         this._updateOpacity();
7428                 }
7429
7430                 return this;
7431         },
7432
7433         _updateOpacity: function () {
7434                 var opacity = this.options.opacity;
7435
7436                 setOpacity(this._icon, opacity);
7437
7438                 if (this._shadow) {
7439                         setOpacity(this._shadow, opacity);
7440                 }
7441         },
7442
7443         _bringToFront: function () {
7444                 this._updateZIndex(this.options.riseOffset);
7445         },
7446
7447         _resetZIndex: function () {
7448                 this._updateZIndex(0);
7449         },
7450
7451         _getPopupAnchor: function () {
7452                 return this.options.icon.options.popupAnchor || [0, 0];
7453         },
7454
7455         _getTooltipAnchor: function () {
7456                 return this.options.icon.options.tooltipAnchor || [0, 0];
7457         }
7458 });
7459
7460
7461 // factory L.marker(latlng: LatLng, options? : Marker options)
7462
7463 // @factory L.marker(latlng: LatLng, options? : Marker options)
7464 // Instantiates a Marker object given a geographical point and optionally an options object.
7465 function marker(latlng, options) {
7466         return new Marker(latlng, options);
7467 }
7468
7469 /*
7470  * @class Path
7471  * @aka L.Path
7472  * @inherits Interactive layer
7473  *
7474  * An abstract class that contains options and constants shared between vector
7475  * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
7476  */
7477
7478 var Path = Layer.extend({
7479
7480         // @section
7481         // @aka Path options
7482         options: {
7483                 // @option stroke: Boolean = true
7484                 // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
7485                 stroke: true,
7486
7487                 // @option color: String = '#3388ff'
7488                 // Stroke color
7489                 color: '#3388ff',
7490
7491                 // @option weight: Number = 3
7492                 // Stroke width in pixels
7493                 weight: 3,
7494
7495                 // @option opacity: Number = 1.0
7496                 // Stroke opacity
7497                 opacity: 1,
7498
7499                 // @option lineCap: String= 'round'
7500                 // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
7501                 lineCap: 'round',
7502
7503                 // @option lineJoin: String = 'round'
7504                 // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
7505                 lineJoin: 'round',
7506
7507                 // @option dashArray: String = null
7508                 // 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).
7509                 dashArray: null,
7510
7511                 // @option dashOffset: String = null
7512                 // 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).
7513                 dashOffset: null,
7514
7515                 // @option fill: Boolean = depends
7516                 // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
7517                 fill: false,
7518
7519                 // @option fillColor: String = *
7520                 // Fill color. Defaults to the value of the [`color`](#path-color) option
7521                 fillColor: null,
7522
7523                 // @option fillOpacity: Number = 0.2
7524                 // Fill opacity.
7525                 fillOpacity: 0.2,
7526
7527                 // @option fillRule: String = 'evenodd'
7528                 // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
7529                 fillRule: 'evenodd',
7530
7531                 // className: '',
7532
7533                 // Option inherited from "Interactive layer" abstract class
7534                 interactive: true,
7535
7536                 // @option bubblingMouseEvents: Boolean = true
7537                 // When `true`, a mouse event on this path will trigger the same event on the map
7538                 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7539                 bubblingMouseEvents: true
7540         },
7541
7542         beforeAdd: function (map) {
7543                 // Renderer is set here because we need to call renderer.getEvents
7544                 // before this.getEvents.
7545                 this._renderer = map.getRenderer(this);
7546         },
7547
7548         onAdd: function () {
7549                 this._renderer._initPath(this);
7550                 this._reset();
7551                 this._renderer._addPath(this);
7552         },
7553
7554         onRemove: function () {
7555                 this._renderer._removePath(this);
7556         },
7557
7558         // @method redraw(): this
7559         // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
7560         redraw: function () {
7561                 if (this._map) {
7562                         this._renderer._updatePath(this);
7563                 }
7564                 return this;
7565         },
7566
7567         // @method setStyle(style: Path options): this
7568         // Changes the appearance of a Path based on the options in the `Path options` object.
7569         setStyle: function (style) {
7570                 setOptions(this, style);
7571                 if (this._renderer) {
7572                         this._renderer._updateStyle(this);
7573                 }
7574                 return this;
7575         },
7576
7577         // @method bringToFront(): this
7578         // Brings the layer to the top of all path layers.
7579         bringToFront: function () {
7580                 if (this._renderer) {
7581                         this._renderer._bringToFront(this);
7582                 }
7583                 return this;
7584         },
7585
7586         // @method bringToBack(): this
7587         // Brings the layer to the bottom of all path layers.
7588         bringToBack: function () {
7589                 if (this._renderer) {
7590                         this._renderer._bringToBack(this);
7591                 }
7592                 return this;
7593         },
7594
7595         getElement: function () {
7596                 return this._path;
7597         },
7598
7599         _reset: function () {
7600                 // defined in child classes
7601                 this._project();
7602                 this._update();
7603         },
7604
7605         _clickTolerance: function () {
7606                 // used when doing hit detection for Canvas layers
7607                 return (this.options.stroke ? this.options.weight / 2 : 0) + (touch ? 10 : 0);
7608         }
7609 });
7610
7611 /*
7612  * @class CircleMarker
7613  * @aka L.CircleMarker
7614  * @inherits Path
7615  *
7616  * A circle of a fixed size with radius specified in pixels. Extends `Path`.
7617  */
7618
7619 var CircleMarker = Path.extend({
7620
7621         // @section
7622         // @aka CircleMarker options
7623         options: {
7624                 fill: true,
7625
7626                 // @option radius: Number = 10
7627                 // Radius of the circle marker, in pixels
7628                 radius: 10
7629         },
7630
7631         initialize: function (latlng, options) {
7632                 setOptions(this, options);
7633                 this._latlng = toLatLng(latlng);
7634                 this._radius = this.options.radius;
7635         },
7636
7637         // @method setLatLng(latLng: LatLng): this
7638         // Sets the position of a circle marker to a new location.
7639         setLatLng: function (latlng) {
7640                 this._latlng = toLatLng(latlng);
7641                 this.redraw();
7642                 return this.fire('move', {latlng: this._latlng});
7643         },
7644
7645         // @method getLatLng(): LatLng
7646         // Returns the current geographical position of the circle marker
7647         getLatLng: function () {
7648                 return this._latlng;
7649         },
7650
7651         // @method setRadius(radius: Number): this
7652         // Sets the radius of a circle marker. Units are in pixels.
7653         setRadius: function (radius) {
7654                 this.options.radius = this._radius = radius;
7655                 return this.redraw();
7656         },
7657
7658         // @method getRadius(): Number
7659         // Returns the current radius of the circle
7660         getRadius: function () {
7661                 return this._radius;
7662         },
7663
7664         setStyle : function (options) {
7665                 var radius = options && options.radius || this._radius;
7666                 Path.prototype.setStyle.call(this, options);
7667                 this.setRadius(radius);
7668                 return this;
7669         },
7670
7671         _project: function () {
7672                 this._point = this._map.latLngToLayerPoint(this._latlng);
7673                 this._updateBounds();
7674         },
7675
7676         _updateBounds: function () {
7677                 var r = this._radius,
7678                     r2 = this._radiusY || r,
7679                     w = this._clickTolerance(),
7680                     p = [r + w, r2 + w];
7681                 this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
7682         },
7683
7684         _update: function () {
7685                 if (this._map) {
7686                         this._updatePath();
7687                 }
7688         },
7689
7690         _updatePath: function () {
7691                 this._renderer._updateCircle(this);
7692         },
7693
7694         _empty: function () {
7695                 return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
7696         },
7697
7698         // Needed by the `Canvas` renderer for interactivity
7699         _containsPoint: function (p) {
7700                 return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
7701         }
7702 });
7703
7704
7705 // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
7706 // Instantiates a circle marker object given a geographical point, and an optional options object.
7707 function circleMarker(latlng, options) {
7708         return new CircleMarker(latlng, options);
7709 }
7710
7711 /*
7712  * @class Circle
7713  * @aka L.Circle
7714  * @inherits CircleMarker
7715  *
7716  * A class for drawing circle overlays on a map. Extends `CircleMarker`.
7717  *
7718  * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
7719  *
7720  * @example
7721  *
7722  * ```js
7723  * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
7724  * ```
7725  */
7726
7727 var Circle = CircleMarker.extend({
7728
7729         initialize: function (latlng, options, legacyOptions) {
7730                 if (typeof options === 'number') {
7731                         // Backwards compatibility with 0.7.x factory (latlng, radius, options?)
7732                         options = extend({}, legacyOptions, {radius: options});
7733                 }
7734                 setOptions(this, options);
7735                 this._latlng = toLatLng(latlng);
7736
7737                 if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
7738
7739                 // @section
7740                 // @aka Circle options
7741                 // @option radius: Number; Radius of the circle, in meters.
7742                 this._mRadius = this.options.radius;
7743         },
7744
7745         // @method setRadius(radius: Number): this
7746         // Sets the radius of a circle. Units are in meters.
7747         setRadius: function (radius) {
7748                 this._mRadius = radius;
7749                 return this.redraw();
7750         },
7751
7752         // @method getRadius(): Number
7753         // Returns the current radius of a circle. Units are in meters.
7754         getRadius: function () {
7755                 return this._mRadius;
7756         },
7757
7758         // @method getBounds(): LatLngBounds
7759         // Returns the `LatLngBounds` of the path.
7760         getBounds: function () {
7761                 var half = [this._radius, this._radiusY || this._radius];
7762
7763                 return new LatLngBounds(
7764                         this._map.layerPointToLatLng(this._point.subtract(half)),
7765                         this._map.layerPointToLatLng(this._point.add(half)));
7766         },
7767
7768         setStyle: Path.prototype.setStyle,
7769
7770         _project: function () {
7771
7772                 var lng = this._latlng.lng,
7773                     lat = this._latlng.lat,
7774                     map = this._map,
7775                     crs = map.options.crs;
7776
7777                 if (crs.distance === Earth.distance) {
7778                         var d = Math.PI / 180,
7779                             latR = (this._mRadius / Earth.R) / d,
7780                             top = map.project([lat + latR, lng]),
7781                             bottom = map.project([lat - latR, lng]),
7782                             p = top.add(bottom).divideBy(2),
7783                             lat2 = map.unproject(p).lat,
7784                             lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
7785                                     (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
7786
7787                         if (isNaN(lngR) || lngR === 0) {
7788                                 lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
7789                         }
7790
7791                         this._point = p.subtract(map.getPixelOrigin());
7792                         this._radius = isNaN(lngR) ? 0 : Math.max(Math.round(p.x - map.project([lat2, lng - lngR]).x), 1);
7793                         this._radiusY = Math.max(Math.round(p.y - top.y), 1);
7794
7795                 } else {
7796                         var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
7797
7798                         this._point = map.latLngToLayerPoint(this._latlng);
7799                         this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
7800                 }
7801
7802                 this._updateBounds();
7803         }
7804 });
7805
7806 // @factory L.circle(latlng: LatLng, options?: Circle options)
7807 // Instantiates a circle object given a geographical point, and an options object
7808 // which contains the circle radius.
7809 // @alternative
7810 // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
7811 // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
7812 // Do not use in new applications or plugins.
7813 function circle(latlng, options, legacyOptions) {
7814         return new Circle(latlng, options, legacyOptions);
7815 }
7816
7817 /*
7818  * @class Polyline
7819  * @aka L.Polyline
7820  * @inherits Path
7821  *
7822  * A class for drawing polyline overlays on a map. Extends `Path`.
7823  *
7824  * @example
7825  *
7826  * ```js
7827  * // create a red polyline from an array of LatLng points
7828  * var latlngs = [
7829  *      [45.51, -122.68],
7830  *      [37.77, -122.43],
7831  *      [34.04, -118.2]
7832  * ];
7833  *
7834  * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
7835  *
7836  * // zoom the map to the polyline
7837  * map.fitBounds(polyline.getBounds());
7838  * ```
7839  *
7840  * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
7841  *
7842  * ```js
7843  * // create a red polyline from an array of arrays of LatLng points
7844  * var latlngs = [
7845  *      [[45.51, -122.68],
7846  *       [37.77, -122.43],
7847  *       [34.04, -118.2]],
7848  *      [[40.78, -73.91],
7849  *       [41.83, -87.62],
7850  *       [32.76, -96.72]]
7851  * ];
7852  * ```
7853  */
7854
7855
7856 var Polyline = Path.extend({
7857
7858         // @section
7859         // @aka Polyline options
7860         options: {
7861                 // @option smoothFactor: Number = 1.0
7862                 // How much to simplify the polyline on each zoom level. More means
7863                 // better performance and smoother look, and less means more accurate representation.
7864                 smoothFactor: 1.0,
7865
7866                 // @option noClip: Boolean = false
7867                 // Disable polyline clipping.
7868                 noClip: false
7869         },
7870
7871         initialize: function (latlngs, options) {
7872                 setOptions(this, options);
7873                 this._setLatLngs(latlngs);
7874         },
7875
7876         // @method getLatLngs(): LatLng[]
7877         // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
7878         getLatLngs: function () {
7879                 return this._latlngs;
7880         },
7881
7882         // @method setLatLngs(latlngs: LatLng[]): this
7883         // Replaces all the points in the polyline with the given array of geographical points.
7884         setLatLngs: function (latlngs) {
7885                 this._setLatLngs(latlngs);
7886                 return this.redraw();
7887         },
7888
7889         // @method isEmpty(): Boolean
7890         // Returns `true` if the Polyline has no LatLngs.
7891         isEmpty: function () {
7892                 return !this._latlngs.length;
7893         },
7894
7895         closestLayerPoint: function (p) {
7896                 var minDistance = Infinity,
7897                     minPoint = null,
7898                     closest = _sqClosestPointOnSegment,
7899                     p1, p2;
7900
7901                 for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
7902                         var points = this._parts[j];
7903
7904                         for (var i = 1, len = points.length; i < len; i++) {
7905                                 p1 = points[i - 1];
7906                                 p2 = points[i];
7907
7908                                 var sqDist = closest(p, p1, p2, true);
7909
7910                                 if (sqDist < minDistance) {
7911                                         minDistance = sqDist;
7912                                         minPoint = closest(p, p1, p2);
7913                                 }
7914                         }
7915                 }
7916                 if (minPoint) {
7917                         minPoint.distance = Math.sqrt(minDistance);
7918                 }
7919                 return minPoint;
7920         },
7921
7922         // @method getCenter(): LatLng
7923         // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
7924         getCenter: function () {
7925                 // throws error when not yet added to map as this center calculation requires projected coordinates
7926                 if (!this._map) {
7927                         throw new Error('Must add layer to map before using getCenter()');
7928                 }
7929
7930                 var i, halfDist, segDist, dist, p1, p2, ratio,
7931                     points = this._rings[0],
7932                     len = points.length;
7933
7934                 if (!len) { return null; }
7935
7936                 // polyline centroid algorithm; only uses the first ring if there are multiple
7937
7938                 for (i = 0, halfDist = 0; i < len - 1; i++) {
7939                         halfDist += points[i].distanceTo(points[i + 1]) / 2;
7940                 }
7941
7942                 // The line is so small in the current view that all points are on the same pixel.
7943                 if (halfDist === 0) {
7944                         return this._map.layerPointToLatLng(points[0]);
7945                 }
7946
7947                 for (i = 0, dist = 0; i < len - 1; i++) {
7948                         p1 = points[i];
7949                         p2 = points[i + 1];
7950                         segDist = p1.distanceTo(p2);
7951                         dist += segDist;
7952
7953                         if (dist > halfDist) {
7954                                 ratio = (dist - halfDist) / segDist;
7955                                 return this._map.layerPointToLatLng([
7956                                         p2.x - ratio * (p2.x - p1.x),
7957                                         p2.y - ratio * (p2.y - p1.y)
7958                                 ]);
7959                         }
7960                 }
7961         },
7962
7963         // @method getBounds(): LatLngBounds
7964         // Returns the `LatLngBounds` of the path.
7965         getBounds: function () {
7966                 return this._bounds;
7967         },
7968
7969         // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
7970         // Adds a given point to the polyline. By default, adds to the first ring of
7971         // the polyline in case of a multi-polyline, but can be overridden by passing
7972         // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
7973         addLatLng: function (latlng, latlngs) {
7974                 latlngs = latlngs || this._defaultShape();
7975                 latlng = toLatLng(latlng);
7976                 latlngs.push(latlng);
7977                 this._bounds.extend(latlng);
7978                 return this.redraw();
7979         },
7980
7981         _setLatLngs: function (latlngs) {
7982                 this._bounds = new LatLngBounds();
7983                 this._latlngs = this._convertLatLngs(latlngs);
7984         },
7985
7986         _defaultShape: function () {
7987                 return _flat(this._latlngs) ? this._latlngs : this._latlngs[0];
7988         },
7989
7990         // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
7991         _convertLatLngs: function (latlngs) {
7992                 var result = [],
7993                     flat = _flat(latlngs);
7994
7995                 for (var i = 0, len = latlngs.length; i < len; i++) {
7996                         if (flat) {
7997                                 result[i] = toLatLng(latlngs[i]);
7998                                 this._bounds.extend(result[i]);
7999                         } else {
8000                                 result[i] = this._convertLatLngs(latlngs[i]);
8001                         }
8002                 }
8003
8004                 return result;
8005         },
8006
8007         _project: function () {
8008                 var pxBounds = new Bounds();
8009                 this._rings = [];
8010                 this._projectLatlngs(this._latlngs, this._rings, pxBounds);
8011
8012                 var w = this._clickTolerance(),
8013                     p = new Point(w, w);
8014
8015                 if (this._bounds.isValid() && pxBounds.isValid()) {
8016                         pxBounds.min._subtract(p);
8017                         pxBounds.max._add(p);
8018                         this._pxBounds = pxBounds;
8019                 }
8020         },
8021
8022         // recursively turns latlngs into a set of rings with projected coordinates
8023         _projectLatlngs: function (latlngs, result, projectedBounds) {
8024                 var flat = latlngs[0] instanceof LatLng,
8025                     len = latlngs.length,
8026                     i, ring;
8027
8028                 if (flat) {
8029                         ring = [];
8030                         for (i = 0; i < len; i++) {
8031                                 ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
8032                                 projectedBounds.extend(ring[i]);
8033                         }
8034                         result.push(ring);
8035                 } else {
8036                         for (i = 0; i < len; i++) {
8037                                 this._projectLatlngs(latlngs[i], result, projectedBounds);
8038                         }
8039                 }
8040         },
8041
8042         // clip polyline by renderer bounds so that we have less to render for performance
8043         _clipPoints: function () {
8044                 var bounds = this._renderer._bounds;
8045
8046                 this._parts = [];
8047                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8048                         return;
8049                 }
8050
8051                 if (this.options.noClip) {
8052                         this._parts = this._rings;
8053                         return;
8054                 }
8055
8056                 var parts = this._parts,
8057                     i, j, k, len, len2, segment, points;
8058
8059                 for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
8060                         points = this._rings[i];
8061
8062                         for (j = 0, len2 = points.length; j < len2 - 1; j++) {
8063                                 segment = clipSegment(points[j], points[j + 1], bounds, j, true);
8064
8065                                 if (!segment) { continue; }
8066
8067                                 parts[k] = parts[k] || [];
8068                                 parts[k].push(segment[0]);
8069
8070                                 // if segment goes out of screen, or it's the last one, it's the end of the line part
8071                                 if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
8072                                         parts[k].push(segment[1]);
8073                                         k++;
8074                                 }
8075                         }
8076                 }
8077         },
8078
8079         // simplify each clipped part of the polyline for performance
8080         _simplifyPoints: function () {
8081                 var parts = this._parts,
8082                     tolerance = this.options.smoothFactor;
8083
8084                 for (var i = 0, len = parts.length; i < len; i++) {
8085                         parts[i] = simplify(parts[i], tolerance);
8086                 }
8087         },
8088
8089         _update: function () {
8090                 if (!this._map) { return; }
8091
8092                 this._clipPoints();
8093                 this._simplifyPoints();
8094                 this._updatePath();
8095         },
8096
8097         _updatePath: function () {
8098                 this._renderer._updatePoly(this);
8099         },
8100
8101         // Needed by the `Canvas` renderer for interactivity
8102         _containsPoint: function (p, closed) {
8103                 var i, j, k, len, len2, part,
8104                     w = this._clickTolerance();
8105
8106                 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8107
8108                 // hit detection for polylines
8109                 for (i = 0, len = this._parts.length; i < len; i++) {
8110                         part = this._parts[i];
8111
8112                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8113                                 if (!closed && (j === 0)) { continue; }
8114
8115                                 if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
8116                                         return true;
8117                                 }
8118                         }
8119                 }
8120                 return false;
8121         }
8122 });
8123
8124 // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
8125 // Instantiates a polyline object given an array of geographical points and
8126 // optionally an options object. You can create a `Polyline` object with
8127 // multiple separate lines (`MultiPolyline`) by passing an array of arrays
8128 // of geographic points.
8129 function polyline(latlngs, options) {
8130         return new Polyline(latlngs, options);
8131 }
8132
8133 /*
8134  * @class Polygon
8135  * @aka L.Polygon
8136  * @inherits Polyline
8137  *
8138  * A class for drawing polygon overlays on a map. Extends `Polyline`.
8139  *
8140  * 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.
8141  *
8142  *
8143  * @example
8144  *
8145  * ```js
8146  * // create a red polygon from an array of LatLng points
8147  * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
8148  *
8149  * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
8150  *
8151  * // zoom the map to the polygon
8152  * map.fitBounds(polygon.getBounds());
8153  * ```
8154  *
8155  * 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:
8156  *
8157  * ```js
8158  * var latlngs = [
8159  *   [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8160  *   [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8161  * ];
8162  * ```
8163  *
8164  * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
8165  *
8166  * ```js
8167  * var latlngs = [
8168  *   [ // first polygon
8169  *     [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8170  *     [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8171  *   ],
8172  *   [ // second polygon
8173  *     [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
8174  *   ]
8175  * ];
8176  * ```
8177  */
8178
8179 var Polygon = Polyline.extend({
8180
8181         options: {
8182                 fill: true
8183         },
8184
8185         isEmpty: function () {
8186                 return !this._latlngs.length || !this._latlngs[0].length;
8187         },
8188
8189         getCenter: function () {
8190                 // throws error when not yet added to map as this center calculation requires projected coordinates
8191                 if (!this._map) {
8192                         throw new Error('Must add layer to map before using getCenter()');
8193                 }
8194
8195                 var i, j, p1, p2, f, area, x, y, center,
8196                     points = this._rings[0],
8197                     len = points.length;
8198
8199                 if (!len) { return null; }
8200
8201                 // polygon centroid algorithm; only uses the first ring if there are multiple
8202
8203                 area = x = y = 0;
8204
8205                 for (i = 0, j = len - 1; i < len; j = i++) {
8206                         p1 = points[i];
8207                         p2 = points[j];
8208
8209                         f = p1.y * p2.x - p2.y * p1.x;
8210                         x += (p1.x + p2.x) * f;
8211                         y += (p1.y + p2.y) * f;
8212                         area += f * 3;
8213                 }
8214
8215                 if (area === 0) {
8216                         // Polygon is so small that all points are on same pixel.
8217                         center = points[0];
8218                 } else {
8219                         center = [x / area, y / area];
8220                 }
8221                 return this._map.layerPointToLatLng(center);
8222         },
8223
8224         _convertLatLngs: function (latlngs) {
8225                 var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
8226                     len = result.length;
8227
8228                 // remove last point if it equals first one
8229                 if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
8230                         result.pop();
8231                 }
8232                 return result;
8233         },
8234
8235         _setLatLngs: function (latlngs) {
8236                 Polyline.prototype._setLatLngs.call(this, latlngs);
8237                 if (_flat(this._latlngs)) {
8238                         this._latlngs = [this._latlngs];
8239                 }
8240         },
8241
8242         _defaultShape: function () {
8243                 return _flat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
8244         },
8245
8246         _clipPoints: function () {
8247                 // polygons need a different clipping algorithm so we redefine that
8248
8249                 var bounds = this._renderer._bounds,
8250                     w = this.options.weight,
8251                     p = new Point(w, w);
8252
8253                 // increase clip padding by stroke width to avoid stroke on clip edges
8254                 bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
8255
8256                 this._parts = [];
8257                 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8258                         return;
8259                 }
8260
8261                 if (this.options.noClip) {
8262                         this._parts = this._rings;
8263                         return;
8264                 }
8265
8266                 for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
8267                         clipped = clipPolygon(this._rings[i], bounds, true);
8268                         if (clipped.length) {
8269                                 this._parts.push(clipped);
8270                         }
8271                 }
8272         },
8273
8274         _updatePath: function () {
8275                 this._renderer._updatePoly(this, true);
8276         },
8277
8278         // Needed by the `Canvas` renderer for interactivity
8279         _containsPoint: function (p) {
8280                 var inside = false,
8281                     part, p1, p2, i, j, k, len, len2;
8282
8283                 if (!this._pxBounds.contains(p)) { return false; }
8284
8285                 // ray casting algorithm for detecting if point is in polygon
8286                 for (i = 0, len = this._parts.length; i < len; i++) {
8287                         part = this._parts[i];
8288
8289                         for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8290                                 p1 = part[j];
8291                                 p2 = part[k];
8292
8293                                 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)) {
8294                                         inside = !inside;
8295                                 }
8296                         }
8297                 }
8298
8299                 // also check if it's on polygon stroke
8300                 return inside || Polyline.prototype._containsPoint.call(this, p, true);
8301         }
8302
8303 });
8304
8305
8306 // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
8307 function polygon(latlngs, options) {
8308         return new Polygon(latlngs, options);
8309 }
8310
8311 /*
8312  * @class GeoJSON
8313  * @aka L.GeoJSON
8314  * @inherits FeatureGroup
8315  *
8316  * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
8317  * GeoJSON data and display it on the map. Extends `FeatureGroup`.
8318  *
8319  * @example
8320  *
8321  * ```js
8322  * L.geoJSON(data, {
8323  *      style: function (feature) {
8324  *              return {color: feature.properties.color};
8325  *      }
8326  * }).bindPopup(function (layer) {
8327  *      return layer.feature.properties.description;
8328  * }).addTo(map);
8329  * ```
8330  */
8331
8332 var GeoJSON = FeatureGroup.extend({
8333
8334         /* @section
8335          * @aka GeoJSON options
8336          *
8337          * @option pointToLayer: Function = *
8338          * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
8339          * called when data is added, passing the GeoJSON point feature and its `LatLng`.
8340          * The default is to spawn a default `Marker`:
8341          * ```js
8342          * function(geoJsonPoint, latlng) {
8343          *      return L.marker(latlng);
8344          * }
8345          * ```
8346          *
8347          * @option style: Function = *
8348          * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
8349          * called internally when data is added.
8350          * The default value is to not override any defaults:
8351          * ```js
8352          * function (geoJsonFeature) {
8353          *      return {}
8354          * }
8355          * ```
8356          *
8357          * @option onEachFeature: Function = *
8358          * A `Function` that will be called once for each created `Feature`, after it has
8359          * been created and styled. Useful for attaching events and popups to features.
8360          * The default is to do nothing with the newly created layers:
8361          * ```js
8362          * function (feature, layer) {}
8363          * ```
8364          *
8365          * @option filter: Function = *
8366          * A `Function` that will be used to decide whether to include a feature or not.
8367          * The default is to include all features:
8368          * ```js
8369          * function (geoJsonFeature) {
8370          *      return true;
8371          * }
8372          * ```
8373          * Note: dynamically changing the `filter` option will have effect only on newly
8374          * added data. It will _not_ re-evaluate already included features.
8375          *
8376          * @option coordsToLatLng: Function = *
8377          * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
8378          * The default is the `coordsToLatLng` static method.
8379          */
8380
8381         initialize: function (geojson, options) {
8382                 setOptions(this, options);
8383
8384                 this._layers = {};
8385
8386                 if (geojson) {
8387                         this.addData(geojson);
8388                 }
8389         },
8390
8391         // @method addData( <GeoJSON> data ): this
8392         // Adds a GeoJSON object to the layer.
8393         addData: function (geojson) {
8394                 var features = isArray(geojson) ? geojson : geojson.features,
8395                     i, len, feature;
8396
8397                 if (features) {
8398                         for (i = 0, len = features.length; i < len; i++) {
8399                                 // only add this if geometry or geometries are set and not null
8400                                 feature = features[i];
8401                                 if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
8402                                         this.addData(feature);
8403                                 }
8404                         }
8405                         return this;
8406                 }
8407
8408                 var options = this.options;
8409
8410                 if (options.filter && !options.filter(geojson)) { return this; }
8411
8412                 var layer = geometryToLayer(geojson, options);
8413                 if (!layer) {
8414                         return this;
8415                 }
8416                 layer.feature = asFeature(geojson);
8417
8418                 layer.defaultOptions = layer.options;
8419                 this.resetStyle(layer);
8420
8421                 if (options.onEachFeature) {
8422                         options.onEachFeature(geojson, layer);
8423                 }
8424
8425                 return this.addLayer(layer);
8426         },
8427
8428         // @method resetStyle( <Path> layer ): this
8429         // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
8430         resetStyle: function (layer) {
8431                 // reset any custom styles
8432                 layer.options = extend({}, layer.defaultOptions);
8433                 this._setLayerStyle(layer, this.options.style);
8434                 return this;
8435         },
8436
8437         // @method setStyle( <Function> style ): this
8438         // Changes styles of GeoJSON vector layers with the given style function.
8439         setStyle: function (style) {
8440                 return this.eachLayer(function (layer) {
8441                         this._setLayerStyle(layer, style);
8442                 }, this);
8443         },
8444
8445         _setLayerStyle: function (layer, style) {
8446                 if (typeof style === 'function') {
8447                         style = style(layer.feature);
8448                 }
8449                 if (layer.setStyle) {
8450                         layer.setStyle(style);
8451                 }
8452         }
8453 });
8454
8455 // @section
8456 // There are several static functions which can be called without instantiating L.GeoJSON:
8457
8458 // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
8459 // Creates a `Layer` from a given GeoJSON feature. Can use a custom
8460 // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
8461 // functions if provided as options.
8462 function geometryToLayer(geojson, options) {
8463
8464         var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
8465             coords = geometry ? geometry.coordinates : null,
8466             layers = [],
8467             pointToLayer = options && options.pointToLayer,
8468             _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
8469             latlng, latlngs, i, len;
8470
8471         if (!coords && !geometry) {
8472                 return null;
8473         }
8474
8475         switch (geometry.type) {
8476         case 'Point':
8477                 latlng = _coordsToLatLng(coords);
8478                 return pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng);
8479
8480         case 'MultiPoint':
8481                 for (i = 0, len = coords.length; i < len; i++) {
8482                         latlng = _coordsToLatLng(coords[i]);
8483                         layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng));
8484                 }
8485                 return new FeatureGroup(layers);
8486
8487         case 'LineString':
8488         case 'MultiLineString':
8489                 latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
8490                 return new Polyline(latlngs, options);
8491
8492         case 'Polygon':
8493         case 'MultiPolygon':
8494                 latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
8495                 return new Polygon(latlngs, options);
8496
8497         case 'GeometryCollection':
8498                 for (i = 0, len = geometry.geometries.length; i < len; i++) {
8499                         var layer = geometryToLayer({
8500                                 geometry: geometry.geometries[i],
8501                                 type: 'Feature',
8502                                 properties: geojson.properties
8503                         }, options);
8504
8505                         if (layer) {
8506                                 layers.push(layer);
8507                         }
8508                 }
8509                 return new FeatureGroup(layers);
8510
8511         default:
8512                 throw new Error('Invalid GeoJSON object.');
8513         }
8514 }
8515
8516 // @function coordsToLatLng(coords: Array): LatLng
8517 // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
8518 // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
8519 function coordsToLatLng(coords) {
8520         return new LatLng(coords[1], coords[0], coords[2]);
8521 }
8522
8523 // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
8524 // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
8525 // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
8526 // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
8527 function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
8528         var latlngs = [];
8529
8530         for (var i = 0, len = coords.length, latlng; i < len; i++) {
8531                 latlng = levelsDeep ?
8532                                 coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
8533                                 (_coordsToLatLng || coordsToLatLng)(coords[i]);
8534
8535                 latlngs.push(latlng);
8536         }
8537
8538         return latlngs;
8539 }
8540
8541 // @function latLngToCoords(latlng: LatLng, precision?: Number): Array
8542 // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
8543 function latLngToCoords(latlng, precision) {
8544         precision = typeof precision === 'number' ? precision : 6;
8545         return latlng.alt !== undefined ?
8546                         [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
8547                         [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
8548 }
8549
8550 // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
8551 // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
8552 // `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.
8553 function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
8554         var coords = [];
8555
8556         for (var i = 0, len = latlngs.length; i < len; i++) {
8557                 coords.push(levelsDeep ?
8558                         latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) :
8559                         latLngToCoords(latlngs[i], precision));
8560         }
8561
8562         if (!levelsDeep && closed) {
8563                 coords.push(coords[0]);
8564         }
8565
8566         return coords;
8567 }
8568
8569 function getFeature(layer, newGeometry) {
8570         return layer.feature ?
8571                         extend({}, layer.feature, {geometry: newGeometry}) :
8572                         asFeature(newGeometry);
8573 }
8574
8575 // @function asFeature(geojson: Object): Object
8576 // Normalize GeoJSON geometries/features into GeoJSON features.
8577 function asFeature(geojson) {
8578         if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
8579                 return geojson;
8580         }
8581
8582         return {
8583                 type: 'Feature',
8584                 properties: {},
8585                 geometry: geojson
8586         };
8587 }
8588
8589 var PointToGeoJSON = {
8590         toGeoJSON: function (precision) {
8591                 return getFeature(this, {
8592                         type: 'Point',
8593                         coordinates: latLngToCoords(this.getLatLng(), precision)
8594                 });
8595         }
8596 };
8597
8598 // @namespace Marker
8599 // @method toGeoJSON(): Object
8600 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
8601 Marker.include(PointToGeoJSON);
8602
8603 // @namespace CircleMarker
8604 // @method toGeoJSON(): Object
8605 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
8606 Circle.include(PointToGeoJSON);
8607 CircleMarker.include(PointToGeoJSON);
8608
8609
8610 // @namespace Polyline
8611 // @method toGeoJSON(): Object
8612 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
8613 Polyline.include({
8614         toGeoJSON: function (precision) {
8615                 var multi = !_flat(this._latlngs);
8616
8617                 var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
8618
8619                 return getFeature(this, {
8620                         type: (multi ? 'Multi' : '') + 'LineString',
8621                         coordinates: coords
8622                 });
8623         }
8624 });
8625
8626 // @namespace Polygon
8627 // @method toGeoJSON(): Object
8628 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
8629 Polygon.include({
8630         toGeoJSON: function (precision) {
8631                 var holes = !_flat(this._latlngs),
8632                     multi = holes && !_flat(this._latlngs[0]);
8633
8634                 var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
8635
8636                 if (!holes) {
8637                         coords = [coords];
8638                 }
8639
8640                 return getFeature(this, {
8641                         type: (multi ? 'Multi' : '') + 'Polygon',
8642                         coordinates: coords
8643                 });
8644         }
8645 });
8646
8647
8648 // @namespace LayerGroup
8649 LayerGroup.include({
8650         toMultiPoint: function (precision) {
8651                 var coords = [];
8652
8653                 this.eachLayer(function (layer) {
8654                         coords.push(layer.toGeoJSON(precision).geometry.coordinates);
8655                 });
8656
8657                 return getFeature(this, {
8658                         type: 'MultiPoint',
8659                         coordinates: coords
8660                 });
8661         },
8662
8663         // @method toGeoJSON(): Object
8664         // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
8665         toGeoJSON: function (precision) {
8666
8667                 var type = this.feature && this.feature.geometry && this.feature.geometry.type;
8668
8669                 if (type === 'MultiPoint') {
8670                         return this.toMultiPoint(precision);
8671                 }
8672
8673                 var isGeometryCollection = type === 'GeometryCollection',
8674                     jsons = [];
8675
8676                 this.eachLayer(function (layer) {
8677                         if (layer.toGeoJSON) {
8678                                 var json = layer.toGeoJSON(precision);
8679                                 if (isGeometryCollection) {
8680                                         jsons.push(json.geometry);
8681                                 } else {
8682                                         var feature = asFeature(json);
8683                                         // Squash nested feature collections
8684                                         if (feature.type === 'FeatureCollection') {
8685                                                 jsons.push.apply(jsons, feature.features);
8686                                         } else {
8687                                                 jsons.push(feature);
8688                                         }
8689                                 }
8690                         }
8691                 });
8692
8693                 if (isGeometryCollection) {
8694                         return getFeature(this, {
8695                                 geometries: jsons,
8696                                 type: 'GeometryCollection'
8697                         });
8698                 }
8699
8700                 return {
8701                         type: 'FeatureCollection',
8702                         features: jsons
8703                 };
8704         }
8705 });
8706
8707 // @namespace GeoJSON
8708 // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
8709 // Creates a GeoJSON layer. Optionally accepts an object in
8710 // [GeoJSON format](http://geojson.org/geojson-spec.html) to display on the map
8711 // (you can alternatively add it later with `addData` method) and an `options` object.
8712 function geoJSON(geojson, options) {
8713         return new GeoJSON(geojson, options);
8714 }
8715
8716 // Backward compatibility.
8717 var geoJson = geoJSON;
8718
8719 /*
8720  * @class ImageOverlay
8721  * @aka L.ImageOverlay
8722  * @inherits Interactive layer
8723  *
8724  * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
8725  *
8726  * @example
8727  *
8728  * ```js
8729  * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
8730  *      imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
8731  * L.imageOverlay(imageUrl, imageBounds).addTo(map);
8732  * ```
8733  */
8734
8735 var ImageOverlay = Layer.extend({
8736
8737         // @section
8738         // @aka ImageOverlay options
8739         options: {
8740                 // @option opacity: Number = 1.0
8741                 // The opacity of the image overlay.
8742                 opacity: 1,
8743
8744                 // @option alt: String = ''
8745                 // Text for the `alt` attribute of the image (useful for accessibility).
8746                 alt: '',
8747
8748                 // @option interactive: Boolean = false
8749                 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
8750                 interactive: false,
8751
8752                 // @option crossOrigin: Boolean = false
8753                 // If true, the image will have its crossOrigin attribute set to ''. This is needed if you want to access image pixel data.
8754                 crossOrigin: false,
8755
8756                 // @option errorOverlayUrl: String = ''
8757                 // URL to the overlay image to show in place of the overlay that failed to load.
8758                 errorOverlayUrl: '',
8759
8760                 // @option zIndex: Number = 1
8761                 // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the tile layer.
8762                 zIndex: 1,
8763
8764                 // @option className: String = ''
8765                 // A custom class name to assign to the image. Empty by default.
8766                 className: '',
8767         },
8768
8769         initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
8770                 this._url = url;
8771                 this._bounds = toLatLngBounds(bounds);
8772
8773                 setOptions(this, options);
8774         },
8775
8776         onAdd: function () {
8777                 if (!this._image) {
8778                         this._initImage();
8779
8780                         if (this.options.opacity < 1) {
8781                                 this._updateOpacity();
8782                         }
8783                 }
8784
8785                 if (this.options.interactive) {
8786                         addClass(this._image, 'leaflet-interactive');
8787                         this.addInteractiveTarget(this._image);
8788                 }
8789
8790                 this.getPane().appendChild(this._image);
8791                 this._reset();
8792         },
8793
8794         onRemove: function () {
8795                 remove(this._image);
8796                 if (this.options.interactive) {
8797                         this.removeInteractiveTarget(this._image);
8798                 }
8799         },
8800
8801         // @method setOpacity(opacity: Number): this
8802         // Sets the opacity of the overlay.
8803         setOpacity: function (opacity) {
8804                 this.options.opacity = opacity;
8805
8806                 if (this._image) {
8807                         this._updateOpacity();
8808                 }
8809                 return this;
8810         },
8811
8812         setStyle: function (styleOpts) {
8813                 if (styleOpts.opacity) {
8814                         this.setOpacity(styleOpts.opacity);
8815                 }
8816                 return this;
8817         },
8818
8819         // @method bringToFront(): this
8820         // Brings the layer to the top of all overlays.
8821         bringToFront: function () {
8822                 if (this._map) {
8823                         toFront(this._image);
8824                 }
8825                 return this;
8826         },
8827
8828         // @method bringToBack(): this
8829         // Brings the layer to the bottom of all overlays.
8830         bringToBack: function () {
8831                 if (this._map) {
8832                         toBack(this._image);
8833                 }
8834                 return this;
8835         },
8836
8837         // @method setUrl(url: String): this
8838         // Changes the URL of the image.
8839         setUrl: function (url) {
8840                 this._url = url;
8841
8842                 if (this._image) {
8843                         this._image.src = url;
8844                 }
8845                 return this;
8846         },
8847
8848         // @method setBounds(bounds: LatLngBounds): this
8849         // Update the bounds that this ImageOverlay covers
8850         setBounds: function (bounds) {
8851                 this._bounds = bounds;
8852
8853                 if (this._map) {
8854                         this._reset();
8855                 }
8856                 return this;
8857         },
8858
8859         getEvents: function () {
8860                 var events = {
8861                         zoom: this._reset,
8862                         viewreset: this._reset
8863                 };
8864
8865                 if (this._zoomAnimated) {
8866                         events.zoomanim = this._animateZoom;
8867                 }
8868
8869                 return events;
8870         },
8871
8872         // @method: setZIndex(value: Number) : this
8873         // Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
8874         setZIndex: function (value) {
8875                 this.options.zIndex = value;
8876                 this._updateZIndex();
8877                 return this;
8878         },
8879
8880         // @method getBounds(): LatLngBounds
8881         // Get the bounds that this ImageOverlay covers
8882         getBounds: function () {
8883                 return this._bounds;
8884         },
8885
8886         // @method getElement(): HTMLElement
8887         // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
8888         // used by this overlay.
8889         getElement: function () {
8890                 return this._image;
8891         },
8892
8893         _initImage: function () {
8894                 var img = this._image = create$1('img',
8895                                 'leaflet-image-layer ' + (this._zoomAnimated ? 'leaflet-zoom-animated' : '') +
8896                                  (this.options.className || ''));
8897
8898                 img.onselectstart = falseFn;
8899                 img.onmousemove = falseFn;
8900
8901                 // @event load: Event
8902                 // Fired when the ImageOverlay layer has loaded its image
8903                 img.onload = bind(this.fire, this, 'load');
8904                 img.onerror = bind(this._overlayOnError, this, 'error');
8905
8906                 if (this.options.crossOrigin) {
8907                         img.crossOrigin = '';
8908                 }
8909
8910                 if (this.options.zIndex) {
8911                         this._updateZIndex();
8912                 }
8913
8914                 img.src = this._url;
8915                 img.alt = this.options.alt;
8916         },
8917
8918         _animateZoom: function (e) {
8919                 var scale = this._map.getZoomScale(e.zoom),
8920                     offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
8921
8922                 setTransform(this._image, offset, scale);
8923         },
8924
8925         _reset: function () {
8926                 var image = this._image,
8927                     bounds = new Bounds(
8928                         this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
8929                         this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
8930                     size = bounds.getSize();
8931
8932                 setPosition(image, bounds.min);
8933
8934                 image.style.width  = size.x + 'px';
8935                 image.style.height = size.y + 'px';
8936         },
8937
8938         _updateOpacity: function () {
8939                 setOpacity(this._image, this.options.opacity);
8940         },
8941
8942         _updateZIndex: function () {
8943                 if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
8944                         this._image.style.zIndex = this.options.zIndex;
8945                 }
8946         },
8947
8948         _overlayOnError: function () {
8949                 // @event error: Event
8950                 // Fired when the ImageOverlay layer has loaded its image
8951                 this.fire('error');
8952
8953                 var errorUrl = this.options.errorOverlayUrl;
8954                 if (errorUrl && this._url !== errorUrl) {
8955                         this._url = errorUrl;
8956                         this._image.src = errorUrl;
8957                 }
8958         }
8959 });
8960
8961 // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
8962 // Instantiates an image overlay object given the URL of the image and the
8963 // geographical bounds it is tied to.
8964 var imageOverlay = function (url, bounds, options) {
8965         return new ImageOverlay(url, bounds, options);
8966 };
8967
8968 /*
8969  * @class VideoOverlay
8970  * @aka L.VideoOverlay
8971  * @inherits ImageOverlay
8972  *
8973  * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
8974  *
8975  * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
8976  * HTML5 element.
8977  *
8978  * @example
8979  *
8980  * ```js
8981  * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
8982  *      imageBounds = [[ 32, -130], [ 13, -100]];
8983  * L.imageOverlay(imageUrl, imageBounds).addTo(map);
8984  * ```
8985  */
8986
8987 var VideoOverlay = ImageOverlay.extend({
8988
8989         // @section
8990         // @aka VideoOverlay options
8991         options: {
8992                 // @option autoplay: Boolean = true
8993                 // Whether the video starts playing automatically when loaded.
8994                 autoplay: true,
8995
8996                 // @option loop: Boolean = true
8997                 // Whether the video will loop back to the beginning when played.
8998                 loop: true
8999         },
9000
9001         _initImage: function () {
9002                 var vid = this._image = create$1('video',
9003                         'leaflet-image-layer ' + (this._zoomAnimated ? 'leaflet-zoom-animated' : ''));
9004
9005                 vid.onselectstart = falseFn;
9006                 vid.onmousemove = falseFn;
9007
9008                 // @event load: Event
9009                 // Fired when the video has finished loading the first frame
9010                 vid.onloadeddata = bind(this.fire, this, 'load');
9011
9012                 if (!isArray(this._url)) { this._url = [this._url]; }
9013
9014                 vid.autoplay = !!this.options.autoplay;
9015                 vid.loop = !!this.options.loop;
9016                 for (var i = 0; i < this._url.length; i++) {
9017                         var source = create$1('source');
9018                         source.src = this._url[i];
9019                         vid.appendChild(source);
9020                 }
9021         }
9022
9023         // @method getElement(): HTMLVideoElement
9024         // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
9025         // used by this overlay.
9026 });
9027
9028
9029 // @factory L.videoOverlay(videoUrl: String|Array, bounds: LatLngBounds, options?: VideoOverlay options)
9030 // Instantiates an image overlay object given the URL of the video (or array of URLs) and the
9031 // geographical bounds it is tied to.
9032 function videoOverlay(url, bounds, options) {
9033         return new VideoOverlay(url, bounds, options);
9034 }
9035
9036 /*
9037  * @class DivOverlay
9038  * @inherits Layer
9039  * @aka L.DivOverlay
9040  * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
9041  */
9042
9043 // @namespace DivOverlay
9044 var DivOverlay = Layer.extend({
9045
9046         // @section
9047         // @aka DivOverlay options
9048         options: {
9049                 // @option offset: Point = Point(0, 7)
9050                 // The offset of the popup position. Useful to control the anchor
9051                 // of the popup when opening it on some overlays.
9052                 offset: [0, 7],
9053
9054                 // @option className: String = ''
9055                 // A custom CSS class name to assign to the popup.
9056                 className: '',
9057
9058                 // @option pane: String = 'popupPane'
9059                 // `Map pane` where the popup will be added.
9060                 pane: 'popupPane'
9061         },
9062
9063         initialize: function (options, source) {
9064                 setOptions(this, options);
9065
9066                 this._source = source;
9067         },
9068
9069         onAdd: function (map) {
9070                 this._zoomAnimated = map._zoomAnimated;
9071
9072                 if (!this._container) {
9073                         this._initLayout();
9074                 }
9075
9076                 if (map._fadeAnimated) {
9077                         setOpacity(this._container, 0);
9078                 }
9079
9080                 clearTimeout(this._removeTimeout);
9081                 this.getPane().appendChild(this._container);
9082                 this.update();
9083
9084                 if (map._fadeAnimated) {
9085                         setOpacity(this._container, 1);
9086                 }
9087
9088                 this.bringToFront();
9089         },
9090
9091         onRemove: function (map) {
9092                 if (map._fadeAnimated) {
9093                         setOpacity(this._container, 0);
9094                         this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200);
9095                 } else {
9096                         remove(this._container);
9097                 }
9098         },
9099
9100         // @namespace Popup
9101         // @method getLatLng: LatLng
9102         // Returns the geographical point of popup.
9103         getLatLng: function () {
9104                 return this._latlng;
9105         },
9106
9107         // @method setLatLng(latlng: LatLng): this
9108         // Sets the geographical point where the popup will open.
9109         setLatLng: function (latlng) {
9110                 this._latlng = toLatLng(latlng);
9111                 if (this._map) {
9112                         this._updatePosition();
9113                         this._adjustPan();
9114                 }
9115                 return this;
9116         },
9117
9118         // @method getContent: String|HTMLElement
9119         // Returns the content of the popup.
9120         getContent: function () {
9121                 return this._content;
9122         },
9123
9124         // @method setContent(htmlContent: String|HTMLElement|Function): this
9125         // 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.
9126         setContent: function (content) {
9127                 this._content = content;
9128                 this.update();
9129                 return this;
9130         },
9131
9132         // @method getElement: String|HTMLElement
9133         // Alias for [getContent()](#popup-getcontent)
9134         getElement: function () {
9135                 return this._container;
9136         },
9137
9138         // @method update: null
9139         // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
9140         update: function () {
9141                 if (!this._map) { return; }
9142
9143                 this._container.style.visibility = 'hidden';
9144
9145                 this._updateContent();
9146                 this._updateLayout();
9147                 this._updatePosition();
9148
9149                 this._container.style.visibility = '';
9150
9151                 this._adjustPan();
9152         },
9153
9154         getEvents: function () {
9155                 var events = {
9156                         zoom: this._updatePosition,
9157                         viewreset: this._updatePosition
9158                 };
9159
9160                 if (this._zoomAnimated) {
9161                         events.zoomanim = this._animateZoom;
9162                 }
9163                 return events;
9164         },
9165
9166         // @method isOpen: Boolean
9167         // Returns `true` when the popup is visible on the map.
9168         isOpen: function () {
9169                 return !!this._map && this._map.hasLayer(this);
9170         },
9171
9172         // @method bringToFront: this
9173         // Brings this popup in front of other popups (in the same map pane).
9174         bringToFront: function () {
9175                 if (this._map) {
9176                         toFront(this._container);
9177                 }
9178                 return this;
9179         },
9180
9181         // @method bringToBack: this
9182         // Brings this popup to the back of other popups (in the same map pane).
9183         bringToBack: function () {
9184                 if (this._map) {
9185                         toBack(this._container);
9186                 }
9187                 return this;
9188         },
9189
9190         _updateContent: function () {
9191                 if (!this._content) { return; }
9192
9193                 var node = this._contentNode;
9194                 var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
9195
9196                 if (typeof content === 'string') {
9197                         node.innerHTML = content;
9198                 } else {
9199                         while (node.hasChildNodes()) {
9200                                 node.removeChild(node.firstChild);
9201                         }
9202                         node.appendChild(content);
9203                 }
9204                 this.fire('contentupdate');
9205         },
9206
9207         _updatePosition: function () {
9208                 if (!this._map) { return; }
9209
9210                 var pos = this._map.latLngToLayerPoint(this._latlng),
9211                     offset = toPoint(this.options.offset),
9212                     anchor = this._getAnchor();
9213
9214                 if (this._zoomAnimated) {
9215                         setPosition(this._container, pos.add(anchor));
9216                 } else {
9217                         offset = offset.add(pos).add(anchor);
9218                 }
9219
9220                 var bottom = this._containerBottom = -offset.y,
9221                     left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
9222
9223                 // bottom position the popup in case the height of the popup changes (images loading etc)
9224                 this._container.style.bottom = bottom + 'px';
9225                 this._container.style.left = left + 'px';
9226         },
9227
9228         _getAnchor: function () {
9229                 return [0, 0];
9230         }
9231
9232 });
9233
9234 /*
9235  * @class Popup
9236  * @inherits DivOverlay
9237  * @aka L.Popup
9238  * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
9239  * open popups while making sure that only one popup is open at one time
9240  * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
9241  *
9242  * @example
9243  *
9244  * If you want to just bind a popup to marker click and then open it, it's really easy:
9245  *
9246  * ```js
9247  * marker.bindPopup(popupContent).openPopup();
9248  * ```
9249  * Path overlays like polylines also have a `bindPopup` method.
9250  * Here's a more complicated way to open a popup on a map:
9251  *
9252  * ```js
9253  * var popup = L.popup()
9254  *      .setLatLng(latlng)
9255  *      .setContent('<p>Hello world!<br />This is a nice popup.</p>')
9256  *      .openOn(map);
9257  * ```
9258  */
9259
9260
9261 // @namespace Popup
9262 var Popup = DivOverlay.extend({
9263
9264         // @section
9265         // @aka Popup options
9266         options: {
9267                 // @option maxWidth: Number = 300
9268                 // Max width of the popup, in pixels.
9269                 maxWidth: 300,
9270
9271                 // @option minWidth: Number = 50
9272                 // Min width of the popup, in pixels.
9273                 minWidth: 50,
9274
9275                 // @option maxHeight: Number = null
9276                 // If set, creates a scrollable container of the given height
9277                 // inside a popup if its content exceeds it.
9278                 maxHeight: null,
9279
9280                 // @option autoPan: Boolean = true
9281                 // Set it to `false` if you don't want the map to do panning animation
9282                 // to fit the opened popup.
9283                 autoPan: true,
9284
9285                 // @option autoPanPaddingTopLeft: Point = null
9286                 // The margin between the popup and the top left corner of the map
9287                 // view after autopanning was performed.
9288                 autoPanPaddingTopLeft: null,
9289
9290                 // @option autoPanPaddingBottomRight: Point = null
9291                 // The margin between the popup and the bottom right corner of the map
9292                 // view after autopanning was performed.
9293                 autoPanPaddingBottomRight: null,
9294
9295                 // @option autoPanPadding: Point = Point(5, 5)
9296                 // Equivalent of setting both top left and bottom right autopan padding to the same value.
9297                 autoPanPadding: [5, 5],
9298
9299                 // @option keepInView: Boolean = false
9300                 // Set it to `true` if you want to prevent users from panning the popup
9301                 // off of the screen while it is open.
9302                 keepInView: false,
9303
9304                 // @option closeButton: Boolean = true
9305                 // Controls the presence of a close button in the popup.
9306                 closeButton: true,
9307
9308                 // @option autoClose: Boolean = true
9309                 // Set it to `false` if you want to override the default behavior of
9310                 // the popup closing when another popup is opened.
9311                 autoClose: true,
9312
9313                 // @option closeOnClick: Boolean = *
9314                 // Set it if you want to override the default behavior of the popup closing when user clicks
9315                 // on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
9316
9317                 // @option className: String = ''
9318                 // A custom CSS class name to assign to the popup.
9319                 className: ''
9320         },
9321
9322         // @namespace Popup
9323         // @method openOn(map: Map): this
9324         // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
9325         openOn: function (map) {
9326                 map.openPopup(this);
9327                 return this;
9328         },
9329
9330         onAdd: function (map) {
9331                 DivOverlay.prototype.onAdd.call(this, map);
9332
9333                 // @namespace Map
9334                 // @section Popup events
9335                 // @event popupopen: PopupEvent
9336                 // Fired when a popup is opened in the map
9337                 map.fire('popupopen', {popup: this});
9338
9339                 if (this._source) {
9340                         // @namespace Layer
9341                         // @section Popup events
9342                         // @event popupopen: PopupEvent
9343                         // Fired when a popup bound to this layer is opened
9344                         this._source.fire('popupopen', {popup: this}, true);
9345                         // For non-path layers, we toggle the popup when clicking
9346                         // again the layer, so prevent the map to reopen it.
9347                         if (!(this._source instanceof Path)) {
9348                                 this._source.on('preclick', stopPropagation);
9349                         }
9350                 }
9351         },
9352
9353         onRemove: function (map) {
9354                 DivOverlay.prototype.onRemove.call(this, map);
9355
9356                 // @namespace Map
9357                 // @section Popup events
9358                 // @event popupclose: PopupEvent
9359                 // Fired when a popup in the map is closed
9360                 map.fire('popupclose', {popup: this});
9361
9362                 if (this._source) {
9363                         // @namespace Layer
9364                         // @section Popup events
9365                         // @event popupclose: PopupEvent
9366                         // Fired when a popup bound to this layer is closed
9367                         this._source.fire('popupclose', {popup: this}, true);
9368                         if (!(this._source instanceof Path)) {
9369                                 this._source.off('preclick', stopPropagation);
9370                         }
9371                 }
9372         },
9373
9374         getEvents: function () {
9375                 var events = DivOverlay.prototype.getEvents.call(this);
9376
9377                 if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
9378                         events.preclick = this._close;
9379                 }
9380
9381                 if (this.options.keepInView) {
9382                         events.moveend = this._adjustPan;
9383                 }
9384
9385                 return events;
9386         },
9387
9388         _close: function () {
9389                 if (this._map) {
9390                         this._map.closePopup(this);
9391                 }
9392         },
9393
9394         _initLayout: function () {
9395                 var prefix = 'leaflet-popup',
9396                     container = this._container = create$1('div',
9397                         prefix + ' ' + (this.options.className || '') +
9398                         ' leaflet-zoom-animated');
9399
9400                 var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
9401                 this._contentNode = create$1('div', prefix + '-content', wrapper);
9402
9403                 disableClickPropagation(wrapper);
9404                 disableScrollPropagation(this._contentNode);
9405                 on(wrapper, 'contextmenu', stopPropagation);
9406
9407                 this._tipContainer = create$1('div', prefix + '-tip-container', container);
9408                 this._tip = create$1('div', prefix + '-tip', this._tipContainer);
9409
9410                 if (this.options.closeButton) {
9411                         var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
9412                         closeButton.href = '#close';
9413                         closeButton.innerHTML = '&#215;';
9414
9415                         on(closeButton, 'click', this._onCloseButtonClick, this);
9416                 }
9417         },
9418
9419         _updateLayout: function () {
9420                 var container = this._contentNode,
9421                     style = container.style;
9422
9423                 style.width = '';
9424                 style.whiteSpace = 'nowrap';
9425
9426                 var width = container.offsetWidth;
9427                 width = Math.min(width, this.options.maxWidth);
9428                 width = Math.max(width, this.options.minWidth);
9429
9430                 style.width = (width + 1) + 'px';
9431                 style.whiteSpace = '';
9432
9433                 style.height = '';
9434
9435                 var height = container.offsetHeight,
9436                     maxHeight = this.options.maxHeight,
9437                     scrolledClass = 'leaflet-popup-scrolled';
9438
9439                 if (maxHeight && height > maxHeight) {
9440                         style.height = maxHeight + 'px';
9441                         addClass(container, scrolledClass);
9442                 } else {
9443                         removeClass(container, scrolledClass);
9444                 }
9445
9446                 this._containerWidth = this._container.offsetWidth;
9447         },
9448
9449         _animateZoom: function (e) {
9450                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
9451                     anchor = this._getAnchor();
9452                 setPosition(this._container, pos.add(anchor));
9453         },
9454
9455         _adjustPan: function () {
9456                 if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; }
9457
9458                 var map = this._map,
9459                     marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
9460                     containerHeight = this._container.offsetHeight + marginBottom,
9461                     containerWidth = this._containerWidth,
9462                     layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
9463
9464                 layerPos._add(getPosition(this._container));
9465
9466                 var containerPos = map.layerPointToContainerPoint(layerPos),
9467                     padding = toPoint(this.options.autoPanPadding),
9468                     paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
9469                     paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
9470                     size = map.getSize(),
9471                     dx = 0,
9472                     dy = 0;
9473
9474                 if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
9475                         dx = containerPos.x + containerWidth - size.x + paddingBR.x;
9476                 }
9477                 if (containerPos.x - dx - paddingTL.x < 0) { // left
9478                         dx = containerPos.x - paddingTL.x;
9479                 }
9480                 if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
9481                         dy = containerPos.y + containerHeight - size.y + paddingBR.y;
9482                 }
9483                 if (containerPos.y - dy - paddingTL.y < 0) { // top
9484                         dy = containerPos.y - paddingTL.y;
9485                 }
9486
9487                 // @namespace Map
9488                 // @section Popup events
9489                 // @event autopanstart: Event
9490                 // Fired when the map starts autopanning when opening a popup.
9491                 if (dx || dy) {
9492                         map
9493                             .fire('autopanstart')
9494                             .panBy([dx, dy]);
9495                 }
9496         },
9497
9498         _onCloseButtonClick: function (e) {
9499                 this._close();
9500                 stop(e);
9501         },
9502
9503         _getAnchor: function () {
9504                 // Where should we anchor the popup on the source layer?
9505                 return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
9506         }
9507
9508 });
9509
9510 // @namespace Popup
9511 // @factory L.popup(options?: Popup options, source?: Layer)
9512 // 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.
9513 var popup = function (options, source) {
9514         return new Popup(options, source);
9515 };
9516
9517
9518 /* @namespace Map
9519  * @section Interaction Options
9520  * @option closePopupOnClick: Boolean = true
9521  * Set it to `false` if you don't want popups to close when user clicks the map.
9522  */
9523 Map.mergeOptions({
9524         closePopupOnClick: true
9525 });
9526
9527
9528 // @namespace Map
9529 // @section Methods for Layers and Controls
9530 Map.include({
9531         // @method openPopup(popup: Popup): this
9532         // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
9533         // @alternative
9534         // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
9535         // Creates a popup with the specified content and options and opens it in the given point on a map.
9536         openPopup: function (popup, latlng, options) {
9537                 if (!(popup instanceof Popup)) {
9538                         popup = new Popup(options).setContent(popup);
9539                 }
9540
9541                 if (latlng) {
9542                         popup.setLatLng(latlng);
9543                 }
9544
9545                 if (this.hasLayer(popup)) {
9546                         return this;
9547                 }
9548
9549                 if (this._popup && this._popup.options.autoClose) {
9550                         this.closePopup();
9551                 }
9552
9553                 this._popup = popup;
9554                 return this.addLayer(popup);
9555         },
9556
9557         // @method closePopup(popup?: Popup): this
9558         // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
9559         closePopup: function (popup) {
9560                 if (!popup || popup === this._popup) {
9561                         popup = this._popup;
9562                         this._popup = null;
9563                 }
9564                 if (popup) {
9565                         this.removeLayer(popup);
9566                 }
9567                 return this;
9568         }
9569 });
9570
9571 /*
9572  * @namespace Layer
9573  * @section Popup methods example
9574  *
9575  * All layers share a set of methods convenient for binding popups to it.
9576  *
9577  * ```js
9578  * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
9579  * layer.openPopup();
9580  * layer.closePopup();
9581  * ```
9582  *
9583  * 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.
9584  */
9585
9586 // @section Popup methods
9587 Layer.include({
9588
9589         // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
9590         // Binds a popup to the layer with the passed `content` and sets up the
9591         // neccessary event listeners. If a `Function` is passed it will receive
9592         // the layer as the first argument and should return a `String` or `HTMLElement`.
9593         bindPopup: function (content, options) {
9594
9595                 if (content instanceof Popup) {
9596                         setOptions(content, options);
9597                         this._popup = content;
9598                         content._source = this;
9599                 } else {
9600                         if (!this._popup || options) {
9601                                 this._popup = new Popup(options, this);
9602                         }
9603                         this._popup.setContent(content);
9604                 }
9605
9606                 if (!this._popupHandlersAdded) {
9607                         this.on({
9608                                 click: this._openPopup,
9609                                 keypress: this._onKeyPress,
9610                                 remove: this.closePopup,
9611                                 move: this._movePopup
9612                         });
9613                         this._popupHandlersAdded = true;
9614                 }
9615
9616                 return this;
9617         },
9618
9619         // @method unbindPopup(): this
9620         // Removes the popup previously bound with `bindPopup`.
9621         unbindPopup: function () {
9622                 if (this._popup) {
9623                         this.off({
9624                                 click: this._openPopup,
9625                                 keypress: this._onKeyPress,
9626                                 remove: this.closePopup,
9627                                 move: this._movePopup
9628                         });
9629                         this._popupHandlersAdded = false;
9630                         this._popup = null;
9631                 }
9632                 return this;
9633         },
9634
9635         // @method openPopup(latlng?: LatLng): this
9636         // Opens the bound popup at the specificed `latlng` or at the default popup anchor if no `latlng` is passed.
9637         openPopup: function (layer, latlng) {
9638                 if (!(layer instanceof Layer)) {
9639                         latlng = layer;
9640                         layer = this;
9641                 }
9642
9643                 if (layer instanceof FeatureGroup) {
9644                         for (var id in this._layers) {
9645                                 layer = this._layers[id];
9646                                 break;
9647                         }
9648                 }
9649
9650                 if (!latlng) {
9651                         latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
9652                 }
9653
9654                 if (this._popup && this._map) {
9655                         // set popup source to this layer
9656                         this._popup._source = layer;
9657
9658                         // update the popup (content, layout, ect...)
9659                         this._popup.update();
9660
9661                         // open the popup on the map
9662                         this._map.openPopup(this._popup, latlng);
9663                 }
9664
9665                 return this;
9666         },
9667
9668         // @method closePopup(): this
9669         // Closes the popup bound to this layer if it is open.
9670         closePopup: function () {
9671                 if (this._popup) {
9672                         this._popup._close();
9673                 }
9674                 return this;
9675         },
9676
9677         // @method togglePopup(): this
9678         // Opens or closes the popup bound to this layer depending on its current state.
9679         togglePopup: function (target) {
9680                 if (this._popup) {
9681                         if (this._popup._map) {
9682                                 this.closePopup();
9683                         } else {
9684                                 this.openPopup(target);
9685                         }
9686                 }
9687                 return this;
9688         },
9689
9690         // @method isPopupOpen(): boolean
9691         // Returns `true` if the popup bound to this layer is currently open.
9692         isPopupOpen: function () {
9693                 return (this._popup ? this._popup.isOpen() : false);
9694         },
9695
9696         // @method setPopupContent(content: String|HTMLElement|Popup): this
9697         // Sets the content of the popup bound to this layer.
9698         setPopupContent: function (content) {
9699                 if (this._popup) {
9700                         this._popup.setContent(content);
9701                 }
9702                 return this;
9703         },
9704
9705         // @method getPopup(): Popup
9706         // Returns the popup bound to this layer.
9707         getPopup: function () {
9708                 return this._popup;
9709         },
9710
9711         _openPopup: function (e) {
9712                 var layer = e.layer || e.target;
9713
9714                 if (!this._popup) {
9715                         return;
9716                 }
9717
9718                 if (!this._map) {
9719                         return;
9720                 }
9721
9722                 // prevent map click
9723                 stop(e);
9724
9725                 // if this inherits from Path its a vector and we can just
9726                 // open the popup at the new location
9727                 if (layer instanceof Path) {
9728                         this.openPopup(e.layer || e.target, e.latlng);
9729                         return;
9730                 }
9731
9732                 // otherwise treat it like a marker and figure out
9733                 // if we should toggle it open/closed
9734                 if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
9735                         this.closePopup();
9736                 } else {
9737                         this.openPopup(layer, e.latlng);
9738                 }
9739         },
9740
9741         _movePopup: function (e) {
9742                 this._popup.setLatLng(e.latlng);
9743         },
9744
9745         _onKeyPress: function (e) {
9746                 if (e.originalEvent.keyCode === 13) {
9747                         this._openPopup(e);
9748                 }
9749         }
9750 });
9751
9752 /*
9753  * @class Tooltip
9754  * @inherits DivOverlay
9755  * @aka L.Tooltip
9756  * Used to display small texts on top of map layers.
9757  *
9758  * @example
9759  *
9760  * ```js
9761  * marker.bindTooltip("my tooltip text").openTooltip();
9762  * ```
9763  * Note about tooltip offset. Leaflet takes two options in consideration
9764  * for computing tooltip offseting:
9765  * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
9766  *   Add a positive x offset to move the tooltip to the right, and a positive y offset to
9767  *   move it to the bottom. Negatives will move to the left and top.
9768  * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
9769  *   should adapt this value if you use a custom icon.
9770  */
9771
9772
9773 // @namespace Tooltip
9774 var Tooltip = DivOverlay.extend({
9775
9776         // @section
9777         // @aka Tooltip options
9778         options: {
9779                 // @option pane: String = 'tooltipPane'
9780                 // `Map pane` where the tooltip will be added.
9781                 pane: 'tooltipPane',
9782
9783                 // @option offset: Point = Point(0, 0)
9784                 // Optional offset of the tooltip position.
9785                 offset: [0, 0],
9786
9787                 // @option direction: String = 'auto'
9788                 // Direction where to open the tooltip. Possible values are: `right`, `left`,
9789                 // `top`, `bottom`, `center`, `auto`.
9790                 // `auto` will dynamicaly switch between `right` and `left` according to the tooltip
9791                 // position on the map.
9792                 direction: 'auto',
9793
9794                 // @option permanent: Boolean = false
9795                 // Whether to open the tooltip permanently or only on mouseover.
9796                 permanent: false,
9797
9798                 // @option sticky: Boolean = false
9799                 // If true, the tooltip will follow the mouse instead of being fixed at the feature center.
9800                 sticky: false,
9801
9802                 // @option interactive: Boolean = false
9803                 // If true, the tooltip will listen to the feature events.
9804                 interactive: false,
9805
9806                 // @option opacity: Number = 0.9
9807                 // Tooltip container opacity.
9808                 opacity: 0.9
9809         },
9810
9811         onAdd: function (map) {
9812                 DivOverlay.prototype.onAdd.call(this, map);
9813                 this.setOpacity(this.options.opacity);
9814
9815                 // @namespace Map
9816                 // @section Tooltip events
9817                 // @event tooltipopen: TooltipEvent
9818                 // Fired when a tooltip is opened in the map.
9819                 map.fire('tooltipopen', {tooltip: this});
9820
9821                 if (this._source) {
9822                         // @namespace Layer
9823                         // @section Tooltip events
9824                         // @event tooltipopen: TooltipEvent
9825                         // Fired when a tooltip bound to this layer is opened.
9826                         this._source.fire('tooltipopen', {tooltip: this}, true);
9827                 }
9828         },
9829
9830         onRemove: function (map) {
9831                 DivOverlay.prototype.onRemove.call(this, map);
9832
9833                 // @namespace Map
9834                 // @section Tooltip events
9835                 // @event tooltipclose: TooltipEvent
9836                 // Fired when a tooltip in the map is closed.
9837                 map.fire('tooltipclose', {tooltip: this});
9838
9839                 if (this._source) {
9840                         // @namespace Layer
9841                         // @section Tooltip events
9842                         // @event tooltipclose: TooltipEvent
9843                         // Fired when a tooltip bound to this layer is closed.
9844                         this._source.fire('tooltipclose', {tooltip: this}, true);
9845                 }
9846         },
9847
9848         getEvents: function () {
9849                 var events = DivOverlay.prototype.getEvents.call(this);
9850
9851                 if (touch && !this.options.permanent) {
9852                         events.preclick = this._close;
9853                 }
9854
9855                 return events;
9856         },
9857
9858         _close: function () {
9859                 if (this._map) {
9860                         this._map.closeTooltip(this);
9861                 }
9862         },
9863
9864         _initLayout: function () {
9865                 var prefix = 'leaflet-tooltip',
9866                     className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
9867
9868                 this._contentNode = this._container = create$1('div', className);
9869         },
9870
9871         _updateLayout: function () {},
9872
9873         _adjustPan: function () {},
9874
9875         _setPosition: function (pos) {
9876                 var map = this._map,
9877                     container = this._container,
9878                     centerPoint = map.latLngToContainerPoint(map.getCenter()),
9879                     tooltipPoint = map.layerPointToContainerPoint(pos),
9880                     direction = this.options.direction,
9881                     tooltipWidth = container.offsetWidth,
9882                     tooltipHeight = container.offsetHeight,
9883                     offset = toPoint(this.options.offset),
9884                     anchor = this._getAnchor();
9885
9886                 if (direction === 'top') {
9887                         pos = pos.add(toPoint(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true));
9888                 } else if (direction === 'bottom') {
9889                         pos = pos.subtract(toPoint(tooltipWidth / 2 - offset.x, -offset.y, true));
9890                 } else if (direction === 'center') {
9891                         pos = pos.subtract(toPoint(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true));
9892                 } else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
9893                         direction = 'right';
9894                         pos = pos.add(toPoint(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true));
9895                 } else {
9896                         direction = 'left';
9897                         pos = pos.subtract(toPoint(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true));
9898                 }
9899
9900                 removeClass(container, 'leaflet-tooltip-right');
9901                 removeClass(container, 'leaflet-tooltip-left');
9902                 removeClass(container, 'leaflet-tooltip-top');
9903                 removeClass(container, 'leaflet-tooltip-bottom');
9904                 addClass(container, 'leaflet-tooltip-' + direction);
9905                 setPosition(container, pos);
9906         },
9907
9908         _updatePosition: function () {
9909                 var pos = this._map.latLngToLayerPoint(this._latlng);
9910                 this._setPosition(pos);
9911         },
9912
9913         setOpacity: function (opacity) {
9914                 this.options.opacity = opacity;
9915
9916                 if (this._container) {
9917                         setOpacity(this._container, opacity);
9918                 }
9919         },
9920
9921         _animateZoom: function (e) {
9922                 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
9923                 this._setPosition(pos);
9924         },
9925
9926         _getAnchor: function () {
9927                 // Where should we anchor the tooltip on the source layer?
9928                 return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
9929         }
9930
9931 });
9932
9933 // @namespace Tooltip
9934 // @factory L.tooltip(options?: Tooltip options, source?: Layer)
9935 // 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.
9936 var tooltip = function (options, source) {
9937         return new Tooltip(options, source);
9938 };
9939
9940 // @namespace Map
9941 // @section Methods for Layers and Controls
9942 Map.include({
9943
9944         // @method openTooltip(tooltip: Tooltip): this
9945         // Opens the specified tooltip.
9946         // @alternative
9947         // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
9948         // Creates a tooltip with the specified content and options and open it.
9949         openTooltip: function (tooltip, latlng, options) {
9950                 if (!(tooltip instanceof Tooltip)) {
9951                         tooltip = new Tooltip(options).setContent(tooltip);
9952                 }
9953
9954                 if (latlng) {
9955                         tooltip.setLatLng(latlng);
9956                 }
9957
9958                 if (this.hasLayer(tooltip)) {
9959                         return this;
9960                 }
9961
9962                 return this.addLayer(tooltip);
9963         },
9964
9965         // @method closeTooltip(tooltip?: Tooltip): this
9966         // Closes the tooltip given as parameter.
9967         closeTooltip: function (tooltip) {
9968                 if (tooltip) {
9969                         this.removeLayer(tooltip);
9970                 }
9971                 return this;
9972         }
9973
9974 });
9975
9976 /*
9977  * @namespace Layer
9978  * @section Tooltip methods example
9979  *
9980  * All layers share a set of methods convenient for binding tooltips to it.
9981  *
9982  * ```js
9983  * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
9984  * layer.openTooltip();
9985  * layer.closeTooltip();
9986  * ```
9987  */
9988
9989 // @section Tooltip methods
9990 Layer.include({
9991
9992         // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
9993         // Binds a tooltip to the layer with the passed `content` and sets up the
9994         // neccessary event listeners. If a `Function` is passed it will receive
9995         // the layer as the first argument and should return a `String` or `HTMLElement`.
9996         bindTooltip: function (content, options) {
9997
9998                 if (content instanceof Tooltip) {
9999                         setOptions(content, options);
10000                         this._tooltip = content;
10001                         content._source = this;
10002                 } else {
10003                         if (!this._tooltip || options) {
10004                                 this._tooltip = new Tooltip(options, this);
10005                         }
10006                         this._tooltip.setContent(content);
10007
10008                 }
10009
10010                 this._initTooltipInteractions();
10011
10012                 if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
10013                         this.openTooltip();
10014                 }
10015
10016                 return this;
10017         },
10018
10019         // @method unbindTooltip(): this
10020         // Removes the tooltip previously bound with `bindTooltip`.
10021         unbindTooltip: function () {
10022                 if (this._tooltip) {
10023                         this._initTooltipInteractions(true);
10024                         this.closeTooltip();
10025                         this._tooltip = null;
10026                 }
10027                 return this;
10028         },
10029
10030         _initTooltipInteractions: function (remove$$1) {
10031                 if (!remove$$1 && this._tooltipHandlersAdded) { return; }
10032                 var onOff = remove$$1 ? 'off' : 'on',
10033                     events = {
10034                         remove: this.closeTooltip,
10035                         move: this._moveTooltip
10036                     };
10037                 if (!this._tooltip.options.permanent) {
10038                         events.mouseover = this._openTooltip;
10039                         events.mouseout = this.closeTooltip;
10040                         if (this._tooltip.options.sticky) {
10041                                 events.mousemove = this._moveTooltip;
10042                         }
10043                         if (touch) {
10044                                 events.click = this._openTooltip;
10045                         }
10046                 } else {
10047                         events.add = this._openTooltip;
10048                 }
10049                 this[onOff](events);
10050                 this._tooltipHandlersAdded = !remove$$1;
10051         },
10052
10053         // @method openTooltip(latlng?: LatLng): this
10054         // Opens the bound tooltip at the specificed `latlng` or at the default tooltip anchor if no `latlng` is passed.
10055         openTooltip: function (layer, latlng) {
10056                 if (!(layer instanceof Layer)) {
10057                         latlng = layer;
10058                         layer = this;
10059                 }
10060
10061                 if (layer instanceof FeatureGroup) {
10062                         for (var id in this._layers) {
10063                                 layer = this._layers[id];
10064                                 break;
10065                         }
10066                 }
10067
10068                 if (!latlng) {
10069                         latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
10070                 }
10071
10072                 if (this._tooltip && this._map) {
10073
10074                         // set tooltip source to this layer
10075                         this._tooltip._source = layer;
10076
10077                         // update the tooltip (content, layout, ect...)
10078                         this._tooltip.update();
10079
10080                         // open the tooltip on the map
10081                         this._map.openTooltip(this._tooltip, latlng);
10082
10083                         // Tooltip container may not be defined if not permanent and never
10084                         // opened.
10085                         if (this._tooltip.options.interactive && this._tooltip._container) {
10086                                 addClass(this._tooltip._container, 'leaflet-clickable');
10087                                 this.addInteractiveTarget(this._tooltip._container);
10088                         }
10089                 }
10090
10091                 return this;
10092         },
10093
10094         // @method closeTooltip(): this
10095         // Closes the tooltip bound to this layer if it is open.
10096         closeTooltip: function () {
10097                 if (this._tooltip) {
10098                         this._tooltip._close();
10099                         if (this._tooltip.options.interactive && this._tooltip._container) {
10100                                 removeClass(this._tooltip._container, 'leaflet-clickable');
10101                                 this.removeInteractiveTarget(this._tooltip._container);
10102                         }
10103                 }
10104                 return this;
10105         },
10106
10107         // @method toggleTooltip(): this
10108         // Opens or closes the tooltip bound to this layer depending on its current state.
10109         toggleTooltip: function (target) {
10110                 if (this._tooltip) {
10111                         if (this._tooltip._map) {
10112                                 this.closeTooltip();
10113                         } else {
10114                                 this.openTooltip(target);
10115                         }
10116                 }
10117                 return this;
10118         },
10119
10120         // @method isTooltipOpen(): boolean
10121         // Returns `true` if the tooltip bound to this layer is currently open.
10122         isTooltipOpen: function () {
10123                 return this._tooltip.isOpen();
10124         },
10125
10126         // @method setTooltipContent(content: String|HTMLElement|Tooltip): this
10127         // Sets the content of the tooltip bound to this layer.
10128         setTooltipContent: function (content) {
10129                 if (this._tooltip) {
10130                         this._tooltip.setContent(content);
10131                 }
10132                 return this;
10133         },
10134
10135         // @method getTooltip(): Tooltip
10136         // Returns the tooltip bound to this layer.
10137         getTooltip: function () {
10138                 return this._tooltip;
10139         },
10140
10141         _openTooltip: function (e) {
10142                 var layer = e.layer || e.target;
10143
10144                 if (!this._tooltip || !this._map) {
10145                         return;
10146                 }
10147                 this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
10148         },
10149
10150         _moveTooltip: function (e) {
10151                 var latlng = e.latlng, containerPoint, layerPoint;
10152                 if (this._tooltip.options.sticky && e.originalEvent) {
10153                         containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
10154                         layerPoint = this._map.containerPointToLayerPoint(containerPoint);
10155                         latlng = this._map.layerPointToLatLng(layerPoint);
10156                 }
10157                 this._tooltip.setLatLng(latlng);
10158         }
10159 });
10160
10161 /*
10162  * @class DivIcon
10163  * @aka L.DivIcon
10164  * @inherits Icon
10165  *
10166  * Represents a lightweight icon for markers that uses a simple `<div>`
10167  * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
10168  *
10169  * @example
10170  * ```js
10171  * var myIcon = L.divIcon({className: 'my-div-icon'});
10172  * // you can set .my-div-icon styles in CSS
10173  *
10174  * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
10175  * ```
10176  *
10177  * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
10178  */
10179
10180 var DivIcon = Icon.extend({
10181         options: {
10182                 // @section
10183                 // @aka DivIcon options
10184                 iconSize: [12, 12], // also can be set through CSS
10185
10186                 // iconAnchor: (Point),
10187                 // popupAnchor: (Point),
10188
10189                 // @option html: String = ''
10190                 // Custom HTML code to put inside the div element, empty by default.
10191                 html: false,
10192
10193                 // @option bgPos: Point = [0, 0]
10194                 // Optional relative position of the background, in pixels
10195                 bgPos: null,
10196
10197                 className: 'leaflet-div-icon'
10198         },
10199
10200         createIcon: function (oldIcon) {
10201                 var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
10202                     options = this.options;
10203
10204                 div.innerHTML = options.html !== false ? options.html : '';
10205
10206                 if (options.bgPos) {
10207                         var bgPos = toPoint(options.bgPos);
10208                         div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
10209                 }
10210                 this._setIconStyles(div, 'icon');
10211
10212                 return div;
10213         },
10214
10215         createShadow: function () {
10216                 return null;
10217         }
10218 });
10219
10220 // @factory L.divIcon(options: DivIcon options)
10221 // Creates a `DivIcon` instance with the given options.
10222 function divIcon(options) {
10223         return new DivIcon(options);
10224 }
10225
10226 Icon.Default = IconDefault;
10227
10228 /*
10229  * @class GridLayer
10230  * @inherits Layer
10231  * @aka L.GridLayer
10232  *
10233  * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
10234  * 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.
10235  *
10236  *
10237  * @section Synchronous usage
10238  * @example
10239  *
10240  * 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.
10241  *
10242  * ```js
10243  * var CanvasLayer = L.GridLayer.extend({
10244  *     createTile: function(coords){
10245  *         // create a <canvas> element for drawing
10246  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
10247  *
10248  *         // setup tile width and height according to the options
10249  *         var size = this.getTileSize();
10250  *         tile.width = size.x;
10251  *         tile.height = size.y;
10252  *
10253  *         // get a canvas context and draw something on it using coords.x, coords.y and coords.z
10254  *         var ctx = tile.getContext('2d');
10255  *
10256  *         // return the tile so it can be rendered on screen
10257  *         return tile;
10258  *     }
10259  * });
10260  * ```
10261  *
10262  * @section Asynchronous usage
10263  * @example
10264  *
10265  * 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.
10266  *
10267  * ```js
10268  * var CanvasLayer = L.GridLayer.extend({
10269  *     createTile: function(coords, done){
10270  *         var error;
10271  *
10272  *         // create a <canvas> element for drawing
10273  *         var tile = L.DomUtil.create('canvas', 'leaflet-tile');
10274  *
10275  *         // setup tile width and height according to the options
10276  *         var size = this.getTileSize();
10277  *         tile.width = size.x;
10278  *         tile.height = size.y;
10279  *
10280  *         // draw something asynchronously and pass the tile to the done() callback
10281  *         setTimeout(function() {
10282  *             done(error, tile);
10283  *         }, 1000);
10284  *
10285  *         return tile;
10286  *     }
10287  * });
10288  * ```
10289  *
10290  * @section
10291  */
10292
10293
10294 var GridLayer = Layer.extend({
10295
10296         // @section
10297         // @aka GridLayer options
10298         options: {
10299                 // @option tileSize: Number|Point = 256
10300                 // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
10301                 tileSize: 256,
10302
10303                 // @option opacity: Number = 1.0
10304                 // Opacity of the tiles. Can be used in the `createTile()` function.
10305                 opacity: 1,
10306
10307                 // @option updateWhenIdle: Boolean = depends
10308                 // If `false`, new tiles are loaded during panning, otherwise only after it (for better performance). `true` by default on mobile browsers, otherwise `false`.
10309                 updateWhenIdle: mobile,
10310
10311                 // @option updateWhenZooming: Boolean = true
10312                 // 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.
10313                 updateWhenZooming: true,
10314
10315                 // @option updateInterval: Number = 200
10316                 // Tiles will not update more than once every `updateInterval` milliseconds when panning.
10317                 updateInterval: 200,
10318
10319                 // @option zIndex: Number = 1
10320                 // The explicit zIndex of the tile layer.
10321                 zIndex: 1,
10322
10323                 // @option bounds: LatLngBounds = undefined
10324                 // If set, tiles will only be loaded inside the set `LatLngBounds`.
10325                 bounds: null,
10326
10327                 // @option minZoom: Number = 0
10328                 // The minimum zoom level down to which this layer will be displayed (inclusive).
10329                 minZoom: 0,
10330
10331                 // @option maxZoom: Number = undefined
10332                 // The maximum zoom level up to which this layer will be displayed (inclusive).
10333                 maxZoom: undefined,
10334
10335                 // @option maxNativeZoom: Number = undefined
10336                 // Maximum zoom number the tile source has available. If it is specified,
10337                 // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
10338                 // from `maxNativeZoom` level and auto-scaled.
10339                 maxNativeZoom: undefined,
10340
10341                 // @option minNativeZoom: Number = undefined
10342                 // Minimum zoom number the tile source has available. If it is specified,
10343                 // the tiles on all zoom levels lower than `minNativeZoom` will be loaded
10344                 // from `minNativeZoom` level and auto-scaled.
10345                 minNativeZoom: undefined,
10346
10347                 // @option noWrap: Boolean = false
10348                 // Whether the layer is wrapped around the antimeridian. If `true`, the
10349                 // GridLayer will only be displayed once at low zoom levels. Has no
10350                 // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
10351                 // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
10352                 // tiles outside the CRS limits.
10353                 noWrap: false,
10354
10355                 // @option pane: String = 'tilePane'
10356                 // `Map pane` where the grid layer will be added.
10357                 pane: 'tilePane',
10358
10359                 // @option className: String = ''
10360                 // A custom class name to assign to the tile layer. Empty by default.
10361                 className: '',
10362
10363                 // @option keepBuffer: Number = 2
10364                 // When panning the map, keep this many rows and columns of tiles before unloading them.
10365                 keepBuffer: 2
10366         },
10367
10368         initialize: function (options) {
10369                 setOptions(this, options);
10370         },
10371
10372         onAdd: function () {
10373                 this._initContainer();
10374
10375                 this._levels = {};
10376                 this._tiles = {};
10377
10378                 this._resetView();
10379                 this._update();
10380         },
10381
10382         beforeAdd: function (map) {
10383                 map._addZoomLimit(this);
10384         },
10385
10386         onRemove: function (map) {
10387                 this._removeAllTiles();
10388                 remove(this._container);
10389                 map._removeZoomLimit(this);
10390                 this._container = null;
10391                 this._tileZoom = null;
10392         },
10393
10394         // @method bringToFront: this
10395         // Brings the tile layer to the top of all tile layers.
10396         bringToFront: function () {
10397                 if (this._map) {
10398                         toFront(this._container);
10399                         this._setAutoZIndex(Math.max);
10400                 }
10401                 return this;
10402         },
10403
10404         // @method bringToBack: this
10405         // Brings the tile layer to the bottom of all tile layers.
10406         bringToBack: function () {
10407                 if (this._map) {
10408                         toBack(this._container);
10409                         this._setAutoZIndex(Math.min);
10410                 }
10411                 return this;
10412         },
10413
10414         // @method getContainer: HTMLElement
10415         // Returns the HTML element that contains the tiles for this layer.
10416         getContainer: function () {
10417                 return this._container;
10418         },
10419
10420         // @method setOpacity(opacity: Number): this
10421         // Changes the [opacity](#gridlayer-opacity) of the grid layer.
10422         setOpacity: function (opacity) {
10423                 this.options.opacity = opacity;
10424                 this._updateOpacity();
10425                 return this;
10426         },
10427
10428         // @method setZIndex(zIndex: Number): this
10429         // Changes the [zIndex](#gridlayer-zindex) of the grid layer.
10430         setZIndex: function (zIndex) {
10431                 this.options.zIndex = zIndex;
10432                 this._updateZIndex();
10433
10434                 return this;
10435         },
10436
10437         // @method isLoading: Boolean
10438         // Returns `true` if any tile in the grid layer has not finished loading.
10439         isLoading: function () {
10440                 return this._loading;
10441         },
10442
10443         // @method redraw: this
10444         // Causes the layer to clear all the tiles and request them again.
10445         redraw: function () {
10446                 if (this._map) {
10447                         this._removeAllTiles();
10448                         this._update();
10449                 }
10450                 return this;
10451         },
10452
10453         getEvents: function () {
10454                 var events = {
10455                         viewprereset: this._invalidateAll,
10456                         viewreset: this._resetView,
10457                         zoom: this._resetView,
10458                         moveend: this._onMoveEnd
10459                 };
10460
10461                 if (!this.options.updateWhenIdle) {
10462                         // update tiles on move, but not more often than once per given interval
10463                         if (!this._onMove) {
10464                                 this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
10465                         }
10466
10467                         events.move = this._onMove;
10468                 }
10469
10470                 if (this._zoomAnimated) {
10471                         events.zoomanim = this._animateZoom;
10472                 }
10473
10474                 return events;
10475         },
10476
10477         // @section Extension methods
10478         // Layers extending `GridLayer` shall reimplement the following method.
10479         // @method createTile(coords: Object, done?: Function): HTMLElement
10480         // Called only internally, must be overriden by classes extending `GridLayer`.
10481         // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
10482         // is specified, it must be called when the tile has finished loading and drawing.
10483         createTile: function () {
10484                 return document.createElement('div');
10485         },
10486
10487         // @section
10488         // @method getTileSize: Point
10489         // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
10490         getTileSize: function () {
10491                 var s = this.options.tileSize;
10492                 return s instanceof Point ? s : new Point(s, s);
10493         },
10494
10495         _updateZIndex: function () {
10496                 if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
10497                         this._container.style.zIndex = this.options.zIndex;
10498                 }
10499         },
10500
10501         _setAutoZIndex: function (compare) {
10502                 // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
10503
10504                 var layers = this.getPane().children,
10505                     edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
10506
10507                 for (var i = 0, len = layers.length, zIndex; i < len; i++) {
10508
10509                         zIndex = layers[i].style.zIndex;
10510
10511                         if (layers[i] !== this._container && zIndex) {
10512                                 edgeZIndex = compare(edgeZIndex, +zIndex);
10513                         }
10514                 }
10515
10516                 if (isFinite(edgeZIndex)) {
10517                         this.options.zIndex = edgeZIndex + compare(-1, 1);
10518                         this._updateZIndex();
10519                 }
10520         },
10521
10522         _updateOpacity: function () {
10523                 if (!this._map) { return; }
10524
10525                 // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
10526                 if (ielt9) { return; }
10527
10528                 setOpacity(this._container, this.options.opacity);
10529
10530                 var now = +new Date(),
10531                     nextFrame = false,
10532                     willPrune = false;
10533
10534                 for (var key in this._tiles) {
10535                         var tile = this._tiles[key];
10536                         if (!tile.current || !tile.loaded) { continue; }
10537
10538                         var fade = Math.min(1, (now - tile.loaded) / 200);
10539
10540                         setOpacity(tile.el, fade);
10541                         if (fade < 1) {
10542                                 nextFrame = true;
10543                         } else {
10544                                 if (tile.active) {
10545                                         willPrune = true;
10546                                 } else {
10547                                         this._onOpaqueTile(tile);
10548                                 }
10549                                 tile.active = true;
10550                         }
10551                 }
10552
10553                 if (willPrune && !this._noPrune) { this._pruneTiles(); }
10554
10555                 if (nextFrame) {
10556                         cancelAnimFrame(this._fadeFrame);
10557                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
10558                 }
10559         },
10560
10561         _onOpaqueTile: falseFn,
10562
10563         _initContainer: function () {
10564                 if (this._container) { return; }
10565
10566                 this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
10567                 this._updateZIndex();
10568
10569                 if (this.options.opacity < 1) {
10570                         this._updateOpacity();
10571                 }
10572
10573                 this.getPane().appendChild(this._container);
10574         },
10575
10576         _updateLevels: function () {
10577
10578                 var zoom = this._tileZoom,
10579                     maxZoom = this.options.maxZoom;
10580
10581                 if (zoom === undefined) { return undefined; }
10582
10583                 for (var z in this._levels) {
10584                         if (this._levels[z].el.children.length || z === zoom) {
10585                                 this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
10586                                 this._onUpdateLevel(z);
10587                         } else {
10588                                 remove(this._levels[z].el);
10589                                 this._removeTilesAtZoom(z);
10590                                 this._onRemoveLevel(z);
10591                                 delete this._levels[z];
10592                         }
10593                 }
10594
10595                 var level = this._levels[zoom],
10596                     map = this._map;
10597
10598                 if (!level) {
10599                         level = this._levels[zoom] = {};
10600
10601                         level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
10602                         level.el.style.zIndex = maxZoom;
10603
10604                         level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
10605                         level.zoom = zoom;
10606
10607                         this._setZoomTransform(level, map.getCenter(), map.getZoom());
10608
10609                         // force the browser to consider the newly added element for transition
10610                         falseFn(level.el.offsetWidth);
10611
10612                         this._onCreateLevel(level);
10613                 }
10614
10615                 this._level = level;
10616
10617                 return level;
10618         },
10619
10620         _onUpdateLevel: falseFn,
10621
10622         _onRemoveLevel: falseFn,
10623
10624         _onCreateLevel: falseFn,
10625
10626         _pruneTiles: function () {
10627                 if (!this._map) {
10628                         return;
10629                 }
10630
10631                 var key, tile;
10632
10633                 var zoom = this._map.getZoom();
10634                 if (zoom > this.options.maxZoom ||
10635                         zoom < this.options.minZoom) {
10636                         this._removeAllTiles();
10637                         return;
10638                 }
10639
10640                 for (key in this._tiles) {
10641                         tile = this._tiles[key];
10642                         tile.retain = tile.current;
10643                 }
10644
10645                 for (key in this._tiles) {
10646                         tile = this._tiles[key];
10647                         if (tile.current && !tile.active) {
10648                                 var coords = tile.coords;
10649                                 if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
10650                                         this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
10651                                 }
10652                         }
10653                 }
10654
10655                 for (key in this._tiles) {
10656                         if (!this._tiles[key].retain) {
10657                                 this._removeTile(key);
10658                         }
10659                 }
10660         },
10661
10662         _removeTilesAtZoom: function (zoom) {
10663                 for (var key in this._tiles) {
10664                         if (this._tiles[key].coords.z !== zoom) {
10665                                 continue;
10666                         }
10667                         this._removeTile(key);
10668                 }
10669         },
10670
10671         _removeAllTiles: function () {
10672                 for (var key in this._tiles) {
10673                         this._removeTile(key);
10674                 }
10675         },
10676
10677         _invalidateAll: function () {
10678                 for (var z in this._levels) {
10679                         remove(this._levels[z].el);
10680                         this._onRemoveLevel(z);
10681                         delete this._levels[z];
10682                 }
10683                 this._removeAllTiles();
10684
10685                 this._tileZoom = null;
10686         },
10687
10688         _retainParent: function (x, y, z, minZoom) {
10689                 var x2 = Math.floor(x / 2),
10690                     y2 = Math.floor(y / 2),
10691                     z2 = z - 1,
10692                     coords2 = new Point(+x2, +y2);
10693                 coords2.z = +z2;
10694
10695                 var key = this._tileCoordsToKey(coords2),
10696                     tile = this._tiles[key];
10697
10698                 if (tile && tile.active) {
10699                         tile.retain = true;
10700                         return true;
10701
10702                 } else if (tile && tile.loaded) {
10703                         tile.retain = true;
10704                 }
10705
10706                 if (z2 > minZoom) {
10707                         return this._retainParent(x2, y2, z2, minZoom);
10708                 }
10709
10710                 return false;
10711         },
10712
10713         _retainChildren: function (x, y, z, maxZoom) {
10714
10715                 for (var i = 2 * x; i < 2 * x + 2; i++) {
10716                         for (var j = 2 * y; j < 2 * y + 2; j++) {
10717
10718                                 var coords = new Point(i, j);
10719                                 coords.z = z + 1;
10720
10721                                 var key = this._tileCoordsToKey(coords),
10722                                     tile = this._tiles[key];
10723
10724                                 if (tile && tile.active) {
10725                                         tile.retain = true;
10726                                         continue;
10727
10728                                 } else if (tile && tile.loaded) {
10729                                         tile.retain = true;
10730                                 }
10731
10732                                 if (z + 1 < maxZoom) {
10733                                         this._retainChildren(i, j, z + 1, maxZoom);
10734                                 }
10735                         }
10736                 }
10737         },
10738
10739         _resetView: function (e) {
10740                 var animating = e && (e.pinch || e.flyTo);
10741                 this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
10742         },
10743
10744         _animateZoom: function (e) {
10745                 this._setView(e.center, e.zoom, true, e.noUpdate);
10746         },
10747
10748         _clampZoom: function (zoom) {
10749                 var options = this.options;
10750
10751                 if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
10752                         return options.minNativeZoom;
10753                 }
10754
10755                 if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
10756                         return options.maxNativeZoom;
10757                 }
10758
10759                 return zoom;
10760         },
10761
10762         _setView: function (center, zoom, noPrune, noUpdate) {
10763                 var tileZoom = this._clampZoom(Math.round(zoom));
10764                 if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
10765                     (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
10766                         tileZoom = undefined;
10767                 }
10768
10769                 var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
10770
10771                 if (!noUpdate || tileZoomChanged) {
10772
10773                         this._tileZoom = tileZoom;
10774
10775                         if (this._abortLoading) {
10776                                 this._abortLoading();
10777                         }
10778
10779                         this._updateLevels();
10780                         this._resetGrid();
10781
10782                         if (tileZoom !== undefined) {
10783                                 this._update(center);
10784                         }
10785
10786                         if (!noPrune) {
10787                                 this._pruneTiles();
10788                         }
10789
10790                         // Flag to prevent _updateOpacity from pruning tiles during
10791                         // a zoom anim or a pinch gesture
10792                         this._noPrune = !!noPrune;
10793                 }
10794
10795                 this._setZoomTransforms(center, zoom);
10796         },
10797
10798         _setZoomTransforms: function (center, zoom) {
10799                 for (var i in this._levels) {
10800                         this._setZoomTransform(this._levels[i], center, zoom);
10801                 }
10802         },
10803
10804         _setZoomTransform: function (level, center, zoom) {
10805                 var scale = this._map.getZoomScale(zoom, level.zoom),
10806                     translate = level.origin.multiplyBy(scale)
10807                         .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
10808
10809                 if (any3d) {
10810                         setTransform(level.el, translate, scale);
10811                 } else {
10812                         setPosition(level.el, translate);
10813                 }
10814         },
10815
10816         _resetGrid: function () {
10817                 var map = this._map,
10818                     crs = map.options.crs,
10819                     tileSize = this._tileSize = this.getTileSize(),
10820                     tileZoom = this._tileZoom;
10821
10822                 var bounds = this._map.getPixelWorldBounds(this._tileZoom);
10823                 if (bounds) {
10824                         this._globalTileRange = this._pxBoundsToTileRange(bounds);
10825                 }
10826
10827                 this._wrapX = crs.wrapLng && !this.options.noWrap && [
10828                         Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
10829                         Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
10830                 ];
10831                 this._wrapY = crs.wrapLat && !this.options.noWrap && [
10832                         Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
10833                         Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
10834                 ];
10835         },
10836
10837         _onMoveEnd: function () {
10838                 if (!this._map || this._map._animatingZoom) { return; }
10839
10840                 this._update();
10841         },
10842
10843         _getTiledPixelBounds: function (center) {
10844                 var map = this._map,
10845                     mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
10846                     scale = map.getZoomScale(mapZoom, this._tileZoom),
10847                     pixelCenter = map.project(center, this._tileZoom).floor(),
10848                     halfSize = map.getSize().divideBy(scale * 2);
10849
10850                 return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
10851         },
10852
10853         // Private method to load tiles in the grid's active zoom level according to map bounds
10854         _update: function (center) {
10855                 var map = this._map;
10856                 if (!map) { return; }
10857                 var zoom = this._clampZoom(map.getZoom());
10858
10859                 if (center === undefined) { center = map.getCenter(); }
10860                 if (this._tileZoom === undefined) { return; }   // if out of minzoom/maxzoom
10861
10862                 var pixelBounds = this._getTiledPixelBounds(center),
10863                     tileRange = this._pxBoundsToTileRange(pixelBounds),
10864                     tileCenter = tileRange.getCenter(),
10865                     queue = [],
10866                     margin = this.options.keepBuffer,
10867                     noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
10868                                               tileRange.getTopRight().add([margin, -margin]));
10869
10870                 // Sanity check: panic if the tile range contains Infinity somewhere.
10871                 if (!(isFinite(tileRange.min.x) &&
10872                       isFinite(tileRange.min.y) &&
10873                       isFinite(tileRange.max.x) &&
10874                       isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
10875
10876                 for (var key in this._tiles) {
10877                         var c = this._tiles[key].coords;
10878                         if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
10879                                 this._tiles[key].current = false;
10880                         }
10881                 }
10882
10883                 // _update just loads more tiles. If the tile zoom level differs too much
10884                 // from the map's, let _setView reset levels and prune old tiles.
10885                 if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
10886
10887                 // create a queue of coordinates to load tiles from
10888                 for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
10889                         for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
10890                                 var coords = new Point(i, j);
10891                                 coords.z = this._tileZoom;
10892
10893                                 if (!this._isValidTile(coords)) { continue; }
10894
10895                                 if (!this._tiles[this._tileCoordsToKey(coords)]) {
10896                                         queue.push(coords);
10897                                 }
10898                         }
10899                 }
10900
10901                 // sort tile queue to load tiles in order of their distance to center
10902                 queue.sort(function (a, b) {
10903                         return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
10904                 });
10905
10906                 if (queue.length !== 0) {
10907                         // if it's the first batch of tiles to load
10908                         if (!this._loading) {
10909                                 this._loading = true;
10910                                 // @event loading: Event
10911                                 // Fired when the grid layer starts loading tiles.
10912                                 this.fire('loading');
10913                         }
10914
10915                         // create DOM fragment to append tiles in one batch
10916                         var fragment = document.createDocumentFragment();
10917
10918                         for (i = 0; i < queue.length; i++) {
10919                                 this._addTile(queue[i], fragment);
10920                         }
10921
10922                         this._level.el.appendChild(fragment);
10923                 }
10924         },
10925
10926         _isValidTile: function (coords) {
10927                 var crs = this._map.options.crs;
10928
10929                 if (!crs.infinite) {
10930                         // don't load tile if it's out of bounds and not wrapped
10931                         var bounds = this._globalTileRange;
10932                         if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
10933                             (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
10934                 }
10935
10936                 if (!this.options.bounds) { return true; }
10937
10938                 // don't load tile if it doesn't intersect the bounds in options
10939                 var tileBounds = this._tileCoordsToBounds(coords);
10940                 return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
10941         },
10942
10943         _keyToBounds: function (key) {
10944                 return this._tileCoordsToBounds(this._keyToTileCoords(key));
10945         },
10946
10947         // converts tile coordinates to its geographical bounds
10948         _tileCoordsToBounds: function (coords) {
10949
10950                 var map = this._map,
10951                     tileSize = this.getTileSize(),
10952
10953                     nwPoint = coords.scaleBy(tileSize),
10954                     sePoint = nwPoint.add(tileSize),
10955
10956                     nw = map.unproject(nwPoint, coords.z),
10957                     se = map.unproject(sePoint, coords.z),
10958                     bounds = new LatLngBounds(nw, se);
10959
10960                 if (!this.options.noWrap) {
10961                         map.wrapLatLngBounds(bounds);
10962                 }
10963
10964                 return bounds;
10965         },
10966
10967         // converts tile coordinates to key for the tile cache
10968         _tileCoordsToKey: function (coords) {
10969                 return coords.x + ':' + coords.y + ':' + coords.z;
10970         },
10971
10972         // converts tile cache key to coordinates
10973         _keyToTileCoords: function (key) {
10974                 var k = key.split(':'),
10975                     coords = new Point(+k[0], +k[1]);
10976                 coords.z = +k[2];
10977                 return coords;
10978         },
10979
10980         _removeTile: function (key) {
10981                 var tile = this._tiles[key];
10982                 if (!tile) { return; }
10983
10984                 remove(tile.el);
10985
10986                 delete this._tiles[key];
10987
10988                 // @event tileunload: TileEvent
10989                 // Fired when a tile is removed (e.g. when a tile goes off the screen).
10990                 this.fire('tileunload', {
10991                         tile: tile.el,
10992                         coords: this._keyToTileCoords(key)
10993                 });
10994         },
10995
10996         _initTile: function (tile) {
10997                 addClass(tile, 'leaflet-tile');
10998
10999                 var tileSize = this.getTileSize();
11000                 tile.style.width = tileSize.x + 'px';
11001                 tile.style.height = tileSize.y + 'px';
11002
11003                 tile.onselectstart = falseFn;
11004                 tile.onmousemove = falseFn;
11005
11006                 // update opacity on tiles in IE7-8 because of filter inheritance problems
11007                 if (ielt9 && this.options.opacity < 1) {
11008                         setOpacity(tile, this.options.opacity);
11009                 }
11010
11011                 // without this hack, tiles disappear after zoom on Chrome for Android
11012                 // https://github.com/Leaflet/Leaflet/issues/2078
11013                 if (android && !android23) {
11014                         tile.style.WebkitBackfaceVisibility = 'hidden';
11015                 }
11016         },
11017
11018         _addTile: function (coords, container) {
11019                 var tilePos = this._getTilePos(coords),
11020                     key = this._tileCoordsToKey(coords);
11021
11022                 var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
11023
11024                 this._initTile(tile);
11025
11026                 // if createTile is defined with a second argument ("done" callback),
11027                 // we know that tile is async and will be ready later; otherwise
11028                 if (this.createTile.length < 2) {
11029                         // mark tile as ready, but delay one frame for opacity animation to happen
11030                         requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
11031                 }
11032
11033                 setPosition(tile, tilePos);
11034
11035                 // save tile in cache
11036                 this._tiles[key] = {
11037                         el: tile,
11038                         coords: coords,
11039                         current: true
11040                 };
11041
11042                 container.appendChild(tile);
11043                 // @event tileloadstart: TileEvent
11044                 // Fired when a tile is requested and starts loading.
11045                 this.fire('tileloadstart', {
11046                         tile: tile,
11047                         coords: coords
11048                 });
11049         },
11050
11051         _tileReady: function (coords, err, tile) {
11052                 if (!this._map) { return; }
11053
11054                 if (err) {
11055                         // @event tileerror: TileErrorEvent
11056                         // Fired when there is an error loading a tile.
11057                         this.fire('tileerror', {
11058                                 error: err,
11059                                 tile: tile,
11060                                 coords: coords
11061                         });
11062                 }
11063
11064                 var key = this._tileCoordsToKey(coords);
11065
11066                 tile = this._tiles[key];
11067                 if (!tile) { return; }
11068
11069                 tile.loaded = +new Date();
11070                 if (this._map._fadeAnimated) {
11071                         setOpacity(tile.el, 0);
11072                         cancelAnimFrame(this._fadeFrame);
11073                         this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
11074                 } else {
11075                         tile.active = true;
11076                         this._pruneTiles();
11077                 }
11078
11079                 if (!err) {
11080                         addClass(tile.el, 'leaflet-tile-loaded');
11081
11082                         // @event tileload: TileEvent
11083                         // Fired when a tile loads.
11084                         this.fire('tileload', {
11085                                 tile: tile.el,
11086                                 coords: coords
11087                         });
11088                 }
11089
11090                 if (this._noTilesToLoad()) {
11091                         this._loading = false;
11092                         // @event load: Event
11093                         // Fired when the grid layer loaded all visible tiles.
11094                         this.fire('load');
11095
11096                         if (ielt9 || !this._map._fadeAnimated) {
11097                                 requestAnimFrame(this._pruneTiles, this);
11098                         } else {
11099                                 // Wait a bit more than 0.2 secs (the duration of the tile fade-in)
11100                                 // to trigger a pruning.
11101                                 setTimeout(bind(this._pruneTiles, this), 250);
11102                         }
11103                 }
11104         },
11105
11106         _getTilePos: function (coords) {
11107                 return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
11108         },
11109
11110         _wrapCoords: function (coords) {
11111                 var newCoords = new Point(
11112                         this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
11113                         this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
11114                 newCoords.z = coords.z;
11115                 return newCoords;
11116         },
11117
11118         _pxBoundsToTileRange: function (bounds) {
11119                 var tileSize = this.getTileSize();
11120                 return new Bounds(
11121                         bounds.min.unscaleBy(tileSize).floor(),
11122                         bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
11123         },
11124
11125         _noTilesToLoad: function () {
11126                 for (var key in this._tiles) {
11127                         if (!this._tiles[key].loaded) { return false; }
11128                 }
11129                 return true;
11130         }
11131 });
11132
11133 // @factory L.gridLayer(options?: GridLayer options)
11134 // Creates a new instance of GridLayer with the supplied options.
11135 function gridLayer(options) {
11136         return new GridLayer(options);
11137 }
11138
11139 /*
11140  * @class TileLayer
11141  * @inherits GridLayer
11142  * @aka L.TileLayer
11143  * Used to load and display tile layers on the map. Extends `GridLayer`.
11144  *
11145  * @example
11146  *
11147  * ```js
11148  * L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar'}).addTo(map);
11149  * ```
11150  *
11151  * @section URL template
11152  * @example
11153  *
11154  * A string of the following form:
11155  *
11156  * ```
11157  * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
11158  * ```
11159  *
11160  * `{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 @2x to the URL to load retina tiles.
11161  *
11162  * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
11163  *
11164  * ```
11165  * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
11166  * ```
11167  */
11168
11169
11170 var TileLayer = GridLayer.extend({
11171
11172         // @section
11173         // @aka TileLayer options
11174         options: {
11175                 // @option minZoom: Number = 0
11176                 // The minimum zoom level down to which this layer will be displayed (inclusive).
11177                 minZoom: 0,
11178
11179                 // @option maxZoom: Number = 18
11180                 // The maximum zoom level up to which this layer will be displayed (inclusive).
11181                 maxZoom: 18,
11182
11183                 // @option subdomains: String|String[] = 'abc'
11184                 // 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.
11185                 subdomains: 'abc',
11186
11187                 // @option errorTileUrl: String = ''
11188                 // URL to the tile image to show in place of the tile that failed to load.
11189                 errorTileUrl: '',
11190
11191                 // @option zoomOffset: Number = 0
11192                 // The zoom number used in tile URLs will be offset with this value.
11193                 zoomOffset: 0,
11194
11195                 // @option tms: Boolean = false
11196                 // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
11197                 tms: false,
11198
11199                 // @option zoomReverse: Boolean = false
11200                 // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
11201                 zoomReverse: false,
11202
11203                 // @option detectRetina: Boolean = false
11204                 // 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.
11205                 detectRetina: false,
11206
11207                 // @option crossOrigin: Boolean = false
11208                 // If true, all tiles will have their crossOrigin attribute set to ''. This is needed if you want to access tile pixel data.
11209                 crossOrigin: false
11210         },
11211
11212         initialize: function (url, options) {
11213
11214                 this._url = url;
11215
11216                 options = setOptions(this, options);
11217
11218                 // detecting retina displays, adjusting tileSize and zoom levels
11219                 if (options.detectRetina && retina && options.maxZoom > 0) {
11220
11221                         options.tileSize = Math.floor(options.tileSize / 2);
11222
11223                         if (!options.zoomReverse) {
11224                                 options.zoomOffset++;
11225                                 options.maxZoom--;
11226                         } else {
11227                                 options.zoomOffset--;
11228                                 options.minZoom++;
11229                         }
11230
11231                         options.minZoom = Math.max(0, options.minZoom);
11232                 }
11233
11234                 if (typeof options.subdomains === 'string') {
11235                         options.subdomains = options.subdomains.split('');
11236                 }
11237
11238                 // for https://github.com/Leaflet/Leaflet/issues/137
11239                 if (!android) {
11240                         this.on('tileunload', this._onTileRemove);
11241                 }
11242         },
11243
11244         // @method setUrl(url: String, noRedraw?: Boolean): this
11245         // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
11246         setUrl: function (url, noRedraw) {
11247                 this._url = url;
11248
11249                 if (!noRedraw) {
11250                         this.redraw();
11251                 }
11252                 return this;
11253         },
11254
11255         // @method createTile(coords: Object, done?: Function): HTMLElement
11256         // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
11257         // to return an `<img>` HTML element with the appropiate image URL given `coords`. The `done`
11258         // callback is called when the tile has been loaded.
11259         createTile: function (coords, done) {
11260                 var tile = document.createElement('img');
11261
11262                 on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
11263                 on(tile, 'error', bind(this._tileOnError, this, done, tile));
11264
11265                 if (this.options.crossOrigin) {
11266                         tile.crossOrigin = '';
11267                 }
11268
11269                 /*
11270                  Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
11271                  http://www.w3.org/TR/WCAG20-TECHS/H67
11272                 */
11273                 tile.alt = '';
11274
11275                 /*
11276                  Set role="presentation" to force screen readers to ignore this
11277                  https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
11278                 */
11279                 tile.setAttribute('role', 'presentation');
11280
11281                 tile.src = this.getTileUrl(coords);
11282
11283                 return tile;
11284         },
11285
11286         // @section Extension methods
11287         // @uninheritable
11288         // Layers extending `TileLayer` might reimplement the following method.
11289         // @method getTileUrl(coords: Object): String
11290         // Called only internally, returns the URL for a tile given its coordinates.
11291         // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
11292         getTileUrl: function (coords) {
11293                 var data = {
11294                         r: retina ? '@2x' : '',
11295                         s: this._getSubdomain(coords),
11296                         x: coords.x,
11297                         y: coords.y,
11298                         z: this._getZoomForUrl()
11299                 };
11300                 if (this._map && !this._map.options.crs.infinite) {
11301                         var invertedY = this._globalTileRange.max.y - coords.y;
11302                         if (this.options.tms) {
11303                                 data['y'] = invertedY;
11304                         }
11305                         data['-y'] = invertedY;
11306                 }
11307
11308                 return template(this._url, extend(data, this.options));
11309         },
11310
11311         _tileOnLoad: function (done, tile) {
11312                 // For https://github.com/Leaflet/Leaflet/issues/3332
11313                 if (ielt9) {
11314                         setTimeout(bind(done, this, null, tile), 0);
11315                 } else {
11316                         done(null, tile);
11317                 }
11318         },
11319
11320         _tileOnError: function (done, tile, e) {
11321                 var errorUrl = this.options.errorTileUrl;
11322                 if (errorUrl && tile.src !== errorUrl) {
11323                         tile.src = errorUrl;
11324                 }
11325                 done(e, tile);
11326         },
11327
11328         _onTileRemove: function (e) {
11329                 e.tile.onload = null;
11330         },
11331
11332         _getZoomForUrl: function () {
11333                 var zoom = this._tileZoom,
11334                 maxZoom = this.options.maxZoom,
11335                 zoomReverse = this.options.zoomReverse,
11336                 zoomOffset = this.options.zoomOffset;
11337
11338                 if (zoomReverse) {
11339                         zoom = maxZoom - zoom;
11340                 }
11341
11342                 return zoom + zoomOffset;
11343         },
11344
11345         _getSubdomain: function (tilePoint) {
11346                 var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
11347                 return this.options.subdomains[index];
11348         },
11349
11350         // stops loading all tiles in the background layer
11351         _abortLoading: function () {
11352                 var i, tile;
11353                 for (i in this._tiles) {
11354                         if (this._tiles[i].coords.z !== this._tileZoom) {
11355                                 tile = this._tiles[i].el;
11356
11357                                 tile.onload = falseFn;
11358                                 tile.onerror = falseFn;
11359
11360                                 if (!tile.complete) {
11361                                         tile.src = emptyImageUrl;
11362                                         remove(tile);
11363                                 }
11364                         }
11365                 }
11366         }
11367 });
11368
11369
11370 // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
11371 // Instantiates a tile layer object given a `URL template` and optionally an options object.
11372
11373 function tileLayer(url, options) {
11374         return new TileLayer(url, options);
11375 }
11376
11377 /*
11378  * @class TileLayer.WMS
11379  * @inherits TileLayer
11380  * @aka L.TileLayer.WMS
11381  * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
11382  *
11383  * @example
11384  *
11385  * ```js
11386  * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
11387  *      layers: 'nexrad-n0r-900913',
11388  *      format: 'image/png',
11389  *      transparent: true,
11390  *      attribution: "Weather data © 2012 IEM Nexrad"
11391  * });
11392  * ```
11393  */
11394
11395 var TileLayerWMS = TileLayer.extend({
11396
11397         // @section
11398         // @aka TileLayer.WMS options
11399         // If any custom options not documented here are used, they will be sent to the
11400         // WMS server as extra parameters in each request URL. This can be useful for
11401         // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
11402         defaultWmsParams: {
11403                 service: 'WMS',
11404                 request: 'GetMap',
11405
11406                 // @option layers: String = ''
11407                 // **(required)** Comma-separated list of WMS layers to show.
11408                 layers: '',
11409
11410                 // @option styles: String = ''
11411                 // Comma-separated list of WMS styles.
11412                 styles: '',
11413
11414                 // @option format: String = 'image/jpeg'
11415                 // WMS image format (use `'image/png'` for layers with transparency).
11416                 format: 'image/jpeg',
11417
11418                 // @option transparent: Boolean = false
11419                 // If `true`, the WMS service will return images with transparency.
11420                 transparent: false,
11421
11422                 // @option version: String = '1.1.1'
11423                 // Version of the WMS service to use
11424                 version: '1.1.1'
11425         },
11426
11427         options: {
11428                 // @option crs: CRS = null
11429                 // Coordinate Reference System to use for the WMS requests, defaults to
11430                 // map CRS. Don't change this if you're not sure what it means.
11431                 crs: null,
11432
11433                 // @option uppercase: Boolean = false
11434                 // If `true`, WMS request parameter keys will be uppercase.
11435                 uppercase: false
11436         },
11437
11438         initialize: function (url, options) {
11439
11440                 this._url = url;
11441
11442                 var wmsParams = extend({}, this.defaultWmsParams);
11443
11444                 // all keys that are not TileLayer options go to WMS params
11445                 for (var i in options) {
11446                         if (!(i in this.options)) {
11447                                 wmsParams[i] = options[i];
11448                         }
11449                 }
11450
11451                 options = setOptions(this, options);
11452
11453                 wmsParams.width = wmsParams.height = options.tileSize * (options.detectRetina && retina ? 2 : 1);
11454
11455                 this.wmsParams = wmsParams;
11456         },
11457
11458         onAdd: function (map) {
11459
11460                 this._crs = this.options.crs || map.options.crs;
11461                 this._wmsVersion = parseFloat(this.wmsParams.version);
11462
11463                 var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
11464                 this.wmsParams[projectionKey] = this._crs.code;
11465
11466                 TileLayer.prototype.onAdd.call(this, map);
11467         },
11468
11469         getTileUrl: function (coords) {
11470
11471                 var tileBounds = this._tileCoordsToBounds(coords),
11472                     nw = this._crs.project(tileBounds.getNorthWest()),
11473                     se = this._crs.project(tileBounds.getSouthEast()),
11474
11475                     bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
11476                             [se.y, nw.x, nw.y, se.x] :
11477                             [nw.x, se.y, se.x, nw.y]).join(','),
11478
11479                     url = TileLayer.prototype.getTileUrl.call(this, coords);
11480
11481                 return url +
11482                         getParamString(this.wmsParams, url, this.options.uppercase) +
11483                         (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
11484         },
11485
11486         // @method setParams(params: Object, noRedraw?: Boolean): this
11487         // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
11488         setParams: function (params, noRedraw) {
11489
11490                 extend(this.wmsParams, params);
11491
11492                 if (!noRedraw) {
11493                         this.redraw();
11494                 }
11495
11496                 return this;
11497         }
11498 });
11499
11500
11501 // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
11502 // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
11503 function tileLayerWMS(url, options) {
11504         return new TileLayerWMS(url, options);
11505 }
11506
11507 TileLayer.WMS = TileLayerWMS;
11508 tileLayer.wms = tileLayerWMS;
11509
11510 /*
11511  * @class Renderer
11512  * @inherits Layer
11513  * @aka L.Renderer
11514  *
11515  * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
11516  * DOM container of the renderer, its bounds, and its zoom animation.
11517  *
11518  * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
11519  * itself can be added or removed to the map. All paths use a renderer, which can
11520  * be implicit (the map will decide the type of renderer and use it automatically)
11521  * or explicit (using the [`renderer`](#path-renderer) option of the path).
11522  *
11523  * Do not use this class directly, use `SVG` and `Canvas` instead.
11524  *
11525  * @event update: Event
11526  * Fired when the renderer updates its bounds, center and zoom, for example when
11527  * its map has moved
11528  */
11529
11530 var Renderer = Layer.extend({
11531
11532         // @section
11533         // @aka Renderer options
11534         options: {
11535                 // @option padding: Number = 0.1
11536                 // How much to extend the clip area around the map view (relative to its size)
11537                 // e.g. 0.1 would be 10% of map view in each direction
11538                 padding: 0.1
11539         },
11540
11541         initialize: function (options) {
11542                 setOptions(this, options);
11543                 stamp(this);
11544                 this._layers = this._layers || {};
11545         },
11546
11547         onAdd: function () {
11548                 if (!this._container) {
11549                         this._initContainer(); // defined by renderer implementations
11550
11551                         if (this._zoomAnimated) {
11552                                 addClass(this._container, 'leaflet-zoom-animated');
11553                         }
11554                 }
11555
11556                 this.getPane().appendChild(this._container);
11557                 this._update();
11558                 this.on('update', this._updatePaths, this);
11559         },
11560
11561         onRemove: function () {
11562                 this.off('update', this._updatePaths, this);
11563                 this._destroyContainer();
11564         },
11565
11566         getEvents: function () {
11567                 var events = {
11568                         viewreset: this._reset,
11569                         zoom: this._onZoom,
11570                         moveend: this._update,
11571                         zoomend: this._onZoomEnd
11572                 };
11573                 if (this._zoomAnimated) {
11574                         events.zoomanim = this._onAnimZoom;
11575                 }
11576                 return events;
11577         },
11578
11579         _onAnimZoom: function (ev) {
11580                 this._updateTransform(ev.center, ev.zoom);
11581         },
11582
11583         _onZoom: function () {
11584                 this._updateTransform(this._map.getCenter(), this._map.getZoom());
11585         },
11586
11587         _updateTransform: function (center, zoom) {
11588                 var scale = this._map.getZoomScale(zoom, this._zoom),
11589                     position = getPosition(this._container),
11590                     viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
11591                     currentCenterPoint = this._map.project(this._center, zoom),
11592                     destCenterPoint = this._map.project(center, zoom),
11593                     centerOffset = destCenterPoint.subtract(currentCenterPoint),
11594
11595                     topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
11596
11597                 if (any3d) {
11598                         setTransform(this._container, topLeftOffset, scale);
11599                 } else {
11600                         setPosition(this._container, topLeftOffset);
11601                 }
11602         },
11603
11604         _reset: function () {
11605                 this._update();
11606                 this._updateTransform(this._center, this._zoom);
11607
11608                 for (var id in this._layers) {
11609                         this._layers[id]._reset();
11610                 }
11611         },
11612
11613         _onZoomEnd: function () {
11614                 for (var id in this._layers) {
11615                         this._layers[id]._project();
11616                 }
11617         },
11618
11619         _updatePaths: function () {
11620                 for (var id in this._layers) {
11621                         this._layers[id]._update();
11622                 }
11623         },
11624
11625         _update: function () {
11626                 // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
11627                 // Subclasses are responsible of firing the 'update' event.
11628                 var p = this.options.padding,
11629                     size = this._map.getSize(),
11630                     min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
11631
11632                 this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
11633
11634                 this._center = this._map.getCenter();
11635                 this._zoom = this._map.getZoom();
11636         }
11637 });
11638
11639 /*
11640  * @class Canvas
11641  * @inherits Renderer
11642  * @aka L.Canvas
11643  *
11644  * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
11645  * Inherits `Renderer`.
11646  *
11647  * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
11648  * available in all web browsers, notably IE8, and overlapping geometries might
11649  * not display properly in some edge cases.
11650  *
11651  * @example
11652  *
11653  * Use Canvas by default for all paths in the map:
11654  *
11655  * ```js
11656  * var map = L.map('map', {
11657  *      renderer: L.canvas()
11658  * });
11659  * ```
11660  *
11661  * Use a Canvas renderer with extra padding for specific vector geometries:
11662  *
11663  * ```js
11664  * var map = L.map('map');
11665  * var myRenderer = L.canvas({ padding: 0.5 });
11666  * var line = L.polyline( coordinates, { renderer: myRenderer } );
11667  * var circle = L.circle( center, { renderer: myRenderer } );
11668  * ```
11669  */
11670
11671 var Canvas = Renderer.extend({
11672         getEvents: function () {
11673                 var events = Renderer.prototype.getEvents.call(this);
11674                 events.viewprereset = this._onViewPreReset;
11675                 return events;
11676         },
11677
11678         _onViewPreReset: function () {
11679                 // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
11680                 this._postponeUpdatePaths = true;
11681         },
11682
11683         onAdd: function () {
11684                 Renderer.prototype.onAdd.call(this);
11685
11686                 // Redraw vectors since canvas is cleared upon removal,
11687                 // in case of removing the renderer itself from the map.
11688                 this._draw();
11689         },
11690
11691         _initContainer: function () {
11692                 var container = this._container = document.createElement('canvas');
11693
11694                 on(container, 'mousemove', throttle(this._onMouseMove, 32, this), this);
11695                 on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
11696                 on(container, 'mouseout', this._handleMouseOut, this);
11697
11698                 this._ctx = container.getContext('2d');
11699         },
11700
11701         _destroyContainer: function () {
11702                 delete this._ctx;
11703                 remove(this._container);
11704                 off(this._container);
11705                 delete this._container;
11706         },
11707
11708         _updatePaths: function () {
11709                 if (this._postponeUpdatePaths) { return; }
11710
11711                 var layer;
11712                 this._redrawBounds = null;
11713                 for (var id in this._layers) {
11714                         layer = this._layers[id];
11715                         layer._update();
11716                 }
11717                 this._redraw();
11718         },
11719
11720         _update: function () {
11721                 if (this._map._animatingZoom && this._bounds) { return; }
11722
11723                 this._drawnLayers = {};
11724
11725                 Renderer.prototype._update.call(this);
11726
11727                 var b = this._bounds,
11728                     container = this._container,
11729                     size = b.getSize(),
11730                     m = retina ? 2 : 1;
11731
11732                 setPosition(container, b.min);
11733
11734                 // set canvas size (also clearing it); use double size on retina
11735                 container.width = m * size.x;
11736                 container.height = m * size.y;
11737                 container.style.width = size.x + 'px';
11738                 container.style.height = size.y + 'px';
11739
11740                 if (retina) {
11741                         this._ctx.scale(2, 2);
11742                 }
11743
11744                 // translate so we use the same path coordinates after canvas element moves
11745                 this._ctx.translate(-b.min.x, -b.min.y);
11746
11747                 // Tell paths to redraw themselves
11748                 this.fire('update');
11749         },
11750
11751         _reset: function () {
11752                 Renderer.prototype._reset.call(this);
11753
11754                 if (this._postponeUpdatePaths) {
11755                         this._postponeUpdatePaths = false;
11756                         this._updatePaths();
11757                 }
11758         },
11759
11760         _initPath: function (layer) {
11761                 this._updateDashArray(layer);
11762                 this._layers[stamp(layer)] = layer;
11763
11764                 var order = layer._order = {
11765                         layer: layer,
11766                         prev: this._drawLast,
11767                         next: null
11768                 };
11769                 if (this._drawLast) { this._drawLast.next = order; }
11770                 this._drawLast = order;
11771                 this._drawFirst = this._drawFirst || this._drawLast;
11772         },
11773
11774         _addPath: function (layer) {
11775                 this._requestRedraw(layer);
11776         },
11777
11778         _removePath: function (layer) {
11779                 var order = layer._order;
11780                 var next = order.next;
11781                 var prev = order.prev;
11782
11783                 if (next) {
11784                         next.prev = prev;
11785                 } else {
11786                         this._drawLast = prev;
11787                 }
11788                 if (prev) {
11789                         prev.next = next;
11790                 } else {
11791                         this._drawFirst = next;
11792                 }
11793
11794                 delete layer._order;
11795
11796                 delete this._layers[L.stamp(layer)];
11797
11798                 this._requestRedraw(layer);
11799         },
11800
11801         _updatePath: function (layer) {
11802                 // Redraw the union of the layer's old pixel
11803                 // bounds and the new pixel bounds.
11804                 this._extendRedrawBounds(layer);
11805                 layer._project();
11806                 layer._update();
11807                 // The redraw will extend the redraw bounds
11808                 // with the new pixel bounds.
11809                 this._requestRedraw(layer);
11810         },
11811
11812         _updateStyle: function (layer) {
11813                 this._updateDashArray(layer);
11814                 this._requestRedraw(layer);
11815         },
11816
11817         _updateDashArray: function (layer) {
11818                 if (layer.options.dashArray) {
11819                         var parts = layer.options.dashArray.split(','),
11820                             dashArray = [],
11821                             i;
11822                         for (i = 0; i < parts.length; i++) {
11823                                 dashArray.push(Number(parts[i]));
11824                         }
11825                         layer.options._dashArray = dashArray;
11826                 }
11827         },
11828
11829         _requestRedraw: function (layer) {
11830                 if (!this._map) { return; }
11831
11832                 this._extendRedrawBounds(layer);
11833                 this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
11834         },
11835
11836         _extendRedrawBounds: function (layer) {
11837                 if (layer._pxBounds) {
11838                         var padding = (layer.options.weight || 0) + 1;
11839                         this._redrawBounds = this._redrawBounds || new Bounds();
11840                         this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
11841                         this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
11842                 }
11843         },
11844
11845         _redraw: function () {
11846                 this._redrawRequest = null;
11847
11848                 if (this._redrawBounds) {
11849                         this._redrawBounds.min._floor();
11850                         this._redrawBounds.max._ceil();
11851                 }
11852
11853                 this._clear(); // clear layers in redraw bounds
11854                 this._draw(); // draw layers
11855
11856                 this._redrawBounds = null;
11857         },
11858
11859         _clear: function () {
11860                 var bounds = this._redrawBounds;
11861                 if (bounds) {
11862                         var size = bounds.getSize();
11863                         this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
11864                 } else {
11865                         this._ctx.clearRect(0, 0, this._container.width, this._container.height);
11866                 }
11867         },
11868
11869         _draw: function () {
11870                 var layer, bounds = this._redrawBounds;
11871                 this._ctx.save();
11872                 if (bounds) {
11873                         var size = bounds.getSize();
11874                         this._ctx.beginPath();
11875                         this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
11876                         this._ctx.clip();
11877                 }
11878
11879                 this._drawing = true;
11880
11881                 for (var order = this._drawFirst; order; order = order.next) {
11882                         layer = order.layer;
11883                         if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
11884                                 layer._updatePath();
11885                         }
11886                 }
11887
11888                 this._drawing = false;
11889
11890                 this._ctx.restore();  // Restore state before clipping.
11891         },
11892
11893         _updatePoly: function (layer, closed) {
11894                 if (!this._drawing) { return; }
11895
11896                 var i, j, len2, p,
11897                     parts = layer._parts,
11898                     len = parts.length,
11899                     ctx = this._ctx;
11900
11901                 if (!len) { return; }
11902
11903                 this._drawnLayers[layer._leaflet_id] = layer;
11904
11905                 ctx.beginPath();
11906
11907                 for (i = 0; i < len; i++) {
11908                         for (j = 0, len2 = parts[i].length; j < len2; j++) {
11909                                 p = parts[i][j];
11910                                 ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
11911                         }
11912                         if (closed) {
11913                                 ctx.closePath();
11914                         }
11915                 }
11916
11917                 this._fillStroke(ctx, layer);
11918
11919                 // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
11920         },
11921
11922         _updateCircle: function (layer) {
11923
11924                 if (!this._drawing || layer._empty()) { return; }
11925
11926                 var p = layer._point,
11927                     ctx = this._ctx,
11928                     r = layer._radius,
11929                     s = (layer._radiusY || r) / r;
11930
11931                 this._drawnLayers[layer._leaflet_id] = layer;
11932
11933                 if (s !== 1) {
11934                         ctx.save();
11935                         ctx.scale(1, s);
11936                 }
11937
11938                 ctx.beginPath();
11939                 ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
11940
11941                 if (s !== 1) {
11942                         ctx.restore();
11943                 }
11944
11945                 this._fillStroke(ctx, layer);
11946         },
11947
11948         _fillStroke: function (ctx, layer) {
11949                 var options = layer.options;
11950
11951                 if (options.fill) {
11952                         ctx.globalAlpha = options.fillOpacity;
11953                         ctx.fillStyle = options.fillColor || options.color;
11954                         ctx.fill(options.fillRule || 'evenodd');
11955                 }
11956
11957                 if (options.stroke && options.weight !== 0) {
11958                         if (ctx.setLineDash) {
11959                                 ctx.setLineDash(layer.options && layer.options._dashArray || []);
11960                         }
11961                         ctx.globalAlpha = options.opacity;
11962                         ctx.lineWidth = options.weight;
11963                         ctx.strokeStyle = options.color;
11964                         ctx.lineCap = options.lineCap;
11965                         ctx.lineJoin = options.lineJoin;
11966                         ctx.stroke();
11967                 }
11968         },
11969
11970         // Canvas obviously doesn't have mouse events for individual drawn objects,
11971         // so we emulate that by calculating what's under the mouse on mousemove/click manually
11972
11973         _onClick: function (e) {
11974                 var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
11975
11976                 for (var order = this._drawFirst; order; order = order.next) {
11977                         layer = order.layer;
11978                         if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
11979                                 clickedLayer = layer;
11980                         }
11981                 }
11982                 if (clickedLayer)  {
11983                         fakeStop(e);
11984                         this._fireEvent([clickedLayer], e);
11985                 }
11986         },
11987
11988         _onMouseMove: function (e) {
11989                 if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
11990
11991                 var point = this._map.mouseEventToLayerPoint(e);
11992                 this._handleMouseHover(e, point);
11993         },
11994
11995
11996         _handleMouseOut: function (e) {
11997                 var layer = this._hoveredLayer;
11998                 if (layer) {
11999                         // if we're leaving the layer, fire mouseout
12000                         removeClass(this._container, 'leaflet-interactive');
12001                         this._fireEvent([layer], e, 'mouseout');
12002                         this._hoveredLayer = null;
12003                 }
12004         },
12005
12006         _handleMouseHover: function (e, point) {
12007                 var layer, candidateHoveredLayer;
12008
12009                 for (var order = this._drawFirst; order; order = order.next) {
12010                         layer = order.layer;
12011                         if (layer.options.interactive && layer._containsPoint(point)) {
12012                                 candidateHoveredLayer = layer;
12013                         }
12014                 }
12015
12016                 if (candidateHoveredLayer !== this._hoveredLayer) {
12017                         this._handleMouseOut(e);
12018
12019                         if (candidateHoveredLayer) {
12020                                 addClass(this._container, 'leaflet-interactive'); // change cursor
12021                                 this._fireEvent([candidateHoveredLayer], e, 'mouseover');
12022                                 this._hoveredLayer = candidateHoveredLayer;
12023                         }
12024                 }
12025
12026                 if (this._hoveredLayer) {
12027                         this._fireEvent([this._hoveredLayer], e);
12028                 }
12029         },
12030
12031         _fireEvent: function (layers, e, type) {
12032                 this._map._fireDOMEvent(e, type || e.type, layers);
12033         },
12034
12035         _bringToFront: function (layer) {
12036                 var order = layer._order;
12037                 var next = order.next;
12038                 var prev = order.prev;
12039
12040                 if (next) {
12041                         next.prev = prev;
12042                 } else {
12043                         // Already last
12044                         return;
12045                 }
12046                 if (prev) {
12047                         prev.next = next;
12048                 } else if (next) {
12049                         // Update first entry unless this is the
12050                         // signle entry
12051                         this._drawFirst = next;
12052                 }
12053
12054                 order.prev = this._drawLast;
12055                 this._drawLast.next = order;
12056
12057                 order.next = null;
12058                 this._drawLast = order;
12059
12060                 this._requestRedraw(layer);
12061         },
12062
12063         _bringToBack: function (layer) {
12064                 var order = layer._order;
12065                 var next = order.next;
12066                 var prev = order.prev;
12067
12068                 if (prev) {
12069                         prev.next = next;
12070                 } else {
12071                         // Already first
12072                         return;
12073                 }
12074                 if (next) {
12075                         next.prev = prev;
12076                 } else if (prev) {
12077                         // Update last entry unless this is the
12078                         // signle entry
12079                         this._drawLast = prev;
12080                 }
12081
12082                 order.prev = null;
12083
12084                 order.next = this._drawFirst;
12085                 this._drawFirst.prev = order;
12086                 this._drawFirst = order;
12087
12088                 this._requestRedraw(layer);
12089         }
12090 });
12091
12092 // @factory L.canvas(options?: Renderer options)
12093 // Creates a Canvas renderer with the given options.
12094 function canvas$1(options) {
12095         return canvas ? new Canvas(options) : null;
12096 }
12097
12098 /*
12099  * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
12100  */
12101
12102
12103 var vmlCreate = (function () {
12104         try {
12105                 document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
12106                 return function (name) {
12107                         return document.createElement('<lvml:' + name + ' class="lvml">');
12108                 };
12109         } catch (e) {
12110                 return function (name) {
12111                         return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
12112                 };
12113         }
12114 })();
12115
12116
12117 /*
12118  * @class SVG
12119  *
12120  * 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.
12121  *
12122  * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
12123  * with old versions of Internet Explorer.
12124  */
12125
12126 // mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
12127 var vmlMixin = {
12128
12129         _initContainer: function () {
12130                 this._container = create$1('div', 'leaflet-vml-container');
12131         },
12132
12133         _update: function () {
12134                 if (this._map._animatingZoom) { return; }
12135                 Renderer.prototype._update.call(this);
12136                 this.fire('update');
12137         },
12138
12139         _initPath: function (layer) {
12140                 var container = layer._container = vmlCreate('shape');
12141
12142                 addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
12143
12144                 container.coordsize = '1 1';
12145
12146                 layer._path = vmlCreate('path');
12147                 container.appendChild(layer._path);
12148
12149                 this._updateStyle(layer);
12150                 this._layers[stamp(layer)] = layer;
12151         },
12152
12153         _addPath: function (layer) {
12154                 var container = layer._container;
12155                 this._container.appendChild(container);
12156
12157                 if (layer.options.interactive) {
12158                         layer.addInteractiveTarget(container);
12159                 }
12160         },
12161
12162         _removePath: function (layer) {
12163                 var container = layer._container;
12164                 remove(container);
12165                 layer.removeInteractiveTarget(container);
12166                 delete this._layers[stamp(layer)];
12167         },
12168
12169         _updateStyle: function (layer) {
12170                 var stroke = layer._stroke,
12171                     fill = layer._fill,
12172                     options = layer.options,
12173                     container = layer._container;
12174
12175                 container.stroked = !!options.stroke;
12176                 container.filled = !!options.fill;
12177
12178                 if (options.stroke) {
12179                         if (!stroke) {
12180                                 stroke = layer._stroke = vmlCreate('stroke');
12181                         }
12182                         container.appendChild(stroke);
12183                         stroke.weight = options.weight + 'px';
12184                         stroke.color = options.color;
12185                         stroke.opacity = options.opacity;
12186
12187                         if (options.dashArray) {
12188                                 stroke.dashStyle = isArray(options.dashArray) ?
12189                                     options.dashArray.join(' ') :
12190                                     options.dashArray.replace(/( *, *)/g, ' ');
12191                         } else {
12192                                 stroke.dashStyle = '';
12193                         }
12194                         stroke.endcap = options.lineCap.replace('butt', 'flat');
12195                         stroke.joinstyle = options.lineJoin;
12196
12197                 } else if (stroke) {
12198                         container.removeChild(stroke);
12199                         layer._stroke = null;
12200                 }
12201
12202                 if (options.fill) {
12203                         if (!fill) {
12204                                 fill = layer._fill = vmlCreate('fill');
12205                         }
12206                         container.appendChild(fill);
12207                         fill.color = options.fillColor || options.color;
12208                         fill.opacity = options.fillOpacity;
12209
12210                 } else if (fill) {
12211                         container.removeChild(fill);
12212                         layer._fill = null;
12213                 }
12214         },
12215
12216         _updateCircle: function (layer) {
12217                 var p = layer._point.round(),
12218                     r = Math.round(layer._radius),
12219                     r2 = Math.round(layer._radiusY || r);
12220
12221                 this._setPath(layer, layer._empty() ? 'M0 0' :
12222                                 'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
12223         },
12224
12225         _setPath: function (layer, path) {
12226                 layer._path.v = path;
12227         },
12228
12229         _bringToFront: function (layer) {
12230                 toFront(layer._container);
12231         },
12232
12233         _bringToBack: function (layer) {
12234                 toBack(layer._container);
12235         }
12236 };
12237
12238 var create$2 = vml ? vmlCreate : svgCreate;
12239
12240 /*
12241  * @class SVG
12242  * @inherits Renderer
12243  * @aka L.SVG
12244  *
12245  * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
12246  * Inherits `Renderer`.
12247  *
12248  * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
12249  * available in all web browsers, notably Android 2.x and 3.x.
12250  *
12251  * Although SVG is not available on IE7 and IE8, these browsers support
12252  * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
12253  * (a now deprecated technology), and the SVG renderer will fall back to VML in
12254  * this case.
12255  *
12256  * @example
12257  *
12258  * Use SVG by default for all paths in the map:
12259  *
12260  * ```js
12261  * var map = L.map('map', {
12262  *      renderer: L.svg()
12263  * });
12264  * ```
12265  *
12266  * Use a SVG renderer with extra padding for specific vector geometries:
12267  *
12268  * ```js
12269  * var map = L.map('map');
12270  * var myRenderer = L.svg({ padding: 0.5 });
12271  * var line = L.polyline( coordinates, { renderer: myRenderer } );
12272  * var circle = L.circle( center, { renderer: myRenderer } );
12273  * ```
12274  */
12275
12276 var SVG = Renderer.extend({
12277
12278         getEvents: function () {
12279                 var events = Renderer.prototype.getEvents.call(this);
12280                 events.zoomstart = this._onZoomStart;
12281                 return events;
12282         },
12283
12284         _initContainer: function () {
12285                 this._container = create$2('svg');
12286
12287                 // makes it possible to click through svg root; we'll reset it back in individual paths
12288                 this._container.setAttribute('pointer-events', 'none');
12289
12290                 this._rootGroup = create$2('g');
12291                 this._container.appendChild(this._rootGroup);
12292         },
12293
12294         _destroyContainer: function () {
12295                 remove(this._container);
12296                 off(this._container);
12297                 delete this._container;
12298                 delete this._rootGroup;
12299         },
12300
12301         _onZoomStart: function () {
12302                 // Drag-then-pinch interactions might mess up the center and zoom.
12303                 // In this case, the easiest way to prevent this is re-do the renderer
12304                 //   bounds and padding when the zooming starts.
12305                 this._update();
12306         },
12307
12308         _update: function () {
12309                 if (this._map._animatingZoom && this._bounds) { return; }
12310
12311                 Renderer.prototype._update.call(this);
12312
12313                 var b = this._bounds,
12314                     size = b.getSize(),
12315                     container = this._container;
12316
12317                 // set size of svg-container if changed
12318                 if (!this._svgSize || !this._svgSize.equals(size)) {
12319                         this._svgSize = size;
12320                         container.setAttribute('width', size.x);
12321                         container.setAttribute('height', size.y);
12322                 }
12323
12324                 // movement: update container viewBox so that we don't have to change coordinates of individual layers
12325                 setPosition(container, b.min);
12326                 container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
12327
12328                 this.fire('update');
12329         },
12330
12331         // methods below are called by vector layers implementations
12332
12333         _initPath: function (layer) {
12334                 var path = layer._path = create$2('path');
12335
12336                 // @namespace Path
12337                 // @option className: String = null
12338                 // Custom class name set on an element. Only for SVG renderer.
12339                 if (layer.options.className) {
12340                         addClass(path, layer.options.className);
12341                 }
12342
12343                 if (layer.options.interactive) {
12344                         addClass(path, 'leaflet-interactive');
12345                 }
12346
12347                 this._updateStyle(layer);
12348                 this._layers[stamp(layer)] = layer;
12349         },
12350
12351         _addPath: function (layer) {
12352                 if (!this._rootGroup) { this._initContainer(); }
12353                 this._rootGroup.appendChild(layer._path);
12354                 layer.addInteractiveTarget(layer._path);
12355         },
12356
12357         _removePath: function (layer) {
12358                 remove(layer._path);
12359                 layer.removeInteractiveTarget(layer._path);
12360                 delete this._layers[stamp(layer)];
12361         },
12362
12363         _updatePath: function (layer) {
12364                 layer._project();
12365                 layer._update();
12366         },
12367
12368         _updateStyle: function (layer) {
12369                 var path = layer._path,
12370                     options = layer.options;
12371
12372                 if (!path) { return; }
12373
12374                 if (options.stroke) {
12375                         path.setAttribute('stroke', options.color);
12376                         path.setAttribute('stroke-opacity', options.opacity);
12377                         path.setAttribute('stroke-width', options.weight);
12378                         path.setAttribute('stroke-linecap', options.lineCap);
12379                         path.setAttribute('stroke-linejoin', options.lineJoin);
12380
12381                         if (options.dashArray) {
12382                                 path.setAttribute('stroke-dasharray', options.dashArray);
12383                         } else {
12384                                 path.removeAttribute('stroke-dasharray');
12385                         }
12386
12387                         if (options.dashOffset) {
12388                                 path.setAttribute('stroke-dashoffset', options.dashOffset);
12389                         } else {
12390                                 path.removeAttribute('stroke-dashoffset');
12391                         }
12392                 } else {
12393                         path.setAttribute('stroke', 'none');
12394                 }
12395
12396                 if (options.fill) {
12397                         path.setAttribute('fill', options.fillColor || options.color);
12398                         path.setAttribute('fill-opacity', options.fillOpacity);
12399                         path.setAttribute('fill-rule', options.fillRule || 'evenodd');
12400                 } else {
12401                         path.setAttribute('fill', 'none');
12402                 }
12403         },
12404
12405         _updatePoly: function (layer, closed) {
12406                 this._setPath(layer, pointsToPath(layer._parts, closed));
12407         },
12408
12409         _updateCircle: function (layer) {
12410                 var p = layer._point,
12411                     r = layer._radius,
12412                     r2 = layer._radiusY || r,
12413                     arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
12414
12415                 // drawing a circle with two half-arcs
12416                 var d = layer._empty() ? 'M0 0' :
12417                                 'M' + (p.x - r) + ',' + p.y +
12418                                 arc + (r * 2) + ',0 ' +
12419                                 arc + (-r * 2) + ',0 ';
12420
12421                 this._setPath(layer, d);
12422         },
12423
12424         _setPath: function (layer, path) {
12425                 layer._path.setAttribute('d', path);
12426         },
12427
12428         // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
12429         _bringToFront: function (layer) {
12430                 toFront(layer._path);
12431         },
12432
12433         _bringToBack: function (layer) {
12434                 toBack(layer._path);
12435         }
12436 });
12437
12438 if (vml) {
12439         SVG.include(vmlMixin);
12440 }
12441
12442 // @factory L.svg(options?: Renderer options)
12443 // Creates a SVG renderer with the given options.
12444 function svg$1(options) {
12445         return svg || vml ? new SVG(options) : null;
12446 }
12447
12448 Map.include({
12449         // @namespace Map; @method getRenderer(layer: Path): Renderer
12450         // Returns the instance of `Renderer` that should be used to render the given
12451         // `Path`. It will ensure that the `renderer` options of the map and paths
12452         // are respected, and that the renderers do exist on the map.
12453         getRenderer: function (layer) {
12454                 // @namespace Path; @option renderer: Renderer
12455                 // Use this specific instance of `Renderer` for this path. Takes
12456                 // precedence over the map's [default renderer](#map-renderer).
12457                 var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
12458
12459                 if (!renderer) {
12460                         // @namespace Map; @option preferCanvas: Boolean = false
12461                         // Whether `Path`s should be rendered on a `Canvas` renderer.
12462                         // By default, all `Path`s are rendered in a `SVG` renderer.
12463                         renderer = this._renderer = (this.options.preferCanvas && canvas$1()) || svg$1();
12464                 }
12465
12466                 if (!this.hasLayer(renderer)) {
12467                         this.addLayer(renderer);
12468                 }
12469                 return renderer;
12470         },
12471
12472         _getPaneRenderer: function (name) {
12473                 if (name === 'overlayPane' || name === undefined) {
12474                         return false;
12475                 }
12476
12477                 var renderer = this._paneRenderers[name];
12478                 if (renderer === undefined) {
12479                         renderer = (SVG && svg$1({pane: name})) || (Canvas && canvas$1({pane: name}));
12480                         this._paneRenderers[name] = renderer;
12481                 }
12482                 return renderer;
12483         }
12484 });
12485
12486 /*
12487  * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
12488  */
12489
12490 /*
12491  * @class Rectangle
12492  * @aka L.Retangle
12493  * @inherits Polygon
12494  *
12495  * A class for drawing rectangle overlays on a map. Extends `Polygon`.
12496  *
12497  * @example
12498  *
12499  * ```js
12500  * // define rectangle geographical bounds
12501  * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
12502  *
12503  * // create an orange rectangle
12504  * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
12505  *
12506  * // zoom the map to the rectangle bounds
12507  * map.fitBounds(bounds);
12508  * ```
12509  *
12510  */
12511
12512
12513 var Rectangle = Polygon.extend({
12514         initialize: function (latLngBounds, options) {
12515                 Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
12516         },
12517
12518         // @method setBounds(latLngBounds: LatLngBounds): this
12519         // Redraws the rectangle with the passed bounds.
12520         setBounds: function (latLngBounds) {
12521                 return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
12522         },
12523
12524         _boundsToLatLngs: function (latLngBounds) {
12525                 latLngBounds = toLatLngBounds(latLngBounds);
12526                 return [
12527                         latLngBounds.getSouthWest(),
12528                         latLngBounds.getNorthWest(),
12529                         latLngBounds.getNorthEast(),
12530                         latLngBounds.getSouthEast()
12531                 ];
12532         }
12533 });
12534
12535
12536 // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
12537 function rectangle(latLngBounds, options) {
12538         return new Rectangle(latLngBounds, options);
12539 }
12540
12541 SVG.create = create$2;
12542 SVG.pointsToPath = pointsToPath;
12543
12544 GeoJSON.geometryToLayer = geometryToLayer;
12545 GeoJSON.coordsToLatLng = coordsToLatLng;
12546 GeoJSON.coordsToLatLngs = coordsToLatLngs;
12547 GeoJSON.latLngToCoords = latLngToCoords;
12548 GeoJSON.latLngsToCoords = latLngsToCoords;
12549 GeoJSON.getFeature = getFeature;
12550 GeoJSON.asFeature = asFeature;
12551
12552 /*
12553  * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
12554  * (zoom to a selected bounding box), enabled by default.
12555  */
12556
12557 // @namespace Map
12558 // @section Interaction Options
12559 Map.mergeOptions({
12560         // @option boxZoom: Boolean = true
12561         // Whether the map can be zoomed to a rectangular area specified by
12562         // dragging the mouse while pressing the shift key.
12563         boxZoom: true
12564 });
12565
12566 var BoxZoom = Handler.extend({
12567         initialize: function (map) {
12568                 this._map = map;
12569                 this._container = map._container;
12570                 this._pane = map._panes.overlayPane;
12571                 this._resetStateTimeout = 0;
12572                 map.on('unload', this._destroy, this);
12573         },
12574
12575         addHooks: function () {
12576                 on(this._container, 'mousedown', this._onMouseDown, this);
12577         },
12578
12579         removeHooks: function () {
12580                 off(this._container, 'mousedown', this._onMouseDown, this);
12581         },
12582
12583         moved: function () {
12584                 return this._moved;
12585         },
12586
12587         _destroy: function () {
12588                 remove(this._pane);
12589                 delete this._pane;
12590         },
12591
12592         _resetState: function () {
12593                 this._resetStateTimeout = 0;
12594                 this._moved = false;
12595         },
12596
12597         _clearDeferredResetState: function () {
12598                 if (this._resetStateTimeout !== 0) {
12599                         clearTimeout(this._resetStateTimeout);
12600                         this._resetStateTimeout = 0;
12601                 }
12602         },
12603
12604         _onMouseDown: function (e) {
12605                 if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
12606
12607                 // Clear the deferred resetState if it hasn't executed yet, otherwise it
12608                 // will interrupt the interaction and orphan a box element in the container.
12609                 this._clearDeferredResetState();
12610                 this._resetState();
12611
12612                 disableTextSelection();
12613                 disableImageDrag();
12614
12615                 this._startPoint = this._map.mouseEventToContainerPoint(e);
12616
12617                 on(document, {
12618                         contextmenu: stop,
12619                         mousemove: this._onMouseMove,
12620                         mouseup: this._onMouseUp,
12621                         keydown: this._onKeyDown
12622                 }, this);
12623         },
12624
12625         _onMouseMove: function (e) {
12626                 if (!this._moved) {
12627                         this._moved = true;
12628
12629                         this._box = create$1('div', 'leaflet-zoom-box', this._container);
12630                         addClass(this._container, 'leaflet-crosshair');
12631
12632                         this._map.fire('boxzoomstart');
12633                 }
12634
12635                 this._point = this._map.mouseEventToContainerPoint(e);
12636
12637                 var bounds = new Bounds(this._point, this._startPoint),
12638                     size = bounds.getSize();
12639
12640                 setPosition(this._box, bounds.min);
12641
12642                 this._box.style.width  = size.x + 'px';
12643                 this._box.style.height = size.y + 'px';
12644         },
12645
12646         _finish: function () {
12647                 if (this._moved) {
12648                         remove(this._box);
12649                         removeClass(this._container, 'leaflet-crosshair');
12650                 }
12651
12652                 enableTextSelection();
12653                 enableImageDrag();
12654
12655                 off(document, {
12656                         contextmenu: stop,
12657                         mousemove: this._onMouseMove,
12658                         mouseup: this._onMouseUp,
12659                         keydown: this._onKeyDown
12660                 }, this);
12661         },
12662
12663         _onMouseUp: function (e) {
12664                 if ((e.which !== 1) && (e.button !== 1)) { return; }
12665
12666                 this._finish();
12667
12668                 if (!this._moved) { return; }
12669                 // Postpone to next JS tick so internal click event handling
12670                 // still see it as "moved".
12671                 this._clearDeferredResetState();
12672                 this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
12673
12674                 var bounds = new LatLngBounds(
12675                         this._map.containerPointToLatLng(this._startPoint),
12676                         this._map.containerPointToLatLng(this._point));
12677
12678                 this._map
12679                         .fitBounds(bounds)
12680                         .fire('boxzoomend', {boxZoomBounds: bounds});
12681         },
12682
12683         _onKeyDown: function (e) {
12684                 if (e.keyCode === 27) {
12685                         this._finish();
12686                 }
12687         }
12688 });
12689
12690 // @section Handlers
12691 // @property boxZoom: Handler
12692 // Box (shift-drag with mouse) zoom handler.
12693 Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
12694
12695 /*
12696  * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
12697  */
12698
12699 // @namespace Map
12700 // @section Interaction Options
12701
12702 Map.mergeOptions({
12703         // @option doubleClickZoom: Boolean|String = true
12704         // Whether the map can be zoomed in by double clicking on it and
12705         // zoomed out by double clicking while holding shift. If passed
12706         // `'center'`, double-click zoom will zoom to the center of the
12707         //  view regardless of where the mouse was.
12708         doubleClickZoom: true
12709 });
12710
12711 var DoubleClickZoom = Handler.extend({
12712         addHooks: function () {
12713                 this._map.on('dblclick', this._onDoubleClick, this);
12714         },
12715
12716         removeHooks: function () {
12717                 this._map.off('dblclick', this._onDoubleClick, this);
12718         },
12719
12720         _onDoubleClick: function (e) {
12721                 var map = this._map,
12722                     oldZoom = map.getZoom(),
12723                     delta = map.options.zoomDelta,
12724                     zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
12725
12726                 if (map.options.doubleClickZoom === 'center') {
12727                         map.setZoom(zoom);
12728                 } else {
12729                         map.setZoomAround(e.containerPoint, zoom);
12730                 }
12731         }
12732 });
12733
12734 // @section Handlers
12735 //
12736 // Map properties include interaction handlers that allow you to control
12737 // interaction behavior in runtime, enabling or disabling certain features such
12738 // as dragging or touch zoom (see `Handler` methods). For example:
12739 //
12740 // ```js
12741 // map.doubleClickZoom.disable();
12742 // ```
12743 //
12744 // @property doubleClickZoom: Handler
12745 // Double click zoom handler.
12746 Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
12747
12748 /*
12749  * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
12750  */
12751
12752 // @namespace Map
12753 // @section Interaction Options
12754 Map.mergeOptions({
12755         // @option dragging: Boolean = true
12756         // Whether the map be draggable with mouse/touch or not.
12757         dragging: true,
12758
12759         // @section Panning Inertia Options
12760         // @option inertia: Boolean = *
12761         // If enabled, panning of the map will have an inertia effect where
12762         // the map builds momentum while dragging and continues moving in
12763         // the same direction for some time. Feels especially nice on touch
12764         // devices. Enabled by default unless running on old Android devices.
12765         inertia: !android23,
12766
12767         // @option inertiaDeceleration: Number = 3000
12768         // The rate with which the inertial movement slows down, in pixels/second².
12769         inertiaDeceleration: 3400, // px/s^2
12770
12771         // @option inertiaMaxSpeed: Number = Infinity
12772         // Max speed of the inertial movement, in pixels/second.
12773         inertiaMaxSpeed: Infinity, // px/s
12774
12775         // @option easeLinearity: Number = 0.2
12776         easeLinearity: 0.2,
12777
12778         // TODO refactor, move to CRS
12779         // @option worldCopyJump: Boolean = false
12780         // With this option enabled, the map tracks when you pan to another "copy"
12781         // of the world and seamlessly jumps to the original one so that all overlays
12782         // like markers and vector layers are still visible.
12783         worldCopyJump: false,
12784
12785         // @option maxBoundsViscosity: Number = 0.0
12786         // If `maxBounds` is set, this option will control how solid the bounds
12787         // are when dragging the map around. The default value of `0.0` allows the
12788         // user to drag outside the bounds at normal speed, higher values will
12789         // slow down map dragging outside bounds, and `1.0` makes the bounds fully
12790         // solid, preventing the user from dragging outside the bounds.
12791         maxBoundsViscosity: 0.0
12792 });
12793
12794 var Drag = Handler.extend({
12795         addHooks: function () {
12796                 if (!this._draggable) {
12797                         var map = this._map;
12798
12799                         this._draggable = new Draggable(map._mapPane, map._container);
12800
12801                         this._draggable.on({
12802                                 dragstart: this._onDragStart,
12803                                 drag: this._onDrag,
12804                                 dragend: this._onDragEnd
12805                         }, this);
12806
12807                         this._draggable.on('predrag', this._onPreDragLimit, this);
12808                         if (map.options.worldCopyJump) {
12809                                 this._draggable.on('predrag', this._onPreDragWrap, this);
12810                                 map.on('zoomend', this._onZoomEnd, this);
12811
12812                                 map.whenReady(this._onZoomEnd, this);
12813                         }
12814                 }
12815                 addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
12816                 this._draggable.enable();
12817                 this._positions = [];
12818                 this._times = [];
12819         },
12820
12821         removeHooks: function () {
12822                 removeClass(this._map._container, 'leaflet-grab');
12823                 removeClass(this._map._container, 'leaflet-touch-drag');
12824                 this._draggable.disable();
12825         },
12826
12827         moved: function () {
12828                 return this._draggable && this._draggable._moved;
12829         },
12830
12831         moving: function () {
12832                 return this._draggable && this._draggable._moving;
12833         },
12834
12835         _onDragStart: function () {
12836                 var map = this._map;
12837
12838                 map._stop();
12839                 if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
12840                         var bounds = toLatLngBounds(this._map.options.maxBounds);
12841
12842                         this._offsetLimit = toBounds(
12843                                 this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
12844                                 this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
12845                                         .add(this._map.getSize()));
12846
12847                         this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
12848                 } else {
12849                         this._offsetLimit = null;
12850                 }
12851
12852                 map
12853                     .fire('movestart')
12854                     .fire('dragstart');
12855
12856                 if (map.options.inertia) {
12857                         this._positions = [];
12858                         this._times = [];
12859                 }
12860         },
12861
12862         _onDrag: function (e) {
12863                 if (this._map.options.inertia) {
12864                         var time = this._lastTime = +new Date(),
12865                             pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
12866
12867                         this._positions.push(pos);
12868                         this._times.push(time);
12869
12870                         if (time - this._times[0] > 50) {
12871                                 this._positions.shift();
12872                                 this._times.shift();
12873                         }
12874                 }
12875
12876                 this._map
12877                     .fire('move', e)
12878                     .fire('drag', e);
12879         },
12880
12881         _onZoomEnd: function () {
12882                 var pxCenter = this._map.getSize().divideBy(2),
12883                     pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
12884
12885                 this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
12886                 this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
12887         },
12888
12889         _viscousLimit: function (value, threshold) {
12890                 return value - (value - threshold) * this._viscosity;
12891         },
12892
12893         _onPreDragLimit: function () {
12894                 if (!this._viscosity || !this._offsetLimit) { return; }
12895
12896                 var offset = this._draggable._newPos.subtract(this._draggable._startPos);
12897
12898                 var limit = this._offsetLimit;
12899                 if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
12900                 if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
12901                 if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
12902                 if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
12903
12904                 this._draggable._newPos = this._draggable._startPos.add(offset);
12905         },
12906
12907         _onPreDragWrap: function () {
12908                 // TODO refactor to be able to adjust map pane position after zoom
12909                 var worldWidth = this._worldWidth,
12910                     halfWidth = Math.round(worldWidth / 2),
12911                     dx = this._initialWorldOffset,
12912                     x = this._draggable._newPos.x,
12913                     newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
12914                     newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
12915                     newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
12916
12917                 this._draggable._absPos = this._draggable._newPos.clone();
12918                 this._draggable._newPos.x = newX;
12919         },
12920
12921         _onDragEnd: function (e) {
12922                 var map = this._map,
12923                     options = map.options,
12924
12925                     noInertia = !options.inertia || this._times.length < 2;
12926
12927                 map.fire('dragend', e);
12928
12929                 if (noInertia) {
12930                         map.fire('moveend');
12931
12932                 } else {
12933
12934                         var direction = this._lastPos.subtract(this._positions[0]),
12935                             duration = (this._lastTime - this._times[0]) / 1000,
12936                             ease = options.easeLinearity,
12937
12938                             speedVector = direction.multiplyBy(ease / duration),
12939                             speed = speedVector.distanceTo([0, 0]),
12940
12941                             limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
12942                             limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
12943
12944                             decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
12945                             offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
12946
12947                         if (!offset.x && !offset.y) {
12948                                 map.fire('moveend');
12949
12950                         } else {
12951                                 offset = map._limitOffset(offset, map.options.maxBounds);
12952
12953                                 requestAnimFrame(function () {
12954                                         map.panBy(offset, {
12955                                                 duration: decelerationDuration,
12956                                                 easeLinearity: ease,
12957                                                 noMoveStart: true,
12958                                                 animate: true
12959                                         });
12960                                 });
12961                         }
12962                 }
12963         }
12964 });
12965
12966 // @section Handlers
12967 // @property dragging: Handler
12968 // Map dragging handler (by both mouse and touch).
12969 Map.addInitHook('addHandler', 'dragging', Drag);
12970
12971 /*
12972  * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
12973  */
12974
12975 // @namespace Map
12976 // @section Keyboard Navigation Options
12977 Map.mergeOptions({
12978         // @option keyboard: Boolean = true
12979         // Makes the map focusable and allows users to navigate the map with keyboard
12980         // arrows and `+`/`-` keys.
12981         keyboard: true,
12982
12983         // @option keyboardPanDelta: Number = 80
12984         // Amount of pixels to pan when pressing an arrow key.
12985         keyboardPanDelta: 80
12986 });
12987
12988 var Keyboard = Handler.extend({
12989
12990         keyCodes: {
12991                 left:    [37],
12992                 right:   [39],
12993                 down:    [40],
12994                 up:      [38],
12995                 zoomIn:  [187, 107, 61, 171],
12996                 zoomOut: [189, 109, 54, 173]
12997         },
12998
12999         initialize: function (map) {
13000                 this._map = map;
13001
13002                 this._setPanDelta(map.options.keyboardPanDelta);
13003                 this._setZoomDelta(map.options.zoomDelta);
13004         },
13005
13006         addHooks: function () {
13007                 var container = this._map._container;
13008
13009                 // make the container focusable by tabbing
13010                 if (container.tabIndex <= 0) {
13011                         container.tabIndex = '0';
13012                 }
13013
13014                 on(container, {
13015                         focus: this._onFocus,
13016                         blur: this._onBlur,
13017                         mousedown: this._onMouseDown
13018                 }, this);
13019
13020                 this._map.on({
13021                         focus: this._addHooks,
13022                         blur: this._removeHooks
13023                 }, this);
13024         },
13025
13026         removeHooks: function () {
13027                 this._removeHooks();
13028
13029                 off(this._map._container, {
13030                         focus: this._onFocus,
13031                         blur: this._onBlur,
13032                         mousedown: this._onMouseDown
13033                 }, this);
13034
13035                 this._map.off({
13036                         focus: this._addHooks,
13037                         blur: this._removeHooks
13038                 }, this);
13039         },
13040
13041         _onMouseDown: function () {
13042                 if (this._focused) { return; }
13043
13044                 var body = document.body,
13045                     docEl = document.documentElement,
13046                     top = body.scrollTop || docEl.scrollTop,
13047                     left = body.scrollLeft || docEl.scrollLeft;
13048
13049                 this._map._container.focus();
13050
13051                 window.scrollTo(left, top);
13052         },
13053
13054         _onFocus: function () {
13055                 this._focused = true;
13056                 this._map.fire('focus');
13057         },
13058
13059         _onBlur: function () {
13060                 this._focused = false;
13061                 this._map.fire('blur');
13062         },
13063
13064         _setPanDelta: function (panDelta) {
13065                 var keys = this._panKeys = {},
13066                     codes = this.keyCodes,
13067                     i, len;
13068
13069                 for (i = 0, len = codes.left.length; i < len; i++) {
13070                         keys[codes.left[i]] = [-1 * panDelta, 0];
13071                 }
13072                 for (i = 0, len = codes.right.length; i < len; i++) {
13073                         keys[codes.right[i]] = [panDelta, 0];
13074                 }
13075                 for (i = 0, len = codes.down.length; i < len; i++) {
13076                         keys[codes.down[i]] = [0, panDelta];
13077                 }
13078                 for (i = 0, len = codes.up.length; i < len; i++) {
13079                         keys[codes.up[i]] = [0, -1 * panDelta];
13080                 }
13081         },
13082
13083         _setZoomDelta: function (zoomDelta) {
13084                 var keys = this._zoomKeys = {},
13085                     codes = this.keyCodes,
13086                     i, len;
13087
13088                 for (i = 0, len = codes.zoomIn.length; i < len; i++) {
13089                         keys[codes.zoomIn[i]] = zoomDelta;
13090                 }
13091                 for (i = 0, len = codes.zoomOut.length; i < len; i++) {
13092                         keys[codes.zoomOut[i]] = -zoomDelta;
13093                 }
13094         },
13095
13096         _addHooks: function () {
13097                 on(document, 'keydown', this._onKeyDown, this);
13098         },
13099
13100         _removeHooks: function () {
13101                 off(document, 'keydown', this._onKeyDown, this);
13102         },
13103
13104         _onKeyDown: function (e) {
13105                 if (e.altKey || e.ctrlKey || e.metaKey) { return; }
13106
13107                 var key = e.keyCode,
13108                     map = this._map,
13109                     offset;
13110
13111                 if (key in this._panKeys) {
13112
13113                         if (map._panAnim && map._panAnim._inProgress) { return; }
13114
13115                         offset = this._panKeys[key];
13116                         if (e.shiftKey) {
13117                                 offset = toPoint(offset).multiplyBy(3);
13118                         }
13119
13120                         map.panBy(offset);
13121
13122                         if (map.options.maxBounds) {
13123                                 map.panInsideBounds(map.options.maxBounds);
13124                         }
13125
13126                 } else if (key in this._zoomKeys) {
13127                         map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
13128
13129                 } else if (key === 27 && map._popup) {
13130                         map.closePopup();
13131
13132                 } else {
13133                         return;
13134                 }
13135
13136                 stop(e);
13137         }
13138 });
13139
13140 // @section Handlers
13141 // @section Handlers
13142 // @property keyboard: Handler
13143 // Keyboard navigation handler.
13144 Map.addInitHook('addHandler', 'keyboard', Keyboard);
13145
13146 /*
13147  * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
13148  */
13149
13150 // @namespace Map
13151 // @section Interaction Options
13152 Map.mergeOptions({
13153         // @section Mousewheel options
13154         // @option scrollWheelZoom: Boolean|String = true
13155         // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
13156         // it will zoom to the center of the view regardless of where the mouse was.
13157         scrollWheelZoom: true,
13158
13159         // @option wheelDebounceTime: Number = 40
13160         // Limits the rate at which a wheel can fire (in milliseconds). By default
13161         // user can't zoom via wheel more often than once per 40 ms.
13162         wheelDebounceTime: 40,
13163
13164         // @option wheelPxPerZoomLevel: Number = 60
13165         // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
13166         // mean a change of one full zoom level. Smaller values will make wheel-zooming
13167         // faster (and vice versa).
13168         wheelPxPerZoomLevel: 60
13169 });
13170
13171 var ScrollWheelZoom = Handler.extend({
13172         addHooks: function () {
13173                 on(this._map._container, 'mousewheel', this._onWheelScroll, this);
13174
13175                 this._delta = 0;
13176         },
13177
13178         removeHooks: function () {
13179                 off(this._map._container, 'mousewheel', this._onWheelScroll, this);
13180         },
13181
13182         _onWheelScroll: function (e) {
13183                 var delta = getWheelDelta(e);
13184
13185                 var debounce = this._map.options.wheelDebounceTime;
13186
13187                 this._delta += delta;
13188                 this._lastMousePos = this._map.mouseEventToContainerPoint(e);
13189
13190                 if (!this._startTime) {
13191                         this._startTime = +new Date();
13192                 }
13193
13194                 var left = Math.max(debounce - (+new Date() - this._startTime), 0);
13195
13196                 clearTimeout(this._timer);
13197                 this._timer = setTimeout(bind(this._performZoom, this), left);
13198
13199                 stop(e);
13200         },
13201
13202         _performZoom: function () {
13203                 var map = this._map,
13204                     zoom = map.getZoom(),
13205                     snap = this._map.options.zoomSnap || 0;
13206
13207                 map._stop(); // stop panning and fly animations if any
13208
13209                 // map the delta with a sigmoid function to -4..4 range leaning on -1..1
13210                 var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
13211                     d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
13212                     d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
13213                     delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
13214
13215                 this._delta = 0;
13216                 this._startTime = null;
13217
13218                 if (!delta) { return; }
13219
13220                 if (map.options.scrollWheelZoom === 'center') {
13221                         map.setZoom(zoom + delta);
13222                 } else {
13223                         map.setZoomAround(this._lastMousePos, zoom + delta);
13224                 }
13225         }
13226 });
13227
13228 // @section Handlers
13229 // @property scrollWheelZoom: Handler
13230 // Scroll wheel zoom handler.
13231 Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
13232
13233 /*
13234  * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
13235  */
13236
13237 // @namespace Map
13238 // @section Interaction Options
13239 Map.mergeOptions({
13240         // @section Touch interaction options
13241         // @option tap: Boolean = true
13242         // Enables mobile hacks for supporting instant taps (fixing 200ms click
13243         // delay on iOS/Android) and touch holds (fired as `contextmenu` events).
13244         tap: true,
13245
13246         // @option tapTolerance: Number = 15
13247         // The max number of pixels a user can shift his finger during touch
13248         // for it to be considered a valid tap.
13249         tapTolerance: 15
13250 });
13251
13252 var Tap = Handler.extend({
13253         addHooks: function () {
13254                 on(this._map._container, 'touchstart', this._onDown, this);
13255         },
13256
13257         removeHooks: function () {
13258                 off(this._map._container, 'touchstart', this._onDown, this);
13259         },
13260
13261         _onDown: function (e) {
13262                 if (!e.touches) { return; }
13263
13264                 preventDefault(e);
13265
13266                 this._fireClick = true;
13267
13268                 // don't simulate click or track longpress if more than 1 touch
13269                 if (e.touches.length > 1) {
13270                         this._fireClick = false;
13271                         clearTimeout(this._holdTimeout);
13272                         return;
13273                 }
13274
13275                 var first = e.touches[0],
13276                     el = first.target;
13277
13278                 this._startPos = this._newPos = new Point(first.clientX, first.clientY);
13279
13280                 // if touching a link, highlight it
13281                 if (el.tagName && el.tagName.toLowerCase() === 'a') {
13282                         addClass(el, 'leaflet-active');
13283                 }
13284
13285                 // simulate long hold but setting a timeout
13286                 this._holdTimeout = setTimeout(bind(function () {
13287                         if (this._isTapValid()) {
13288                                 this._fireClick = false;
13289                                 this._onUp();
13290                                 this._simulateEvent('contextmenu', first);
13291                         }
13292                 }, this), 1000);
13293
13294                 this._simulateEvent('mousedown', first);
13295
13296                 on(document, {
13297                         touchmove: this._onMove,
13298                         touchend: this._onUp
13299                 }, this);
13300         },
13301
13302         _onUp: function (e) {
13303                 clearTimeout(this._holdTimeout);
13304
13305                 off(document, {
13306                         touchmove: this._onMove,
13307                         touchend: this._onUp
13308                 }, this);
13309
13310                 if (this._fireClick && e && e.changedTouches) {
13311
13312                         var first = e.changedTouches[0],
13313                             el = first.target;
13314
13315                         if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
13316                                 removeClass(el, 'leaflet-active');
13317                         }
13318
13319                         this._simulateEvent('mouseup', first);
13320
13321                         // simulate click if the touch didn't move too much
13322                         if (this._isTapValid()) {
13323                                 this._simulateEvent('click', first);
13324                         }
13325                 }
13326         },
13327
13328         _isTapValid: function () {
13329                 return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
13330         },
13331
13332         _onMove: function (e) {
13333                 var first = e.touches[0];
13334                 this._newPos = new Point(first.clientX, first.clientY);
13335                 this._simulateEvent('mousemove', first);
13336         },
13337
13338         _simulateEvent: function (type, e) {
13339                 var simulatedEvent = document.createEvent('MouseEvents');
13340
13341                 simulatedEvent._simulated = true;
13342                 e.target._simulatedClick = true;
13343
13344                 simulatedEvent.initMouseEvent(
13345                         type, true, true, window, 1,
13346                         e.screenX, e.screenY,
13347                         e.clientX, e.clientY,
13348                         false, false, false, false, 0, null);
13349
13350                 e.target.dispatchEvent(simulatedEvent);
13351         }
13352 });
13353
13354 // @section Handlers
13355 // @property tap: Handler
13356 // Mobile touch hacks (quick tap and touch hold) handler.
13357 if (touch && !pointer) {
13358         Map.addInitHook('addHandler', 'tap', Tap);
13359 }
13360
13361 /*
13362  * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
13363  */
13364
13365 // @namespace Map
13366 // @section Interaction Options
13367 Map.mergeOptions({
13368         // @section Touch interaction options
13369         // @option touchZoom: Boolean|String = *
13370         // Whether the map can be zoomed by touch-dragging with two fingers. If
13371         // passed `'center'`, it will zoom to the center of the view regardless of
13372         // where the touch events (fingers) were. Enabled for touch-capable web
13373         // browsers except for old Androids.
13374         touchZoom: touch && !android23,
13375
13376         // @option bounceAtZoomLimits: Boolean = true
13377         // Set it to false if you don't want the map to zoom beyond min/max zoom
13378         // and then bounce back when pinch-zooming.
13379         bounceAtZoomLimits: true
13380 });
13381
13382 var TouchZoom = Handler.extend({
13383         addHooks: function () {
13384                 addClass(this._map._container, 'leaflet-touch-zoom');
13385                 on(this._map._container, 'touchstart', this._onTouchStart, this);
13386         },
13387
13388         removeHooks: function () {
13389                 removeClass(this._map._container, 'leaflet-touch-zoom');
13390                 off(this._map._container, 'touchstart', this._onTouchStart, this);
13391         },
13392
13393         _onTouchStart: function (e) {
13394                 var map = this._map;
13395                 if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
13396
13397                 var p1 = map.mouseEventToContainerPoint(e.touches[0]),
13398                     p2 = map.mouseEventToContainerPoint(e.touches[1]);
13399
13400                 this._centerPoint = map.getSize()._divideBy(2);
13401                 this._startLatLng = map.containerPointToLatLng(this._centerPoint);
13402                 if (map.options.touchZoom !== 'center') {
13403                         this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
13404                 }
13405
13406                 this._startDist = p1.distanceTo(p2);
13407                 this._startZoom = map.getZoom();
13408
13409                 this._moved = false;
13410                 this._zooming = true;
13411
13412                 map._stop();
13413
13414                 on(document, 'touchmove', this._onTouchMove, this);
13415                 on(document, 'touchend', this._onTouchEnd, this);
13416
13417                 preventDefault(e);
13418         },
13419
13420         _onTouchMove: function (e) {
13421                 if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
13422
13423                 var map = this._map,
13424                     p1 = map.mouseEventToContainerPoint(e.touches[0]),
13425                     p2 = map.mouseEventToContainerPoint(e.touches[1]),
13426                     scale = p1.distanceTo(p2) / this._startDist;
13427
13428                 this._zoom = map.getScaleZoom(scale, this._startZoom);
13429
13430                 if (!map.options.bounceAtZoomLimits && (
13431                         (this._zoom < map.getMinZoom() && scale < 1) ||
13432                         (this._zoom > map.getMaxZoom() && scale > 1))) {
13433                         this._zoom = map._limitZoom(this._zoom);
13434                 }
13435
13436                 if (map.options.touchZoom === 'center') {
13437                         this._center = this._startLatLng;
13438                         if (scale === 1) { return; }
13439                 } else {
13440                         // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
13441                         var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
13442                         if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
13443                         this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
13444                 }
13445
13446                 if (!this._moved) {
13447                         map._moveStart(true);
13448                         this._moved = true;
13449                 }
13450
13451                 cancelAnimFrame(this._animRequest);
13452
13453                 var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
13454                 this._animRequest = requestAnimFrame(moveFn, this, true);
13455
13456                 preventDefault(e);
13457         },
13458
13459         _onTouchEnd: function () {
13460                 if (!this._moved || !this._zooming) {
13461                         this._zooming = false;
13462                         return;
13463                 }
13464
13465                 this._zooming = false;
13466                 cancelAnimFrame(this._animRequest);
13467
13468                 off(document, 'touchmove', this._onTouchMove);
13469                 off(document, 'touchend', this._onTouchEnd);
13470
13471                 // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
13472                 if (this._map.options.zoomAnimation) {
13473                         this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
13474                 } else {
13475                         this._map._resetView(this._center, this._map._limitZoom(this._zoom));
13476                 }
13477         }
13478 });
13479
13480 // @section Handlers
13481 // @property touchZoom: Handler
13482 // Touch zoom handler.
13483 Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
13484
13485 Map.BoxZoom = BoxZoom;
13486 Map.DoubleClickZoom = DoubleClickZoom;
13487 Map.Drag = Drag;
13488 Map.Keyboard = Keyboard;
13489 Map.ScrollWheelZoom = ScrollWheelZoom;
13490 Map.Tap = Tap;
13491 Map.TouchZoom = TouchZoom;
13492
13493 // misc
13494
13495 var oldL = window.L;
13496 function noConflict() {
13497         window.L = oldL;
13498         return this;
13499 }
13500
13501 // Always export us to window global (see #2364)
13502 window.L = exports;
13503
13504 exports.version = version;
13505 exports.noConflict = noConflict;
13506 exports.Control = Control;
13507 exports.control = control;
13508 exports.Browser = Browser;
13509 exports.Evented = Evented;
13510 exports.Mixin = Mixin;
13511 exports.Util = Util;
13512 exports.Class = Class;
13513 exports.Handler = Handler;
13514 exports.extend = extend;
13515 exports.bind = bind;
13516 exports.stamp = stamp;
13517 exports.setOptions = setOptions;
13518 exports.DomEvent = DomEvent;
13519 exports.DomUtil = DomUtil;
13520 exports.PosAnimation = PosAnimation;
13521 exports.Draggable = Draggable;
13522 exports.LineUtil = LineUtil;
13523 exports.PolyUtil = PolyUtil;
13524 exports.Point = Point;
13525 exports.point = toPoint;
13526 exports.Bounds = Bounds;
13527 exports.bounds = toBounds;
13528 exports.Transformation = Transformation;
13529 exports.transformation = toTransformation;
13530 exports.Projection = index;
13531 exports.LatLng = LatLng;
13532 exports.latLng = toLatLng;
13533 exports.LatLngBounds = LatLngBounds;
13534 exports.latLngBounds = toLatLngBounds;
13535 exports.CRS = CRS;
13536 exports.GeoJSON = GeoJSON;
13537 exports.geoJSON = geoJSON;
13538 exports.geoJson = geoJson;
13539 exports.Layer = Layer;
13540 exports.LayerGroup = LayerGroup;
13541 exports.layerGroup = layerGroup;
13542 exports.FeatureGroup = FeatureGroup;
13543 exports.featureGroup = featureGroup;
13544 exports.ImageOverlay = ImageOverlay;
13545 exports.imageOverlay = imageOverlay;
13546 exports.VideoOverlay = VideoOverlay;
13547 exports.videoOverlay = videoOverlay;
13548 exports.DivOverlay = DivOverlay;
13549 exports.Popup = Popup;
13550 exports.popup = popup;
13551 exports.Tooltip = Tooltip;
13552 exports.tooltip = tooltip;
13553 exports.Icon = Icon;
13554 exports.icon = icon;
13555 exports.DivIcon = DivIcon;
13556 exports.divIcon = divIcon;
13557 exports.Marker = Marker;
13558 exports.marker = marker;
13559 exports.TileLayer = TileLayer;
13560 exports.tileLayer = tileLayer;
13561 exports.GridLayer = GridLayer;
13562 exports.gridLayer = gridLayer;
13563 exports.SVG = SVG;
13564 exports.svg = svg$1;
13565 exports.Renderer = Renderer;
13566 exports.Canvas = Canvas;
13567 exports.canvas = canvas$1;
13568 exports.Path = Path;
13569 exports.CircleMarker = CircleMarker;
13570 exports.circleMarker = circleMarker;
13571 exports.Circle = Circle;
13572 exports.circle = circle;
13573 exports.Polyline = Polyline;
13574 exports.polyline = polyline;
13575 exports.Polygon = Polygon;
13576 exports.polygon = polygon;
13577 exports.Rectangle = Rectangle;
13578 exports.rectangle = rectangle;
13579 exports.Map = Map;
13580 exports.map = createMap;
13581
13582 })));
13583 //# sourceMappingURL=leaflet-src.js.map