+// @section There is static function which can be called without instantiating L.Handler:
+// @function addTo(map: Map, name: String): this
+// Adds a new Handler to the given map with the given name.
+Handler.addTo = function (map, name) {
+ map.addHandler(name, this);
+ return this;
+};
+
+var Mixin = {Events: Events};
+
+/*
+ * @class Draggable
+ * @aka L.Draggable
+ * @inherits Evented
+ *
+ * A class for making DOM elements draggable (including touch support).
+ * Used internally for map and marker dragging. Only works for elements
+ * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
+ *
+ * @example
+ * ```js
+ * var draggable = new L.Draggable(elementToDrag);
+ * draggable.enable();
+ * ```
+ */
+
+var START = touch ? 'touchstart mousedown' : 'mousedown';
+var END = {
+ mousedown: 'mouseup',
+ touchstart: 'touchend',
+ pointerdown: 'touchend',
+ MSPointerDown: 'touchend'
+};
+var MOVE = {
+ mousedown: 'mousemove',
+ touchstart: 'touchmove',
+ pointerdown: 'touchmove',
+ MSPointerDown: 'touchmove'
+};
+
+
+var Draggable = Evented.extend({
+
+ options: {
+ // @section
+ // @aka Draggable options
+ // @option clickTolerance: Number = 3
+ // The max number of pixels a user can shift the mouse pointer during a click
+ // for it to be considered a valid click (as opposed to a mouse drag).
+ clickTolerance: 3
+ },
+
+ // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
+ // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
+ initialize: function (element, dragStartTarget, preventOutline$$1, options) {
+ setOptions(this, options);
+
+ this._element = element;
+ this._dragStartTarget = dragStartTarget || element;
+ this._preventOutline = preventOutline$$1;
+ },
+
+ // @method enable()
+ // Enables the dragging ability
+ enable: function () {
+ if (this._enabled) { return; }
+
+ on(this._dragStartTarget, START, this._onDown, this);
+
+ this._enabled = true;
+ },
+
+ // @method disable()
+ // Disables the dragging ability
+ disable: function () {
+ if (!this._enabled) { return; }
+
+ // If we're currently dragging this draggable,
+ // disabling it counts as first ending the drag.
+ if (Draggable._dragging === this) {
+ this.finishDrag();
+ }
+
+ off(this._dragStartTarget, START, this._onDown, this);
+
+ this._enabled = false;
+ this._moved = false;
+ },
+
+ _onDown: function (e) {
+ // Ignore simulated events, since we handle both touch and
+ // mouse explicitly; otherwise we risk getting duplicates of
+ // touch events, see #4315.
+ // Also ignore the event if disabled; this happens in IE11
+ // under some circumstances, see #3666.
+ if (e._simulated || !this._enabled) { return; }
+
+ this._moved = false;
+
+ if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
+
+ if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
+ Draggable._dragging = this; // Prevent dragging multiple objects at once.
+
+ if (this._preventOutline) {
+ preventOutline(this._element);
+ }
+
+ disableImageDrag();
+ disableTextSelection();
+
+ if (this._moving) { return; }
+
+ // @event down: Event
+ // Fired when a drag is about to start.
+ this.fire('down');
+
+ var first = e.touches ? e.touches[0] : e,
+ sizedParent = getSizedParentNode(this._element);
+
+ this._startPoint = new Point(first.clientX, first.clientY);
+
+ // Cache the scale, so that we can continuously compensate for it during drag (_onMove).
+ this._parentScale = getScale(sizedParent);
+
+ on(document, MOVE[e.type], this._onMove, this);
+ on(document, END[e.type], this._onUp, this);
+ },
+
+ _onMove: function (e) {
+ // Ignore simulated events, since we handle both touch and
+ // mouse explicitly; otherwise we risk getting duplicates of
+ // touch events, see #4315.
+ // Also ignore the event if disabled; this happens in IE11
+ // under some circumstances, see #3666.
+ if (e._simulated || !this._enabled) { return; }
+
+ if (e.touches && e.touches.length > 1) {
+ this._moved = true;
+ return;
+ }
+
+ var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
+ offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint);
+
+ if (!offset.x && !offset.y) { return; }
+ if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
+
+ // We assume that the parent container's position, border and scale do not change for the duration of the drag.
+ // Therefore there is no need to account for the position and border (they are eliminated by the subtraction)
+ // and we can use the cached value for the scale.
+ offset.x /= this._parentScale.x;
+ offset.y /= this._parentScale.y;
+
+ preventDefault(e);
+
+ if (!this._moved) {
+ // @event dragstart: Event
+ // Fired when a drag starts
+ this.fire('dragstart');
+
+ this._moved = true;
+ this._startPos = getPosition(this._element).subtract(offset);
+
+ addClass(document.body, 'leaflet-dragging');
+
+ this._lastTarget = e.target || e.srcElement;
+ // IE and Edge do not give the <use> element, so fetch it
+ // if necessary
+ if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
+ this._lastTarget = this._lastTarget.correspondingUseElement;
+ }
+ addClass(this._lastTarget, 'leaflet-drag-target');
+ }
+
+ this._newPos = this._startPos.add(offset);
+ this._moving = true;
+
+ cancelAnimFrame(this._animRequest);
+ this._lastEvent = e;
+ this._animRequest = requestAnimFrame(this._updatePosition, this, true);
+ },
+
+ _updatePosition: function () {
+ var e = {originalEvent: this._lastEvent};
+
+ // @event predrag: Event
+ // Fired continuously during dragging *before* each corresponding
+ // update of the element's position.
+ this.fire('predrag', e);
+ setPosition(this._element, this._newPos);
+
+ // @event drag: Event
+ // Fired continuously during dragging.
+ this.fire('drag', e);
+ },
+
+ _onUp: function (e) {
+ // Ignore simulated events, since we handle both touch and
+ // mouse explicitly; otherwise we risk getting duplicates of
+ // touch events, see #4315.
+ // Also ignore the event if disabled; this happens in IE11
+ // under some circumstances, see #3666.
+ if (e._simulated || !this._enabled) { return; }
+ this.finishDrag();
+ },
+
+ finishDrag: function () {
+ removeClass(document.body, 'leaflet-dragging');
+
+ if (this._lastTarget) {
+ removeClass(this._lastTarget, 'leaflet-drag-target');
+ this._lastTarget = null;
+ }
+
+ for (var i in MOVE) {
+ off(document, MOVE[i], this._onMove, this);
+ off(document, END[i], this._onUp, this);
+ }
+
+ enableImageDrag();
+ enableTextSelection();
+
+ if (this._moved && this._moving) {
+ // ensure drag is not fired after dragend
+ cancelAnimFrame(this._animRequest);
+
+ // @event dragend: DragEndEvent
+ // Fired when the drag ends.
+ this.fire('dragend', {
+ distance: this._newPos.distanceTo(this._startPos)
+ });
+ }
+
+ this._moving = false;
+ Draggable._dragging = false;
+ }
+
+});
+
+/*
+ * @namespace LineUtil
+ *
+ * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast.
+ */
+
+// Simplify polyline with vertex reduction and Douglas-Peucker simplification.
+// Improves rendering performance dramatically by lessening the number of points to draw.
+
+// @function simplify(points: Point[], tolerance: Number): Point[]
+// Dramatically reduces the number of points in a polyline while retaining
+// its shape and returns a new array of simplified points, using the
+// [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
+// Used for a huge performance boost when processing/displaying Leaflet polylines for
+// each zoom level and also reducing visual noise. tolerance affects the amount of
+// simplification (lesser value means higher quality but slower and with more points).
+// Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
+function simplify(points, tolerance) {
+ if (!tolerance || !points.length) {
+ return points.slice();
+ }
+
+ var sqTolerance = tolerance * tolerance;
+
+ // stage 1: vertex reduction
+ points = _reducePoints(points, sqTolerance);
+
+ // stage 2: Douglas-Peucker simplification
+ points = _simplifyDP(points, sqTolerance);
+
+ return points;
+}
+
+// @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
+// Returns the distance between point `p` and segment `p1` to `p2`.
+function pointToSegmentDistance(p, p1, p2) {
+ return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
+}
+
+// @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
+// Returns the closest point from a point `p` on a segment `p1` to `p2`.
+function closestPointOnSegment(p, p1, p2) {
+ return _sqClosestPointOnSegment(p, p1, p2);
+}
+
+// Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
+function _simplifyDP(points, sqTolerance) {
+
+ var len = points.length,
+ ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
+ markers = new ArrayConstructor(len);
+
+ markers[0] = markers[len - 1] = 1;
+
+ _simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
+
+ var i,
+ newPoints = [];
+
+ for (i = 0; i < len; i++) {
+ if (markers[i]) {
+ newPoints.push(points[i]);
+ }
+ }
+
+ return newPoints;
+}
+
+function _simplifyDPStep(points, markers, sqTolerance, first, last) {
+
+ var maxSqDist = 0,
+ index, i, sqDist;
+
+ for (i = first + 1; i <= last - 1; i++) {
+ sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
+
+ if (sqDist > maxSqDist) {
+ index = i;
+ maxSqDist = sqDist;
+ }
+ }
+
+ if (maxSqDist > sqTolerance) {
+ markers[index] = 1;
+
+ _simplifyDPStep(points, markers, sqTolerance, first, index);
+ _simplifyDPStep(points, markers, sqTolerance, index, last);
+ }
+}
+
+// reduce points that are too close to each other to a single point
+function _reducePoints(points, sqTolerance) {
+ var reducedPoints = [points[0]];
+
+ for (var i = 1, prev = 0, len = points.length; i < len; i++) {
+ if (_sqDist(points[i], points[prev]) > sqTolerance) {
+ reducedPoints.push(points[i]);
+ prev = i;
+ }
+ }
+ if (prev < len - 1) {
+ reducedPoints.push(points[len - 1]);
+ }
+ return reducedPoints;
+}
+
+var _lastCode;
+
+// @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
+// Clips the segment a to b by rectangular bounds with the
+// [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
+// (modifying the segment points directly!). Used by Leaflet to only show polyline
+// points that are on the screen or near, increasing performance.
+function clipSegment(a, b, bounds, useLastCode, round) {
+ var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
+ codeB = _getBitCode(b, bounds),
+
+ codeOut, p, newCode;
+
+ // save 2nd code to avoid calculating it on the next segment
+ _lastCode = codeB;
+
+ while (true) {
+ // if a,b is inside the clip window (trivial accept)
+ if (!(codeA | codeB)) {
+ return [a, b];
+ }
+
+ // if a,b is outside the clip window (trivial reject)
+ if (codeA & codeB) {
+ return false;
+ }
+
+ // other cases
+ codeOut = codeA || codeB;
+ p = _getEdgeIntersection(a, b, codeOut, bounds, round);
+ newCode = _getBitCode(p, bounds);
+
+ if (codeOut === codeA) {
+ a = p;
+ codeA = newCode;
+ } else {
+ b = p;
+ codeB = newCode;
+ }
+ }
+}
+
+function _getEdgeIntersection(a, b, code, bounds, round) {
+ var dx = b.x - a.x,
+ dy = b.y - a.y,
+ min = bounds.min,
+ max = bounds.max,
+ x, y;
+
+ if (code & 8) { // top
+ x = a.x + dx * (max.y - a.y) / dy;
+ y = max.y;
+
+ } else if (code & 4) { // bottom
+ x = a.x + dx * (min.y - a.y) / dy;
+ y = min.y;
+
+ } else if (code & 2) { // right
+ x = max.x;
+ y = a.y + dy * (max.x - a.x) / dx;
+
+ } else if (code & 1) { // left
+ x = min.x;
+ y = a.y + dy * (min.x - a.x) / dx;
+ }
+
+ return new Point(x, y, round);
+}
+
+function _getBitCode(p, bounds) {
+ var code = 0;
+
+ if (p.x < bounds.min.x) { // left
+ code |= 1;
+ } else if (p.x > bounds.max.x) { // right
+ code |= 2;
+ }
+
+ if (p.y < bounds.min.y) { // bottom
+ code |= 4;
+ } else if (p.y > bounds.max.y) { // top
+ code |= 8;
+ }
+
+ return code;
+}
+
+// square distance (to avoid unnecessary Math.sqrt calls)
+function _sqDist(p1, p2) {
+ var dx = p2.x - p1.x,
+ dy = p2.y - p1.y;
+ return dx * dx + dy * dy;
+}
+
+// return closest point on segment or distance to that point
+function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
+ var x = p1.x,
+ y = p1.y,
+ dx = p2.x - x,
+ dy = p2.y - y,
+ dot = dx * dx + dy * dy,
+ t;
+
+ if (dot > 0) {
+ t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
+
+ if (t > 1) {
+ x = p2.x;
+ y = p2.y;
+ } else if (t > 0) {
+ x += dx * t;
+ y += dy * t;
+ }
+ }
+
+ dx = p.x - x;
+ dy = p.y - y;
+
+ return sqDist ? dx * dx + dy * dy : new Point(x, y);
+}
+
+
+// @function isFlat(latlngs: LatLng[]): Boolean
+// Returns true if `latlngs` is a flat array, false is nested.
+function isFlat(latlngs) {
+ return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
+}
+
+function _flat(latlngs) {
+ console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
+ return isFlat(latlngs);
+}
+
+
+var LineUtil = (Object.freeze || Object)({
+ simplify: simplify,
+ pointToSegmentDistance: pointToSegmentDistance,
+ closestPointOnSegment: closestPointOnSegment,
+ clipSegment: clipSegment,
+ _getEdgeIntersection: _getEdgeIntersection,
+ _getBitCode: _getBitCode,
+ _sqClosestPointOnSegment: _sqClosestPointOnSegment,
+ isFlat: isFlat,
+ _flat: _flat
+});
+
+/*
+ * @namespace PolyUtil
+ * Various utility functions for polygon geometries.
+ */
+
+/* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
+ * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)).
+ * Used by Leaflet to only show polygon points that are on the screen or near, increasing
+ * performance. Note that polygon points needs different algorithm for clipping
+ * than polyline, so there's a separate method for it.
+ */
+function clipPolygon(points, bounds, round) {
+ var clippedPoints,
+ edges = [1, 4, 2, 8],
+ i, j, k,
+ a, b,
+ len, edge, p;
+
+ for (i = 0, len = points.length; i < len; i++) {
+ points[i]._code = _getBitCode(points[i], bounds);
+ }
+
+ // for each edge (left, bottom, right, top)
+ for (k = 0; k < 4; k++) {
+ edge = edges[k];
+ clippedPoints = [];
+
+ for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
+ a = points[i];
+ b = points[j];
+
+ // if a is inside the clip window
+ if (!(a._code & edge)) {
+ // if b is outside the clip window (a->b goes out of screen)
+ if (b._code & edge) {
+ p = _getEdgeIntersection(b, a, edge, bounds, round);
+ p._code = _getBitCode(p, bounds);
+ clippedPoints.push(p);
+ }
+ clippedPoints.push(a);
+
+ // else if b is inside the clip window (a->b enters the screen)
+ } else if (!(b._code & edge)) {
+ p = _getEdgeIntersection(b, a, edge, bounds, round);
+ p._code = _getBitCode(p, bounds);
+ clippedPoints.push(p);
+ }
+ }
+ points = clippedPoints;
+ }
+
+ return points;
+}
+
+
+var PolyUtil = (Object.freeze || Object)({
+ clipPolygon: clipPolygon
+});
+
+/*
+ * @namespace Projection
+ * @section
+ * Leaflet comes with a set of already defined Projections out of the box:
+ *
+ * @projection L.Projection.LonLat
+ *
+ * Equirectangular, or Plate Carree projection — the most simple projection,
+ * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
+ * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
+ * `EPSG:4326` and `Simple` CRS.
+ */
+
+var LonLat = {
+ project: function (latlng) {
+ return new Point(latlng.lng, latlng.lat);
+ },
+
+ unproject: function (point) {
+ return new LatLng(point.y, point.x);
+ },
+
+ bounds: new Bounds([-180, -90], [180, 90])
+};
+
+/*
+ * @namespace Projection
+ * @projection L.Projection.Mercator
+ *
+ * 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.
+ */
+
+var Mercator = {
+ R: 6378137,
+ R_MINOR: 6356752.314245179,
+
+ bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
+
+ project: function (latlng) {
+ var d = Math.PI / 180,
+ r = this.R,
+ y = latlng.lat * d,
+ tmp = this.R_MINOR / r,
+ e = Math.sqrt(1 - tmp * tmp),
+ con = e * Math.sin(y);
+
+ var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
+ y = -r * Math.log(Math.max(ts, 1E-10));
+
+ return new Point(latlng.lng * d * r, y);
+ },
+
+ unproject: function (point) {
+ var d = 180 / Math.PI,
+ r = this.R,
+ tmp = this.R_MINOR / r,
+ e = Math.sqrt(1 - tmp * tmp),
+ ts = Math.exp(-point.y / r),
+ phi = Math.PI / 2 - 2 * Math.atan(ts);
+
+ for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
+ con = e * Math.sin(phi);
+ con = Math.pow((1 - con) / (1 + con), e / 2);
+ dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
+ phi += dphi;
+ }
+
+ return new LatLng(phi * d, point.x * d / r);
+ }
+};
+
+/*
+ * @class Projection
+
+ * An object with methods for projecting geographical coordinates of the world onto
+ * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection).
+
+ * @property bounds: Bounds
+ * The bounds (specified in CRS units) where the projection is valid
+
+ * @method project(latlng: LatLng): Point
+ * Projects geographical coordinates into a 2D point.
+ * Only accepts actual `L.LatLng` instances, not arrays.
+
+ * @method unproject(point: Point): LatLng
+ * The inverse of `project`. Projects a 2D point into a geographical location.
+ * Only accepts actual `L.Point` instances, not arrays.
+
+ * Note that the projection instances do not inherit from Leafet's `Class` object,
+ * and can't be instantiated. Also, new classes can't inherit from them,
+ * and methods can't be added to them with the `include` function.
+
+ */
+
+
+
+
+var index = (Object.freeze || Object)({
+ LonLat: LonLat,
+ Mercator: Mercator,
+ SphericalMercator: SphericalMercator
+});
+
+/*
+ * @namespace CRS
+ * @crs L.CRS.EPSG3395
+ *
+ * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
+ */
+var EPSG3395 = extend({}, Earth, {
+ code: 'EPSG:3395',
+ projection: Mercator,
+
+ transformation: (function () {
+ var scale = 0.5 / (Math.PI * Mercator.R);
+ return toTransformation(scale, 0.5, -scale, 0.5);
+ }())
+});
+
+/*
+ * @namespace CRS
+ * @crs L.CRS.EPSG4326
+ *
+ * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
+ *
+ * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
+ * which is a breaking change from 0.7.x behaviour. If you are using a `TileLayer`
+ * with this CRS, ensure that there are two 256x256 pixel tiles covering the
+ * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
+ * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
+ */
+
+var EPSG4326 = extend({}, Earth, {
+ code: 'EPSG:4326',
+ projection: LonLat,
+ transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
+});
+
+/*
+ * @namespace CRS
+ * @crs L.CRS.Simple
+ *
+ * A simple CRS that maps longitude and latitude into `x` and `y` directly.
+ * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
+ * axis should still be inverted (going from bottom to top). `distance()` returns
+ * simple euclidean distance.
+ */
+
+var Simple = extend({}, CRS, {
+ projection: LonLat,
+ transformation: toTransformation(1, 0, -1, 0),
+
+ scale: function (zoom) {
+ return Math.pow(2, zoom);
+ },
+
+ zoom: function (scale) {
+ return Math.log(scale) / Math.LN2;
+ },
+
+ distance: function (latlng1, latlng2) {
+ var dx = latlng2.lng - latlng1.lng,
+ dy = latlng2.lat - latlng1.lat;
+
+ return Math.sqrt(dx * dx + dy * dy);
+ },
+
+ infinite: true
+});
+
+CRS.Earth = Earth;
+CRS.EPSG3395 = EPSG3395;
+CRS.EPSG3857 = EPSG3857;
+CRS.EPSG900913 = EPSG900913;
+CRS.EPSG4326 = EPSG4326;
+CRS.Simple = Simple;
+
+/*
+ * @class Layer
+ * @inherits Evented
+ * @aka L.Layer
+ * @aka ILayer
+ *
+ * A set of methods from the Layer base class that all Leaflet layers use.
+ * Inherits all methods, options and events from `L.Evented`.
+ *
+ * @example
+ *
+ * ```js
+ * var layer = L.Marker(latlng).addTo(map);
+ * layer.addTo(map);
+ * layer.remove();
+ * ```
+ *
+ * @event add: Event
+ * Fired after the layer is added to a map
+ *
+ * @event remove: Event
+ * Fired after the layer is removed from a map
+ */
+
+
+var Layer = Evented.extend({
+
+ // Classes extending `L.Layer` will inherit the following options:
+ options: {
+ // @option pane: String = 'overlayPane'
+ // 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.
+ pane: 'overlayPane',
+
+ // @option attribution: String = null
+ // String to be shown in the attribution control, e.g. "© OpenStreetMap contributors". It describes the layer data and is often a legal obligation towards copyright holders and tile providers.
+ attribution: null,
+
+ bubblingMouseEvents: true
+ },
+
+ /* @section
+ * Classes extending `L.Layer` will inherit the following methods:
+ *
+ * @method addTo(map: Map|LayerGroup): this
+ * Adds the layer to the given map or layer group.
+ */
+ addTo: function (map) {
+ map.addLayer(this);
+ return this;
+ },
+
+ // @method remove: this
+ // Removes the layer from the map it is currently active on.
+ remove: function () {
+ return this.removeFrom(this._map || this._mapToAdd);
+ },
+
+ // @method removeFrom(map: Map): this
+ // Removes the layer from the given map
+ removeFrom: function (obj) {
+ if (obj) {
+ obj.removeLayer(this);
+ }
+ return this;
+ },
+
+ // @method getPane(name? : String): HTMLElement
+ // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
+ getPane: function (name) {
+ return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
+ },
+
+ addInteractiveTarget: function (targetEl) {
+ this._map._targets[stamp(targetEl)] = this;
+ return this;
+ },
+
+ removeInteractiveTarget: function (targetEl) {
+ delete this._map._targets[stamp(targetEl)];
+ return this;
+ },
+
+ // @method getAttribution: String
+ // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
+ getAttribution: function () {
+ return this.options.attribution;
+ },
+
+ _layerAdd: function (e) {
+ var map = e.target;
+
+ // check in case layer gets added and then removed before the map is ready
+ if (!map.hasLayer(this)) { return; }
+
+ this._map = map;
+ this._zoomAnimated = map._zoomAnimated;
+
+ if (this.getEvents) {
+ var events = this.getEvents();
+ map.on(events, this);
+ this.once('remove', function () {
+ map.off(events, this);
+ }, this);
+ }
+
+ this.onAdd(map);
+
+ if (this.getAttribution && map.attributionControl) {
+ map.attributionControl.addAttribution(this.getAttribution());
+ }
+
+ this.fire('add');
+ map.fire('layeradd', {layer: this});
+ }
+});
+
+/* @section Extension methods
+ * @uninheritable
+ *
+ * Every layer should extend from `L.Layer` and (re-)implement the following methods.
+ *
+ * @method onAdd(map: Map): this
+ * 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).
+ *
+ * @method onRemove(map: Map): this
+ * 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).
+ *
+ * @method getEvents(): Object
+ * 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.
+ *
+ * @method getAttribution(): String
+ * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
+ *
+ * @method beforeAdd(map: Map): this
+ * 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.
+ */
+
+
+/* @namespace Map
+ * @section Layer events
+ *
+ * @event layeradd: LayerEvent
+ * Fired when a new layer is added to the map.
+ *
+ * @event layerremove: LayerEvent
+ * Fired when some layer is removed from the map
+ *
+ * @section Methods for Layers and Controls
+ */
+Map.include({
+ // @method addLayer(layer: Layer): this
+ // Adds the given layer to the map
+ addLayer: function (layer) {
+ if (!layer._layerAdd) {
+ throw new Error('The provided object is not a Layer.');
+ }
+
+ var id = stamp(layer);
+ if (this._layers[id]) { return this; }
+ this._layers[id] = layer;
+
+ layer._mapToAdd = this;
+
+ if (layer.beforeAdd) {
+ layer.beforeAdd(this);
+ }
+
+ this.whenReady(layer._layerAdd, layer);
+
+ return this;
+ },
+
+ // @method removeLayer(layer: Layer): this
+ // Removes the given layer from the map.
+ removeLayer: function (layer) {
+ var id = stamp(layer);
+
+ if (!this._layers[id]) { return this; }
+
+ if (this._loaded) {
+ layer.onRemove(this);
+ }
+
+ if (layer.getAttribution && this.attributionControl) {
+ this.attributionControl.removeAttribution(layer.getAttribution());
+ }
+
+ delete this._layers[id];
+
+ if (this._loaded) {
+ this.fire('layerremove', {layer: layer});
+ layer.fire('remove');
+ }
+
+ layer._map = layer._mapToAdd = null;
+
+ return this;
+ },
+
+ // @method hasLayer(layer: Layer): Boolean
+ // Returns `true` if the given layer is currently added to the map
+ hasLayer: function (layer) {
+ return !!layer && (stamp(layer) in this._layers);
+ },
+
+ /* @method eachLayer(fn: Function, context?: Object): this
+ * Iterates over the layers of the map, optionally specifying context of the iterator function.
+ * ```
+ * map.eachLayer(function(layer){
+ * layer.bindPopup('Hello');
+ * });
+ * ```
+ */
+ eachLayer: function (method, context) {
+ for (var i in this._layers) {
+ method.call(context, this._layers[i]);
+ }
+ return this;
+ },
+
+ _addLayers: function (layers) {
+ layers = layers ? (isArray(layers) ? layers : [layers]) : [];
+
+ for (var i = 0, len = layers.length; i < len; i++) {
+ this.addLayer(layers[i]);
+ }
+ },
+
+ _addZoomLimit: function (layer) {
+ if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
+ this._zoomBoundLayers[stamp(layer)] = layer;
+ this._updateZoomLevels();
+ }
+ },
+
+ _removeZoomLimit: function (layer) {
+ var id = stamp(layer);
+
+ if (this._zoomBoundLayers[id]) {
+ delete this._zoomBoundLayers[id];
+ this._updateZoomLevels();
+ }
+ },
+
+ _updateZoomLevels: function () {
+ var minZoom = Infinity,
+ maxZoom = -Infinity,
+ oldZoomSpan = this._getZoomSpan();
+
+ for (var i in this._zoomBoundLayers) {
+ var options = this._zoomBoundLayers[i].options;
+
+ minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
+ maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
+ }
+
+ this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
+ this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
+
+ // @section Map state change events
+ // @event zoomlevelschange: Event
+ // Fired when the number of zoomlevels on the map is changed due
+ // to adding or removing a layer.
+ if (oldZoomSpan !== this._getZoomSpan()) {
+ this.fire('zoomlevelschange');
+ }
+
+ if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
+ this.setZoom(this._layersMaxZoom);
+ }
+ if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
+ this.setZoom(this._layersMinZoom);
+ }
+ }
+});
+
+/*
+ * @class LayerGroup
+ * @aka L.LayerGroup
+ * @inherits Layer
+ *
+ * Used to group several layers and handle them as one. If you add it to the map,
+ * any layers added or removed from the group will be added/removed on the map as
+ * well. Extends `Layer`.
+ *
+ * @example
+ *
+ * ```js
+ * L.layerGroup([marker1, marker2])
+ * .addLayer(polyline)
+ * .addTo(map);
+ * ```
+ */
+
+var LayerGroup = Layer.extend({
+
+ initialize: function (layers, options) {
+ setOptions(this, options);
+
+ this._layers = {};
+
+ var i, len;
+
+ if (layers) {
+ for (i = 0, len = layers.length; i < len; i++) {
+ this.addLayer(layers[i]);
+ }
+ }
+ },
+
+ // @method addLayer(layer: Layer): this
+ // Adds the given layer to the group.
+ addLayer: function (layer) {
+ var id = this.getLayerId(layer);
+
+ this._layers[id] = layer;
+
+ if (this._map) {
+ this._map.addLayer(layer);
+ }
+
+ return this;
+ },
+
+ // @method removeLayer(layer: Layer): this
+ // Removes the given layer from the group.
+ // @alternative
+ // @method removeLayer(id: Number): this
+ // Removes the layer with the given internal ID from the group.
+ removeLayer: function (layer) {
+ var id = layer in this._layers ? layer : this.getLayerId(layer);
+
+ if (this._map && this._layers[id]) {
+ this._map.removeLayer(this._layers[id]);
+ }
+
+ delete this._layers[id];
+
+ return this;
+ },
+
+ // @method hasLayer(layer: Layer): Boolean
+ // Returns `true` if the given layer is currently added to the group.
+ // @alternative
+ // @method hasLayer(id: Number): Boolean
+ // Returns `true` if the given internal ID is currently added to the group.
+ hasLayer: function (layer) {
+ return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
+ },
+
+ // @method clearLayers(): this
+ // Removes all the layers from the group.
+ clearLayers: function () {
+ return this.eachLayer(this.removeLayer, this);
+ },
+
+ // @method invoke(methodName: String, …): this
+ // Calls `methodName` on every layer contained in this group, passing any
+ // additional parameters. Has no effect if the layers contained do not
+ // implement `methodName`.
+ invoke: function (methodName) {
+ var args = Array.prototype.slice.call(arguments, 1),
+ i, layer;
+
+ for (i in this._layers) {
+ layer = this._layers[i];
+
+ if (layer[methodName]) {
+ layer[methodName].apply(layer, args);
+ }
+ }
+
+ return this;
+ },
+
+ onAdd: function (map) {
+ this.eachLayer(map.addLayer, map);
+ },
+
+ onRemove: function (map) {
+ this.eachLayer(map.removeLayer, map);
+ },
+
+ // @method eachLayer(fn: Function, context?: Object): this
+ // Iterates over the layers of the group, optionally specifying context of the iterator function.
+ // ```js
+ // group.eachLayer(function (layer) {
+ // layer.bindPopup('Hello');
+ // });
+ // ```
+ eachLayer: function (method, context) {
+ for (var i in this._layers) {
+ method.call(context, this._layers[i]);
+ }
+ return this;
+ },
+
+ // @method getLayer(id: Number): Layer
+ // Returns the layer with the given internal ID.
+ getLayer: function (id) {
+ return this._layers[id];
+ },
+
+ // @method getLayers(): Layer[]
+ // Returns an array of all the layers added to the group.
+ getLayers: function () {
+ var layers = [];
+ this.eachLayer(layers.push, layers);
+ return layers;
+ },
+
+ // @method setZIndex(zIndex: Number): this
+ // Calls `setZIndex` on every layer contained in this group, passing the z-index.
+ setZIndex: function (zIndex) {
+ return this.invoke('setZIndex', zIndex);
+ },
+
+ // @method getLayerId(layer: Layer): Number
+ // Returns the internal ID for a layer
+ getLayerId: function (layer) {
+ return stamp(layer);
+ }
+});
+
+
+// @factory L.layerGroup(layers?: Layer[], options?: Object)
+// Create a layer group, optionally given an initial set of layers and an `options` object.
+var layerGroup = function (layers, options) {
+ return new LayerGroup(layers, options);
+};
+
+/*
+ * @class FeatureGroup
+ * @aka L.FeatureGroup
+ * @inherits LayerGroup
+ *
+ * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
+ * * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
+ * * Events are propagated to the `FeatureGroup`, so if the group has an event
+ * handler, it will handle events from any of the layers. This includes mouse events
+ * and custom events.
+ * * Has `layeradd` and `layerremove` events
+ *
+ * @example
+ *
+ * ```js
+ * L.featureGroup([marker1, marker2, polyline])
+ * .bindPopup('Hello world!')
+ * .on('click', function() { alert('Clicked on a member of the group!'); })
+ * .addTo(map);
+ * ```
+ */
+
+var FeatureGroup = LayerGroup.extend({
+
+ addLayer: function (layer) {
+ if (this.hasLayer(layer)) {
+ return this;
+ }
+
+ layer.addEventParent(this);
+
+ LayerGroup.prototype.addLayer.call(this, layer);
+
+ // @event layeradd: LayerEvent
+ // Fired when a layer is added to this `FeatureGroup`
+ return this.fire('layeradd', {layer: layer});
+ },
+
+ removeLayer: function (layer) {
+ if (!this.hasLayer(layer)) {
+ return this;
+ }
+ if (layer in this._layers) {
+ layer = this._layers[layer];
+ }
+
+ layer.removeEventParent(this);
+
+ LayerGroup.prototype.removeLayer.call(this, layer);
+
+ // @event layerremove: LayerEvent
+ // Fired when a layer is removed from this `FeatureGroup`
+ return this.fire('layerremove', {layer: layer});
+ },
+
+ // @method setStyle(style: Path options): this
+ // Sets the given path options to each layer of the group that has a `setStyle` method.
+ setStyle: function (style) {
+ return this.invoke('setStyle', style);
+ },
+
+ // @method bringToFront(): this
+ // Brings the layer group to the top of all other layers
+ bringToFront: function () {
+ return this.invoke('bringToFront');
+ },
+
+ // @method bringToBack(): this
+ // Brings the layer group to the back of all other layers
+ bringToBack: function () {
+ return this.invoke('bringToBack');
+ },
+
+ // @method getBounds(): LatLngBounds
+ // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
+ getBounds: function () {
+ var bounds = new LatLngBounds();
+
+ for (var id in this._layers) {
+ var layer = this._layers[id];
+ bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
+ }
+ return bounds;
+ }
+});
+
+// @factory L.featureGroup(layers: Layer[])
+// Create a feature group, optionally given an initial set of layers.
+var featureGroup = function (layers) {
+ return new FeatureGroup(layers);
+};
+
+/*
+ * @class Icon
+ * @aka L.Icon
+ *
+ * Represents an icon to provide when creating a marker.
+ *
+ * @example
+ *
+ * ```js
+ * var myIcon = L.icon({
+ * iconUrl: 'my-icon.png',
+ * iconRetinaUrl: 'my-icon@2x.png',
+ * iconSize: [38, 95],
+ * iconAnchor: [22, 94],
+ * popupAnchor: [-3, -76],
+ * shadowUrl: 'my-icon-shadow.png',
+ * shadowRetinaUrl: 'my-icon-shadow@2x.png',
+ * shadowSize: [68, 95],
+ * shadowAnchor: [22, 94]
+ * });
+ *
+ * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
+ * ```
+ *
+ * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
+ *
+ */
+
+var Icon = Class.extend({
+
+ /* @section
+ * @aka Icon options
+ *
+ * @option iconUrl: String = null
+ * **(required)** The URL to the icon image (absolute or relative to your script path).
+ *
+ * @option iconRetinaUrl: String = null
+ * The URL to a retina sized version of the icon image (absolute or relative to your
+ * script path). Used for Retina screen devices.
+ *
+ * @option iconSize: Point = null
+ * Size of the icon image in pixels.
+ *
+ * @option iconAnchor: Point = null
+ * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
+ * will be aligned so that this point is at the marker's geographical location. Centered
+ * by default if size is specified, also can be set in CSS with negative margins.
+ *
+ * @option popupAnchor: Point = [0, 0]
+ * The coordinates of the point from which popups will "open", relative to the icon anchor.
+ *
+ * @option tooltipAnchor: Point = [0, 0]
+ * The coordinates of the point from which tooltips will "open", relative to the icon anchor.
+ *
+ * @option shadowUrl: String = null
+ * The URL to the icon shadow image. If not specified, no shadow image will be created.
+ *
+ * @option shadowRetinaUrl: String = null
+ *
+ * @option shadowSize: Point = null
+ * Size of the shadow image in pixels.
+ *
+ * @option shadowAnchor: Point = null
+ * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
+ * as iconAnchor if not specified).
+ *
+ * @option className: String = ''
+ * A custom class name to assign to both icon and shadow images. Empty by default.
+ */
+
+ options: {
+ popupAnchor: [0, 0],
+ tooltipAnchor: [0, 0]
+ },
+
+ initialize: function (options) {
+ setOptions(this, options);
+ },
+
+ // @method createIcon(oldIcon?: HTMLElement): HTMLElement
+ // Called internally when the icon has to be shown, returns a `<img>` HTML element
+ // styled according to the options.
+ createIcon: function (oldIcon) {
+ return this._createIcon('icon', oldIcon);
+ },
+
+ // @method createShadow(oldIcon?: HTMLElement): HTMLElement
+ // As `createIcon`, but for the shadow beneath it.
+ createShadow: function (oldIcon) {
+ return this._createIcon('shadow', oldIcon);
+ },
+
+ _createIcon: function (name, oldIcon) {
+ var src = this._getIconUrl(name);
+
+ if (!src) {
+ if (name === 'icon') {
+ throw new Error('iconUrl not set in Icon options (see the docs).');
+ }
+ return null;
+ }
+
+ var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
+ this._setIconStyles(img, name);
+
+ return img;
+ },
+
+ _setIconStyles: function (img, name) {
+ var options = this.options;
+ var sizeOption = options[name + 'Size'];
+
+ if (typeof sizeOption === 'number') {
+ sizeOption = [sizeOption, sizeOption];
+ }
+
+ var size = toPoint(sizeOption),
+ anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
+ size && size.divideBy(2, true));
+
+ img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
+
+ if (anchor) {
+ img.style.marginLeft = (-anchor.x) + 'px';
+ img.style.marginTop = (-anchor.y) + 'px';
+ }
+
+ if (size) {
+ img.style.width = size.x + 'px';
+ img.style.height = size.y + 'px';
+ }
+ },
+
+ _createImg: function (src, el) {
+ el = el || document.createElement('img');
+ el.src = src;
+ return el;
+ },
+
+ _getIconUrl: function (name) {
+ return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
+ }
+});
+
+
+// @factory L.icon(options: Icon options)
+// Creates an icon instance with the given options.
+function icon(options) {
+ return new Icon(options);
+}
+
+/*
+ * @miniclass Icon.Default (Icon)
+ * @aka L.Icon.Default
+ * @section
+ *
+ * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
+ * no icon is specified. Points to the blue marker image distributed with Leaflet
+ * releases.
+ *
+ * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
+ * (which is a set of `Icon options`).
+ *
+ * If you want to _completely_ replace the default icon, override the
+ * `L.Marker.prototype.options.icon` with your own icon instead.
+ */
+
+var IconDefault = Icon.extend({
+
+ options: {
+ iconUrl: 'marker-icon.png',
+ iconRetinaUrl: 'marker-icon-2x.png',
+ shadowUrl: 'marker-shadow.png',
+ iconSize: [25, 41],
+ iconAnchor: [12, 41],
+ popupAnchor: [1, -34],
+ tooltipAnchor: [16, -28],
+ shadowSize: [41, 41]
+ },
+
+ _getIconUrl: function (name) {
+ if (!IconDefault.imagePath) { // Deprecated, backwards-compatibility only
+ IconDefault.imagePath = this._detectIconPath();
+ }
+
+ // @option imagePath: String
+ // `Icon.Default` will try to auto-detect the location of the
+ // blue icon images. If you are placing these images in a non-standard
+ // way, set this option to point to the right path.
+ return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
+ },
+
+ _detectIconPath: function () {
+ var el = create$1('div', 'leaflet-default-icon-path', document.body);
+ var path = getStyle(el, 'background-image') ||
+ getStyle(el, 'backgroundImage'); // IE8
+
+ document.body.removeChild(el);
+
+ if (path === null || path.indexOf('url') !== 0) {
+ path = '';
+ } else {
+ path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, '');
+ }
+
+ return path;
+ }
+});
+
+/*
+ * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
+ */
+
+
+/* @namespace Marker
+ * @section Interaction handlers
+ *
+ * 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:
+ *
+ * ```js
+ * marker.dragging.disable();
+ * ```
+ *
+ * @property dragging: Handler
+ * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
+ */
+
+var MarkerDrag = Handler.extend({
+ initialize: function (marker) {
+ this._marker = marker;
+ },
+
+ addHooks: function () {
+ var icon = this._marker._icon;
+
+ if (!this._draggable) {
+ this._draggable = new Draggable(icon, icon, true);
+ }
+
+ this._draggable.on({
+ dragstart: this._onDragStart,
+ predrag: this._onPreDrag,
+ drag: this._onDrag,
+ dragend: this._onDragEnd
+ }, this).enable();
+
+ addClass(icon, 'leaflet-marker-draggable');
+ },
+
+ removeHooks: function () {
+ this._draggable.off({
+ dragstart: this._onDragStart,
+ predrag: this._onPreDrag,
+ drag: this._onDrag,
+ dragend: this._onDragEnd
+ }, this).disable();
+
+ if (this._marker._icon) {
+ removeClass(this._marker._icon, 'leaflet-marker-draggable');
+ }
+ },
+
+ moved: function () {
+ return this._draggable && this._draggable._moved;
+ },
+
+ _adjustPan: function (e) {
+ var marker = this._marker,
+ map = marker._map,
+ speed = this._marker.options.autoPanSpeed,
+ padding = this._marker.options.autoPanPadding,
+ iconPos = getPosition(marker._icon),
+ bounds = map.getPixelBounds(),
+ origin = map.getPixelOrigin();
+
+ var panBounds = toBounds(
+ bounds.min._subtract(origin).add(padding),
+ bounds.max._subtract(origin).subtract(padding)
+ );
+
+ if (!panBounds.contains(iconPos)) {
+ // Compute incremental movement
+ var movement = toPoint(
+ (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) -
+ (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x),
+
+ (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) -
+ (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y)
+ ).multiplyBy(speed);
+
+ map.panBy(movement, {animate: false});
+
+ this._draggable._newPos._add(movement);
+ this._draggable._startPos._add(movement);
+
+ setPosition(marker._icon, this._draggable._newPos);
+ this._onDrag(e);
+
+ this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
+ }
+ },
+
+ _onDragStart: function () {
+ // @section Dragging events
+ // @event dragstart: Event
+ // Fired when the user starts dragging the marker.
+
+ // @event movestart: Event
+ // Fired when the marker starts moving (because of dragging).
+
+ this._oldLatLng = this._marker.getLatLng();
+ this._marker
+ .closePopup()
+ .fire('movestart')
+ .fire('dragstart');
+ },
+
+ _onPreDrag: function (e) {
+ if (this._marker.options.autoPan) {
+ cancelAnimFrame(this._panRequest);
+ this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
+ }
+ },
+
+ _onDrag: function (e) {
+ var marker = this._marker,
+ shadow = marker._shadow,
+ iconPos = getPosition(marker._icon),
+ latlng = marker._map.layerPointToLatLng(iconPos);
+
+ // update shadow position
+ if (shadow) {
+ setPosition(shadow, iconPos);
+ }
+
+ marker._latlng = latlng;
+ e.latlng = latlng;
+ e.oldLatLng = this._oldLatLng;
+
+ // @event drag: Event
+ // Fired repeatedly while the user drags the marker.
+ marker
+ .fire('move', e)
+ .fire('drag', e);
+ },
+
+ _onDragEnd: function (e) {
+ // @event dragend: DragEndEvent
+ // Fired when the user stops dragging the marker.
+
+ cancelAnimFrame(this._panRequest);
+
+ // @event moveend: Event
+ // Fired when the marker stops moving (because of dragging).
+ delete this._oldLatLng;
+ this._marker
+ .fire('moveend')
+ .fire('dragend', e);
+ }
+});
+
+/*
+ * @class Marker
+ * @inherits Interactive layer
+ * @aka L.Marker
+ * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
+ *
+ * @example
+ *
+ * ```js
+ * L.marker([50.5, 30.5]).addTo(map);
+ * ```
+ */
+
+var Marker = Layer.extend({
+
+ // @section
+ // @aka Marker options
+ options: {
+ // @option icon: Icon = *
+ // Icon instance to use for rendering the marker.
+ // See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
+ // If not specified, a common instance of `L.Icon.Default` is used.
+ icon: new IconDefault(),
+
+ // Option inherited from "Interactive layer" abstract class
+ interactive: true,
+
+ // @option keyboard: Boolean = true
+ // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
+ keyboard: true,
+
+ // @option title: String = ''
+ // Text for the browser tooltip that appear on marker hover (no tooltip by default).
+ title: '',
+
+ // @option alt: String = ''
+ // Text for the `alt` attribute of the icon image (useful for accessibility).
+ alt: '',