UNPKG

leaflet

Version:

JavaScript library for mobile-friendly interactive maps

349 lines (292 loc) 9.29 kB
import {Map} from '../map/Map'; import {Layer} from './Layer'; import {FeatureGroup} from './FeatureGroup'; import * as Util from '../core/Util'; import {toLatLng, LatLng} from '../geo/LatLng'; import {toPoint} from '../geometry/Point'; import * as DomUtil from '../dom/DomUtil'; /* * @class DivOverlay * @inherits Interactive layer * @aka L.DivOverlay * Base model for L.Popup and L.Tooltip. Inherit from it for custom overlays like plugins. */ // @namespace DivOverlay export var DivOverlay = Layer.extend({ // @section // @aka DivOverlay options options: { // @option interactive: Boolean = false // If true, the popup/tooltip will listen to the mouse events. interactive: false, // @option offset: Point = Point(0, 0) // The offset of the overlay position. offset: [0, 0], // @option className: String = '' // A custom CSS class name to assign to the overlay. className: '', // @option pane: String = undefined // `Map pane` where the overlay will be added. pane: undefined, // @option content: String|HTMLElement|Function = '' // Sets the HTML content of the overlay while initializing. 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 overlay. content: '' }, initialize: function (options, source) { if (options && (options instanceof LatLng || Util.isArray(options))) { this._latlng = toLatLng(options); Util.setOptions(this, source); } else { Util.setOptions(this, options); this._source = source; } if (this.options.content) { this._content = this.options.content; } }, // @method openOn(map: Map): this // Adds the overlay to the map. // Alternative to `map.openPopup(popup)`/`.openTooltip(tooltip)`. openOn: function (map) { map = arguments.length ? map : this._source._map; // experimental, not the part of public api if (!map.hasLayer(this)) { map.addLayer(this); } return this; }, // @method close(): this // Closes the overlay. // Alternative to `map.closePopup(popup)`/`.closeTooltip(tooltip)` // and `layer.closePopup()`/`.closeTooltip()`. close: function () { if (this._map) { this._map.removeLayer(this); } return this; }, // @method toggle(layer?: Layer): this // Opens or closes the overlay bound to layer depending on its current state. // Argument may be omitted only for overlay bound to layer. // Alternative to `layer.togglePopup()`/`.toggleTooltip()`. toggle: function (layer) { if (this._map) { this.close(); } else { if (arguments.length) { this._source = layer; } else { layer = this._source; } this._prepareOpen(); // open the overlay on the map this.openOn(layer._map); } return this; }, onAdd: function (map) { this._zoomAnimated = map._zoomAnimated; if (!this._container) { this._initLayout(); } if (map._fadeAnimated) { DomUtil.setOpacity(this._container, 0); } clearTimeout(this._removeTimeout); this.getPane().appendChild(this._container); this.update(); if (map._fadeAnimated) { DomUtil.setOpacity(this._container, 1); } this.bringToFront(); if (this.options.interactive) { DomUtil.addClass(this._container, 'leaflet-interactive'); this.addInteractiveTarget(this._container); } }, onRemove: function (map) { if (map._fadeAnimated) { DomUtil.setOpacity(this._container, 0); this._removeTimeout = setTimeout(Util.bind(DomUtil.remove, undefined, this._container), 200); } else { DomUtil.remove(this._container); } if (this.options.interactive) { DomUtil.removeClass(this._container, 'leaflet-interactive'); this.removeInteractiveTarget(this._container); } }, // @namespace DivOverlay // @method getLatLng: LatLng // Returns the geographical point of the overlay. getLatLng: function () { return this._latlng; }, // @method setLatLng(latlng: LatLng): this // Sets the geographical point where the overlay will open. setLatLng: function (latlng) { this._latlng = toLatLng(latlng); if (this._map) { this._updatePosition(); this._adjustPan(); } return this; }, // @method getContent: String|HTMLElement // Returns the content of the overlay. getContent: function () { return this._content; }, // @method setContent(htmlContent: String|HTMLElement|Function): this // Sets the HTML content of the overlay. 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 overlay. setContent: function (content) { this._content = content; this.update(); return this; }, // @method getElement: String|HTMLElement // Returns the HTML container of the overlay. getElement: function () { return this._container; }, // @method update: null // Updates the overlay content, layout and position. Useful for updating the overlay 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 overlay is visible on the map. isOpen: function () { return !!this._map && this._map.hasLayer(this); }, // @method bringToFront: this // Brings this overlay in front of other overlays (in the same map pane). bringToFront: function () { if (this._map) { DomUtil.toFront(this._container); } return this; }, // @method bringToBack: this // Brings this overlay to the back of other overlays (in the same map pane). bringToBack: function () { if (this._map) { DomUtil.toBack(this._container); } return this; }, // prepare bound overlay to open: update latlng pos / content source (for FeatureGroup) _prepareOpen: function (latlng) { var source = this._source; if (!source._map) { return false; } if (source instanceof FeatureGroup) { source = null; var layers = this._source._layers; for (var id in layers) { if (layers[id]._map) { source = layers[id]; break; } } if (!source) { return false; } // Unable to get source layer. // set overlay source to this layer this._source = source; } if (!latlng) { if (source.getCenter) { latlng = source.getCenter(); } else if (source.getLatLng) { latlng = source.getLatLng(); } else if (source.getBounds) { latlng = source.getBounds().getCenter(); } else { throw new Error('Unable to get source layer LatLng.'); } } this.setLatLng(latlng); if (this._map) { // update the overlay (content, layout, etc...) this.update(); } return true; }, _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); } // @namespace DivOverlay // @section DivOverlay events // @event contentupdate: Event // Fired when the content of the overlay is updated this.fire('contentupdate'); }, _updatePosition: function () { if (!this._map) { return; } var pos = this._map.latLngToLayerPoint(this._latlng), offset = toPoint(this.options.offset), anchor = this._getAnchor(); if (this._zoomAnimated) { 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 overlay in case the height of the overlay changes (images loading etc) this._container.style.bottom = bottom + 'px'; this._container.style.left = left + 'px'; }, _getAnchor: function () { return [0, 0]; } }); Map.include({ _initOverlay: function (OverlayClass, content, latlng, options) { var overlay = content; if (!(overlay instanceof OverlayClass)) { overlay = new OverlayClass(options).setContent(content); } if (latlng) { overlay.setLatLng(latlng); } return overlay; } }); Layer.include({ _initOverlay: function (OverlayClass, old, content, options) { var overlay = content; if (overlay instanceof OverlayClass) { Util.setOptions(overlay, options); overlay._source = this; } else { overlay = (old && !options) ? old : new OverlayClass(options, this); overlay.setContent(content); } return overlay; } });