+ initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
+ this._url = url;
+ this._bounds = L.latLngBounds(bounds);
+
+ L.setOptions(this, options);
+ },
+
+ onAdd: function () {
+ if (!this._image) {
+ this._initImage();
+
+ if (this.options.opacity < 1) {
+ this._updateOpacity();
+ }
+ }
+
+ if (this.options.interactive) {
+ L.DomUtil.addClass(this._image, 'leaflet-interactive');
+ this.addInteractiveTarget(this._image);
+ }
+
+ this.getPane().appendChild(this._image);
+ this._reset();
+ },
+
+ onRemove: function () {
+ L.DomUtil.remove(this._image);
+ if (this.options.interactive) {
+ this.removeInteractiveTarget(this._image);
+ }
+ },
+
+ // @method setOpacity(opacity: Number): this
+ // Sets the opacity of the overlay.
+ setOpacity: function (opacity) {
+ this.options.opacity = opacity;
+
+ if (this._image) {
+ this._updateOpacity();
+ }
+ return this;
+ },
+
+ setStyle: function (styleOpts) {
+ if (styleOpts.opacity) {
+ this.setOpacity(styleOpts.opacity);
+ }
+ return this;
+ },
+
+ // @method bringToFront(): this
+ // Brings the layer to the top of all overlays.
+ bringToFront: function () {
+ if (this._map) {
+ L.DomUtil.toFront(this._image);
+ }
+ return this;
+ },
+
+ // @method bringToBack(): this
+ // Brings the layer to the bottom of all overlays.
+ bringToBack: function () {
+ if (this._map) {
+ L.DomUtil.toBack(this._image);
+ }
+ return this;
+ },
+
+ // @method setUrl(url: String): this
+ // Changes the URL of the image.
+ setUrl: function (url) {
+ this._url = url;
+
+ if (this._image) {
+ this._image.src = url;
+ }
+ return this;
+ },
+
+ setBounds: function (bounds) {
+ this._bounds = bounds;
+
+ if (this._map) {
+ this._reset();
+ }
+ return this;
+ },
+
+ getAttribution: function () {
+ return this.options.attribution;
+ },
+
+ getEvents: function () {
+ var events = {
+ zoom: this._reset,
+ viewreset: this._reset
+ };
+
+ if (this._zoomAnimated) {
+ events.zoomanim = this._animateZoom;
+ }
+
+ return events;
+ },
+
+ getBounds: function () {
+ return this._bounds;
+ },
+
+ getElement: function () {
+ return this._image;
+ },
+
+ _initImage: function () {
+ var img = this._image = L.DomUtil.create('img',
+ 'leaflet-image-layer ' + (this._zoomAnimated ? 'leaflet-zoom-animated' : ''));
+
+ img.onselectstart = L.Util.falseFn;
+ img.onmousemove = L.Util.falseFn;
+
+ img.onload = L.bind(this.fire, this, 'load');
+
+ if (this.options.crossOrigin) {
+ img.crossOrigin = '';
+ }
+
+ img.src = this._url;
+ img.alt = this.options.alt;
+ },
+
+ _animateZoom: function (e) {
+ var scale = this._map.getZoomScale(e.zoom),
+ offset = this._map._latLngToNewLayerPoint(this._bounds.getNorthWest(), e.zoom, e.center);
+
+ L.DomUtil.setTransform(this._image, offset, scale);
+ },
+
+ _reset: function () {
+ var image = this._image,
+ bounds = new L.Bounds(
+ this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
+ this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
+ size = bounds.getSize();
+
+ L.DomUtil.setPosition(image, bounds.min);
+
+ image.style.width = size.x + 'px';
+ image.style.height = size.y + 'px';
+ },
+
+ _updateOpacity: function () {
+ L.DomUtil.setOpacity(this._image, this.options.opacity);
+ }
+});
+
+// @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
+// Instantiates an image overlay object given the URL of the image and the
+// geographical bounds it is tied to.
+L.imageOverlay = function (url, bounds, options) {
+ return new L.ImageOverlay(url, bounds, options);
+};
+
+
+
+/*
+ * @class Icon
+ * @aka L.Icon
+ * @inherits Layer
+ *
+ * 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.
+ *
+ */
+
+L.Icon = L.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 = null
+ * The coordinates of the point from which popups 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.
+ */
+
+ initialize: function (options) {
+ L.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 = L.point(sizeOption),
+ anchor = L.point(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 L.Browser.retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
+ }
+});
+
+
+// @factory L.icon(options: Icon options)
+// Creates an icon instance with the given options.
+L.icon = function (options) {
+ return new L.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 change the default icon, just change the properties of `L.Icon.Default.prototype.options`
+ * (which is a set of `Icon options`).
+ */
+
+L.Icon.Default = L.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 (!L.Icon.Default.imagePath) { // Deprecated, backwards-compatibility only
+ L.Icon.Default.imagePath = this._detectIconPath();
+ }
+
+ // @option imagePath: String
+ // `L.Icon.Default` will try to auto-detect the absolute 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 absolute path.
+ return (this.options.imagePath || L.Icon.Default.imagePath) + L.Icon.prototype._getIconUrl.call(this, name);
+ },
+
+ _detectIconPath: function () {
+ var el = L.DomUtil.create('div', 'leaflet-default-icon-path', document.body);
+ var path = L.DomUtil.getStyle(el, 'background-image') ||
+ L.DomUtil.getStyle(el, 'backgroundImage'); // IE8
+
+ document.body.removeChild(el);
+
+ return path.indexOf('url') === 0 ?
+ path.replace(/^url\([\"\']?/, '').replace(/marker-icon\.png[\"\']?\)$/, '') : '';
+ }
+});
+
+
+
+/*
+ * @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);
+ * ```
+ */
+
+L.Marker = L.Layer.extend({
+
+ // @section
+ // @aka Marker options
+ options: {
+ // @option icon: Icon = *
+ // Icon class to use for rendering the marker. See [Icon documentation](#L.Icon) for details on how to customize the marker icon. If not specified, a new `L.Icon.Default` is used.
+ icon: new L.Icon.Default(),
+
+ // Option inherited from "Interactive layer" abstract class
+ interactive: true,
+
+ // @option draggable: Boolean = false
+ // Whether the marker is draggable with mouse/touch or not.
+ draggable: false,
+
+ // @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: '',
+
+ // @option zIndexOffset: Number = 0
+ // 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).
+ zIndexOffset: 0,
+
+ // @option opacity: Number = 1.0
+ // The opacity of the marker.
+ opacity: 1,
+
+ // @option riseOnHover: Boolean = false
+ // If `true`, the marker will get on top of others when you hover the mouse over it.
+ riseOnHover: false,
+
+ // @option riseOffset: Number = 250
+ // The z-index offset used for the `riseOnHover` feature.
+ riseOffset: 250,
+
+ // @option pane: String = 'markerPane'
+ // `Map pane` where the markers icon will be added.
+ pane: 'markerPane',
+
+ // FIXME: shadowPane is no longer a valid option
+ nonBubblingEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu']
+ },
+
+ /* @section
+ *
+ * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
+ */
+
+ initialize: function (latlng, options) {
+ L.setOptions(this, options);
+ this._latlng = L.latLng(latlng);
+ },
+
+ onAdd: function (map) {
+ this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
+
+ if (this._zoomAnimated) {
+ map.on('zoomanim', this._animateZoom, this);
+ }
+
+ this._initIcon();
+ this.update();
+ },
+
+ onRemove: function (map) {
+ if (this.dragging && this.dragging.enabled()) {
+ this.options.draggable = true;
+ this.dragging.removeHooks();
+ }
+
+ if (this._zoomAnimated) {
+ map.off('zoomanim', this._animateZoom, this);
+ }
+
+ this._removeIcon();
+ this._removeShadow();
+ },
+
+ getEvents: function () {
+ return {
+ zoom: this.update,
+ viewreset: this.update
+ };
+ },
+
+ // @method getLatLng: LatLng
+ // Returns the current geographical position of the marker.
+ getLatLng: function () {
+ return this._latlng;
+ },
+
+ // @method setLatLng(latlng: LatLng): this
+ // Changes the marker position to the given point.
+ setLatLng: function (latlng) {
+ var oldLatLng = this._latlng;
+ this._latlng = L.latLng(latlng);
+ this.update();
+
+ // @event move: Event
+ // 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`.
+ return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
+ },
+
+ // @method setZIndexOffset(offset: Number): this
+ // Changes the [zIndex offset](#marker-zindexoffset) of the marker.
+ setZIndexOffset: function (offset) {
+ this.options.zIndexOffset = offset;
+ return this.update();
+ },
+
+ // @method setIcon(icon: Icon): this
+ // Changes the marker icon.
+ setIcon: function (icon) {
+
+ this.options.icon = icon;
+
+ if (this._map) {
+ this._initIcon();
+ this.update();
+ }
+
+ if (this._popup) {
+ this.bindPopup(this._popup, this._popup.options);
+ }
+
+ return this;
+ },
+
+ getElement: function () {
+ return this._icon;
+ },
+
+ update: function () {
+
+ if (this._icon) {
+ var pos = this._map.latLngToLayerPoint(this._latlng).round();
+ this._setPos(pos);
+ }
+
+ return this;
+ },
+
+ _initIcon: function () {
+ var options = this.options,
+ classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
+
+ var icon = options.icon.createIcon(this._icon),
+ addIcon = false;
+
+ // if we're not reusing the icon, remove the old one and init new one
+ if (icon !== this._icon) {
+ if (this._icon) {
+ this._removeIcon();
+ }
+ addIcon = true;
+
+ if (options.title) {
+ icon.title = options.title;
+ }
+ if (options.alt) {
+ icon.alt = options.alt;
+ }
+ }
+
+ L.DomUtil.addClass(icon, classToAdd);
+
+ if (options.keyboard) {
+ icon.tabIndex = '0';
+ }
+
+ this._icon = icon;
+
+ if (options.riseOnHover) {
+ this.on({
+ mouseover: this._bringToFront,
+ mouseout: this._resetZIndex
+ });
+ }
+
+ var newShadow = options.icon.createShadow(this._shadow),
+ addShadow = false;
+
+ if (newShadow !== this._shadow) {
+ this._removeShadow();
+ addShadow = true;
+ }
+
+ if (newShadow) {
+ L.DomUtil.addClass(newShadow, classToAdd);
+ }
+ this._shadow = newShadow;
+
+
+ if (options.opacity < 1) {
+ this._updateOpacity();
+ }
+
+
+ if (addIcon) {
+ this.getPane().appendChild(this._icon);
+ }
+ this._initInteraction();
+ if (newShadow && addShadow) {
+ this.getPane('shadowPane').appendChild(this._shadow);
+ }
+ },
+
+ _removeIcon: function () {
+ if (this.options.riseOnHover) {
+ this.off({
+ mouseover: this._bringToFront,
+ mouseout: this._resetZIndex
+ });
+ }
+
+ L.DomUtil.remove(this._icon);
+ this.removeInteractiveTarget(this._icon);
+
+ this._icon = null;
+ },
+
+ _removeShadow: function () {
+ if (this._shadow) {
+ L.DomUtil.remove(this._shadow);
+ }
+ this._shadow = null;
+ },
+
+ _setPos: function (pos) {
+ L.DomUtil.setPosition(this._icon, pos);
+
+ if (this._shadow) {
+ L.DomUtil.setPosition(this._shadow, pos);
+ }
+
+ this._zIndex = pos.y + this.options.zIndexOffset;
+
+ this._resetZIndex();
+ },
+
+ _updateZIndex: function (offset) {
+ this._icon.style.zIndex = this._zIndex + offset;
+ },
+
+ _animateZoom: function (opt) {
+ var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
+
+ this._setPos(pos);
+ },
+
+ _initInteraction: function () {
+
+ if (!this.options.interactive) { return; }
+
+ L.DomUtil.addClass(this._icon, 'leaflet-interactive');
+
+ this.addInteractiveTarget(this._icon);
+
+ if (L.Handler.MarkerDrag) {
+ var draggable = this.options.draggable;
+ if (this.dragging) {
+ draggable = this.dragging.enabled();
+ this.dragging.disable();
+ }
+
+ this.dragging = new L.Handler.MarkerDrag(this);
+
+ if (draggable) {
+ this.dragging.enable();
+ }
+ }
+ },
+
+ // @method setOpacity(opacity: Number): this
+ // Changes the opacity of the marker.
+ setOpacity: function (opacity) {
+ this.options.opacity = opacity;
+ if (this._map) {
+ this._updateOpacity();
+ }
+
+ return this;
+ },
+
+ _updateOpacity: function () {
+ var opacity = this.options.opacity;
+
+ L.DomUtil.setOpacity(this._icon, opacity);
+
+ if (this._shadow) {
+ L.DomUtil.setOpacity(this._shadow, opacity);
+ }
+ },
+
+ _bringToFront: function () {
+ this._updateZIndex(this.options.riseOffset);
+ },
+
+ _resetZIndex: function () {