/* Leaflet.label, a plugin that adds labels to markers and vectors for Leaflet powered maps. (c) 2012-2013, Jacob Toye, Smartrak https://github.com/Leaflet/Leaflet.label http://leafletjs.com https://github.com/jacobtoye */ (function (window, document, undefined) { /* * Leaflet.label assumes that you have already included the Leaflet library. */ L.labelVersion = '0.1.2-dev'; L.Label = L.Popup.extend({ options: { autoPan: false, className: '', closePopupOnClick: false, noHide: false, offset: new L.Point(12, -15) // 6 (width of the label triangle) + 6 (padding) }, onAdd: function (map) { this._map = map; this._pane = this._source instanceof L.Marker ? map._panes.markerPane : map._panes.popupPane; if (!this._container) { this._initLayout(); } this._updateContent(); var animFade = map.options.fadeAnimation; if (animFade) { L.DomUtil.setOpacity(this._container, 0); } this._pane.appendChild(this._container); map.on('viewreset', this._updatePosition, this); if (this._animated) { map.on('zoomanim', this._zoomAnimation, this); } if (L.Browser.touch && !this.options.noHide) { L.DomEvent.on(this._container, 'click', this.close, this); } this._update(); if (animFade) { L.DomUtil.setOpacity(this._container, 1); } }, onRemove: function (map) { this._pane.removeChild(this._container); L.Util.falseFn(this._container.offsetWidth); // force reflow map.off({ viewreset: this._updatePosition, zoomanim: this._zoomAnimation }, this); if (map.options.fadeAnimation) { L.DomUtil.setOpacity(this._container, 0); } this._map = null; }, close: function () { var map = this._map; if (L.Browser.touch && !this.options.noHide) { L.DomEvent.off(this._container, 'click', this.close); } if (map) { map._label = null; map.removeLayer(this); } }, updateZIndex: function (zIndex) { this._zIndex = zIndex; if (this._container) { this._container.style.zIndex = zIndex; } }, _initLayout: function () { this._container = L.DomUtil.create('div', 'leaflet-label ' + this.options.className + ' leaflet-zoom-animated'); this.updateZIndex(this._zIndex); }, _updateContent: function () { if (!this._content) { return; } if (typeof this._content === 'string') { this._container.innerHTML = this._content; } }, _updateLayout: function () { // Do nothing }, _updatePosition: function () { var pos = this._map.latLngToLayerPoint(this._latlng); this._setPosition(pos); }, _setPosition: function (pos) { pos = pos.add(this.options.offset); L.DomUtil.setPosition(this._container, pos); }, _zoomAnimation: function (opt) { var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center); this._setPosition(pos); } }); // Add in an option to icon that is used to set where the label anchor is L.Icon.Default.mergeOptions({ labelAnchor: new L.Point(9, -20) }); // Have to do this since Leaflet is loaded before this plugin and initializes // L.Marker.options.icon therefore missing our mixin above. L.Marker.mergeOptions({ icon: new L.Icon.Default() }); L.Marker.include({ showLabel: function () { if (this._label && this._map) { this._label.setLatLng(this._latlng); this._map.showLabel(this._label); } return this; }, hideLabel: function () { if (this._label) { this._label.close(); } return this; }, setLabelNoHide: function (noHide) { if (this._labelNoHide === noHide) { return; } this._labelNoHide = noHide; if (noHide) { this._removeLabelRevealHandlers(); this.showLabel(); } else { this._addLabelRevealHandlers(); this.hideLabel(); } }, bindLabel: function (content, options) { var anchor = L.point(this.options.icon.options.labelAnchor) || new L.Point(0, 0); anchor = anchor.add(L.Label.prototype.options.offset); if (options && options.offset) { anchor = anchor.add(options.offset); } options = L.Util.extend({offset: anchor}, options); this._labelNoHide = options.noHide; if (!this._label) { if (!this._labelNoHide) { this._addLabelRevealHandlers(); } this .on('remove', this.hideLabel, this) .on('move', this._moveLabel, this); this._hasLabelHandlers = true; } this._label = new L.Label(options, this) .setContent(content); return this; }, unbindLabel: function () { if (this._label) { this.hideLabel(); this._label = null; if (this._hasLabelHandlers) { if (!this._labelNoHide) { this._removeLabelRevealHandlers(); } this .off('remove', this.hideLabel, this) .off('move', this._moveLabel, this); } this._hasLabelHandlers = false; } return this; }, updateLabelContent: function (content) { if (this._label) { this._label.setContent(content); } }, _addLabelRevealHandlers: function () { this .on('mouseover', this.showLabel, this) .on('mouseout', this.hideLabel, this); if (L.Browser.touch) { this.on('click', this.showLabel, this); } }, _removeLabelRevealHandlers: function () { this .off('mouseover', this.showLabel, this) .off('mouseout', this.hideLabel, this) .off('remove', this.hideLabel, this) .off('move', this._moveLabel, this); if (L.Browser.touch) { this.off('click', this.showLabel, this); } }, _moveLabel: function (e) { this._label.setLatLng(e.latlng); }, _originalUpdateZIndex: L.Marker.prototype._updateZIndex, _updateZIndex: function (offset) { var zIndex = this._zIndex + offset; this._originalUpdateZIndex(offset); if (this._label) { this._label.updateZIndex(zIndex); } } }); L.Path.include({ bindLabel: function (content, options) { if (!this._label || this._label.options !== options) { this._label = new L.Label(options, this); } this._label.setContent(content); if (!this._showLabelAdded) { this .on('mouseover', this._showLabel, this) .on('mousemove', this._moveLabel, this) .on('mouseout remove', this._hideLabel, this); if (L.Browser.touch) { this.on('click', this._showLabel, this); } this._showLabelAdded = true; } return this; }, unbindLabel: function () { if (this._label) { this._hideLabel(); this._label = null; this._showLabelAdded = false; this .off('mouseover', this._showLabel, this) .off('mousemove', this._moveLabel, this) .off('mouseout remove', this._hideLabel, this); } return this; }, updateLabelContent: function (content) { if (this._label) { this._label.setContent(content); } }, _showLabel: function (e) { this._label.setLatLng(e.latlng); this._map.showLabel(this._label); }, _moveLabel: function (e) { this._label.setLatLng(e.latlng); }, _hideLabel: function () { this._label.close(); } }); L.Map.include({ showLabel: function (label) { this._label = label; return this.addLayer(label); } }); L.FeatureGroup.include({ // TODO: remove this when AOP is supported in Leaflet, need this as we cannot put code in removeLayer() clearLayers: function () { this.unbindLabel(); this.eachLayer(this.removeLayer, this); return this; }, bindLabel: function (content, options) { return this.invoke('bindLabel', content, options); }, unbindLabel: function () { return this.invoke('unbindLabel'); }, updateLabelContent: function (content) { this.invoke('updateLabelContent', content); } }); }(this, document));