+ _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 () {
+ this._updateZIndex(0);
+ }
+});
+
+
+// factory L.marker(latlng: LatLng, options? : Marker options)
+
+// @factory L.marker(latlng: LatLng, options? : Marker options)
+// Instantiates a Marker object given a geographical point and optionally an options object.
+L.marker = function (latlng, options) {
+ return new L.Marker(latlng, options);
+};
+
+
+
+/*
+ * @class DivIcon
+ * @aka L.DivIcon
+ * @inherits Icon
+ *
+ * Represents a lightweight icon for markers that uses a simple `<div>`
+ * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
+ *
+ * @example
+ * ```js
+ * var myIcon = L.divIcon({className: 'my-div-icon'});
+ * // you can set .my-div-icon styles in CSS
+ *
+ * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
+ * ```
+ *
+ * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
+ */
+
+L.DivIcon = L.Icon.extend({
+ options: {
+ // @section
+ // @aka DivIcon options
+ iconSize: [12, 12], // also can be set through CSS
+
+ // iconAnchor: (Point),
+ // popupAnchor: (Point),
+
+ // @option html: String = ''
+ // Custom HTML code to put inside the div element, empty by default.
+ html: false,
+
+ // @option bgPos: Point = [0, 0]
+ // Optional relative position of the background, in pixels
+ bgPos: null,
+
+ className: 'leaflet-div-icon'
+ },
+
+ createIcon: function (oldIcon) {
+ var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
+ options = this.options;
+
+ div.innerHTML = options.html !== false ? options.html : '';
+
+ if (options.bgPos) {
+ var bgPos = L.point(options.bgPos);
+ div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
+ }
+ this._setIconStyles(div, 'icon');
+
+ return div;
+ },
+
+ createShadow: function () {
+ return null;
+ }
+});
+
+// @factory L.divIcon(options: DivIcon options)
+// Creates a `DivIcon` instance with the given options.
+L.divIcon = function (options) {
+ return new L.DivIcon(options);
+};
+
+
+
+/*
+ * @class DivOverlay
+ * @inherits Layer
+ * @aka L.DivOverlay
+ * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
+ */
+
+// @namespace DivOverlay
+L.DivOverlay = L.Layer.extend({
+
+ // @section
+ // @aka DivOverlay options
+ options: {
+ // @option offset: Point = Point(0, 7)
+ // The offset of the popup position. Useful to control the anchor
+ // of the popup when opening it on some overlays.
+ offset: [0, 7],
+
+ // @option className: String = ''
+ // A custom CSS class name to assign to the popup.
+ className: '',
+
+ // @option pane: String = 'popupPane'
+ // `Map pane` where the popup will be added.
+ pane: 'popupPane'
+ },
+
+ initialize: function (options, source) {
+ L.setOptions(this, options);
+
+ this._source = source;
+ },
+
+ onAdd: function (map) {
+ this._zoomAnimated = map._zoomAnimated;
+
+ if (!this._container) {
+ this._initLayout();
+ }
+
+ if (map._fadeAnimated) {
+ L.DomUtil.setOpacity(this._container, 0);
+ }
+
+ clearTimeout(this._removeTimeout);
+ this.getPane().appendChild(this._container);
+ this.update();
+
+ if (map._fadeAnimated) {
+ L.DomUtil.setOpacity(this._container, 1);
+ }
+
+ this.bringToFront();
+ },
+
+ onRemove: function (map) {
+ if (map._fadeAnimated) {
+ L.DomUtil.setOpacity(this._container, 0);
+ this._removeTimeout = setTimeout(L.bind(L.DomUtil.remove, L.DomUtil, this._container), 200);
+ } else {
+ L.DomUtil.remove(this._container);
+ }
+ },
+
+ // @namespace Popup
+ // @method getLatLng: LatLng
+ // Returns the geographical point of popup.
+ getLatLng: function () {
+ return this._latlng;
+ },
+
+ // @method setLatLng(latlng: LatLng): this
+ // Sets the geographical point where the popup will open.
+ setLatLng: function (latlng) {
+ this._latlng = L.latLng(latlng);
+ if (this._map) {
+ this._updatePosition();
+ this._adjustPan();
+ }
+ return this;
+ },
+
+ // @method getContent: String|HTMLElement
+ // Returns the content of the popup.
+ getContent: function () {
+ return this._content;
+ },
+
+ // @method setContent(htmlContent: String|HTMLElement|Function): this
+ // 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.
+ setContent: function (content) {
+ this._content = content;
+ this.update();
+ return this;
+ },
+
+ // @method getElement: String|HTMLElement
+ // Alias for [getContent()](#popup-getcontent)
+ getElement: function () {
+ return this._container;
+ },
+
+ // @method update: null
+ // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
+ update: function () {
+ if (!this._map) { return; }
+
+ this._container.style.visibility = 'hidden';
+
+ this._updateContent();
+ this._updateLayout();
+ this._updatePosition();
+
+ this._container.style.visibility = '';
+
+ this._adjustPan();
+ },
+
+ getEvents: function () {
+ var events = {
+ zoom: this._updatePosition,
+ viewreset: this._updatePosition
+ };
+
+ if (this._zoomAnimated) {
+ events.zoomanim = this._animateZoom;
+ }
+ return events;
+ },
+
+ // @method isOpen: Boolean
+ // Returns `true` when the popup is visible on the map.
+ isOpen: function () {
+ return !!this._map && this._map.hasLayer(this);
+ },
+
+ // @method bringToFront: this
+ // Brings this popup in front of other popups (in the same map pane).
+ bringToFront: function () {
+ if (this._map) {
+ L.DomUtil.toFront(this._container);
+ }
+ return this;
+ },
+
+ // @method bringToBack: this
+ // Brings this popup to the back of other popups (in the same map pane).
+ bringToBack: function () {
+ if (this._map) {
+ L.DomUtil.toBack(this._container);
+ }
+ return this;
+ },
+
+ _updateContent: function () {
+ if (!this._content) { return; }
+
+ var node = this._contentNode;
+ var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
+
+ if (typeof content === 'string') {
+ node.innerHTML = content;
+ } else {
+ while (node.hasChildNodes()) {
+ node.removeChild(node.firstChild);
+ }
+ node.appendChild(content);
+ }
+ this.fire('contentupdate');
+ },
+
+ _updatePosition: function () {
+ if (!this._map) { return; }
+
+ var pos = this._map.latLngToLayerPoint(this._latlng),
+ offset = L.point(this.options.offset),
+ anchor = this._getAnchor();
+
+ if (this._zoomAnimated) {
+ L.DomUtil.setPosition(this._container, pos.add(anchor));
+ } else {
+ offset = offset.add(pos).add(anchor);
+ }
+
+ var bottom = this._containerBottom = -offset.y,
+ left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
+
+ // bottom position the popup in case the height of the popup changes (images loading etc)
+ this._container.style.bottom = bottom + 'px';
+ this._container.style.left = left + 'px';
+ },
+
+ _getAnchor: function () {
+ return [0, 0];
+ }
+
+});
+
+
+
+/*
+ * @class Popup
+ * @inherits DivOverlay
+ * @aka L.Popup
+ * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
+ * open popups while making sure that only one popup is open at one time
+ * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
+ *
+ * @example
+ *
+ * If you want to just bind a popup to marker click and then open it, it's really easy:
+ *
+ * ```js
+ * marker.bindPopup(popupContent).openPopup();
+ * ```
+ * Path overlays like polylines also have a `bindPopup` method.
+ * Here's a more complicated way to open a popup on a map:
+ *
+ * ```js
+ * var popup = L.popup()
+ * .setLatLng(latlng)
+ * .setContent('<p>Hello world!<br />This is a nice popup.</p>')
+ * .openOn(map);
+ * ```
+ */
+
+
+// @namespace Popup
+L.Popup = L.DivOverlay.extend({
+
+ // @section
+ // @aka Popup options
+ options: {
+ // @option maxWidth: Number = 300
+ // Max width of the popup, in pixels.
+ maxWidth: 300,
+
+ // @option minWidth: Number = 50
+ // Min width of the popup, in pixels.
+ minWidth: 50,
+
+ // @option maxHeight: Number = null
+ // If set, creates a scrollable container of the given height
+ // inside a popup if its content exceeds it.
+ maxHeight: null,
+
+ // @option autoPan: Boolean = true
+ // Set it to `false` if you don't want the map to do panning animation
+ // to fit the opened popup.
+ autoPan: true,
+
+ // @option autoPanPaddingTopLeft: Point = null
+ // The margin between the popup and the top left corner of the map
+ // view after autopanning was performed.
+ autoPanPaddingTopLeft: null,
+
+ // @option autoPanPaddingBottomRight: Point = null
+ // The margin between the popup and the bottom right corner of the map
+ // view after autopanning was performed.
+ autoPanPaddingBottomRight: null,
+
+ // @option autoPanPadding: Point = Point(5, 5)
+ // Equivalent of setting both top left and bottom right autopan padding to the same value.
+ autoPanPadding: [5, 5],
+
+ // @option keepInView: Boolean = false
+ // Set it to `true` if you want to prevent users from panning the popup
+ // off of the screen while it is open.
+ keepInView: false,
+
+ // @option closeButton: Boolean = true
+ // Controls the presence of a close button in the popup.
+ closeButton: true,
+
+ // @option autoClose: Boolean = true
+ // Set it to `false` if you want to override the default behavior of
+ // the popup closing when user clicks the map (set globally by
+ // the Map's [closePopupOnClick](#map-closepopuponclick) option).
+ autoClose: true,
+
+ // @option className: String = ''
+ // A custom CSS class name to assign to the popup.
+ className: ''
+ },
+
+ // @namespace Popup
+ // @method openOn(map: Map): this
+ // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
+ openOn: function (map) {
+ map.openPopup(this);
+ return this;
+ },
+
+ onAdd: function (map) {
+ L.DivOverlay.prototype.onAdd.call(this, map);
+
+ // @namespace Map
+ // @section Popup events
+ // @event popupopen: PopupEvent
+ // Fired when a popup is opened in the map
+ map.fire('popupopen', {popup: this});
+
+ if (this._source) {
+ // @namespace Layer
+ // @section Popup events
+ // @event popupopen: PopupEvent
+ // Fired when a popup bound to this layer is opened
+ this._source.fire('popupopen', {popup: this}, true);
+ // For non-path layers, we toggle the popup when clicking
+ // again the layer, so prevent the map to reopen it.
+ if (!(this._source instanceof L.Path)) {
+ this._source.on('preclick', L.DomEvent.stopPropagation);
+ }
+ }
+ },
+
+ onRemove: function (map) {
+ L.DivOverlay.prototype.onRemove.call(this, map);
+
+ // @namespace Map
+ // @section Popup events
+ // @event popupclose: PopupEvent
+ // Fired when a popup in the map is closed
+ map.fire('popupclose', {popup: this});
+
+ if (this._source) {
+ // @namespace Layer
+ // @section Popup events
+ // @event popupclose: PopupEvent
+ // Fired when a popup bound to this layer is closed
+ this._source.fire('popupclose', {popup: this}, true);
+ if (!(this._source instanceof L.Path)) {
+ this._source.off('preclick', L.DomEvent.stopPropagation);
+ }
+ }
+ },
+
+ getEvents: function () {
+ var events = L.DivOverlay.prototype.getEvents.call(this);
+
+ if ('closeOnClick' in this.options ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
+ events.preclick = this._close;
+ }
+
+ if (this.options.keepInView) {
+ events.moveend = this._adjustPan;
+ }
+
+ return events;
+ },
+
+ _close: function () {
+ if (this._map) {
+ this._map.closePopup(this);
+ }
+ },
+
+ _initLayout: function () {
+ var prefix = 'leaflet-popup',
+ container = this._container = L.DomUtil.create('div',
+ prefix + ' ' + (this.options.className || '') +
+ ' leaflet-zoom-animated');
+
+ if (this.options.closeButton) {
+ var closeButton = this._closeButton = L.DomUtil.create('a', prefix + '-close-button', container);
+ closeButton.href = '#close';
+ closeButton.innerHTML = '×';
+
+ L.DomEvent.on(closeButton, 'click', this._onCloseButtonClick, this);
+ }
+
+ var wrapper = this._wrapper = L.DomUtil.create('div', prefix + '-content-wrapper', container);
+ this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper);
+
+ L.DomEvent
+ .disableClickPropagation(wrapper)
+ .disableScrollPropagation(this._contentNode)
+ .on(wrapper, 'contextmenu', L.DomEvent.stopPropagation);