lib/leaflet.label/dist/leaflet.label-src.js in leaflet-js-0.6.beta4 vs lib/leaflet.label/dist/leaflet.label-src.js in leaflet-js-0.7.0

- old
+ new

@@ -9,153 +9,275 @@ (function (window, document, undefined) { /* * Leaflet.label assumes that you have already included the Leaflet library. */ -L.labelVersion = '0.1.2-dev'; +L.labelVersion = '0.2.1-dev'; -L.Label = L.Popup.extend({ +L.Label = L.Class.extend({ + + includes: L.Mixin.Events, + options: { - autoPan: false, className: '', - closePopupOnClick: false, + clickable: false, + direction: 'right', noHide: false, - offset: new L.Point(12, -15) // 6 (width of the label triangle) + 6 (padding) + offset: [12, -15], // 6 (width of the label triangle) + 6 (padding) + opacity: 1, + zoomAnimation: true }, + initialize: function (options, source) { + L.setOptions(this, options); + + this._source = source; + this._animated = L.Browser.any3d && this.options.zoomAnimation; + this._isOpen = false; + }, + 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); + this._initInteraction(); + this._update(); + + this.setOpacity(this.options.opacity); + + map + .on('moveend', this._onMoveEnd, this) + .on('viewreset', this._onViewReset, 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 + zoomanim: this._zoomAnimation, + moveend: this._onMoveEnd, + viewreset: this._onViewReset }, this); - if (map.options.fadeAnimation) { - L.DomUtil.setOpacity(this._container, 0); - } + this._removeInteraction(); this._map = null; }, + setLatLng: function (latlng) { + this._latlng = L.latLng(latlng); + if (this._map) { + this._updatePosition(); + } + return this; + }, + + setContent: function (content) { + // Backup previous content and store new content + this._previousContent = this._content; + this._content = content; + + this._updateContent(); + + return this; + }, + 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; + if (L.Browser.touch && !this.options.noHide) { + L.DomEvent.off(this._container, 'click', this.close); + } map.removeLayer(this); } }, updateZIndex: function (zIndex) { this._zIndex = zIndex; - if (this._container) { + if (this._container && this._zIndex) { this._container.style.zIndex = zIndex; } }, + setOpacity: function (opacity) { + this.options.opacity = opacity; + + if (this._container) { + L.DomUtil.setOpacity(this._container, opacity); + } + }, + _initLayout: function () { this._container = L.DomUtil.create('div', 'leaflet-label ' + this.options.className + ' leaflet-zoom-animated'); this.updateZIndex(this._zIndex); }, + _update: function () { + if (!this._map) { return; } + + this._container.style.visibility = 'hidden'; + + this._updateContent(); + this._updatePosition(); + + this._container.style.visibility = ''; + }, + _updateContent: function () { - if (!this._content) { return; } + if (!this._content || !this._map || this._prevContent === this._content) { + return; + } if (typeof this._content === 'string') { this._container.innerHTML = this._content; + + this._prevContent = this._content; + + this._labelWidth = this._container.offsetWidth; } }, - _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); + var map = this._map, + container = this._container, + centerPoint = map.latLngToContainerPoint(map.getCenter()), + labelPoint = map.layerPointToContainerPoint(pos), + direction = this.options.direction, + labelWidth = this._labelWidth, + offset = L.point(this.options.offset); - L.DomUtil.setPosition(this._container, pos); + // position to the right (right or auto & needs to) + if (direction === 'right' || direction === 'auto' && labelPoint.x < centerPoint.x) { + L.DomUtil.addClass(container, 'leaflet-label-right'); + L.DomUtil.removeClass(container, 'leaflet-label-left'); + + pos = pos.add(offset); + } else { // position to the left + L.DomUtil.addClass(container, 'leaflet-label-left'); + L.DomUtil.removeClass(container, 'leaflet-label-right'); + + pos = pos.add(L.point(-offset.x - labelWidth, offset.y)); + } + + L.DomUtil.setPosition(container, pos); }, _zoomAnimation: function (opt) { - var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center); + var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round(); 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) -}); + _onMoveEnd: function () { + if (!this._animated || this.options.direction === 'auto') { + this._updatePosition(); + } + }, -// 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() + _onViewReset: function (e) { + /* if map resets hard, we must update the label */ + if (e && e.hard) { + this._update(); + } + }, + + _initInteraction: function () { + if (!this.options.clickable) { return; } + + var container = this._container, + events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu']; + + L.DomUtil.addClass(container, 'leaflet-clickable'); + L.DomEvent.on(container, 'click', this._onMouseClick, this); + + for (var i = 0; i < events.length; i++) { + L.DomEvent.on(container, events[i], this._fireMouseEvent, this); + } + }, + + _removeInteraction: function () { + if (!this.options.clickable) { return; } + + var container = this._container, + events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu']; + + L.DomUtil.removeClass(container, 'leaflet-clickable'); + L.DomEvent.off(container, 'click', this._onMouseClick, this); + + for (var i = 0; i < events.length; i++) { + L.DomEvent.off(container, events[i], this._fireMouseEvent, this); + } + }, + + _onMouseClick: function (e) { + if (this.hasEventListeners(e.type)) { + L.DomEvent.stopPropagation(e); + } + + this.fire(e.type, { + originalEvent: e + }); + }, + + _fireMouseEvent: function (e) { + this.fire(e.type, { + originalEvent: e + }); + + // TODO proper custom event propagation + // this line will always be called if marker is in a FeatureGroup + if (e.type === 'contextmenu' && this.hasEventListeners(e.type)) { + L.DomEvent.preventDefault(e); + } + if (e.type !== 'mousedown') { + L.DomEvent.stopPropagation(e); + } else { + L.DomEvent.preventDefault(e); + } + } }); -L.Marker.include({ + +// This object is a mixin for L.Marker and L.CircleMarker. We declare it here as both need to include the contents. +L.BaseMarkerMethods = { showLabel: function () { - if (this._label && this._map) { - this._label.setLatLng(this._latlng); - this._map.showLabel(this._label); + 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(); + if (this.label) { + this.label.close(); } return this; }, setLabelNoHide: function (noHide) { @@ -173,11 +295,12 @@ this.hideLabel(); } }, bindLabel: function (content, options) { - var anchor = L.point(this.options.icon.options.labelAnchor) || new L.Point(0, 0); + var labelAnchor = this.options.icon ? this.options.icon.options.labelAnchor : this.options.labelAnchor, + anchor = L.point(labelAnchor) || L.point(0, 0); anchor = anchor.add(L.Label.prototype.options.offset); if (options && options.offset) { anchor = anchor.add(options.offset); @@ -185,55 +308,67 @@ options = L.Util.extend({offset: anchor}, options); this._labelNoHide = options.noHide; - if (!this._label) { + if (!this.label) { if (!this._labelNoHide) { this._addLabelRevealHandlers(); } this .on('remove', this.hideLabel, this) - .on('move', this._moveLabel, this); + .on('move', this._moveLabel, this) + .on('add', this._onMarkerAdd, this); this._hasLabelHandlers = true; } - this._label = new L.Label(options, this) + this.label = new L.Label(options, this) .setContent(content); return this; }, unbindLabel: function () { - if (this._label) { + if (this.label) { this.hideLabel(); - this._label = null; + this.label = null; if (this._hasLabelHandlers) { if (!this._labelNoHide) { this._removeLabelRevealHandlers(); } this .off('remove', this.hideLabel, this) - .off('move', this._moveLabel, this); + .off('move', this._moveLabel, this) + .off('add', this._onMarkerAdd, this); } this._hasLabelHandlers = false; } return this; }, updateLabelContent: function (content) { - if (this._label) { - this._label.setContent(content); + if (this.label) { + this.label.setContent(content); } }, + getLabel: function () { + return this.label; + }, + + _onMarkerAdd: function () { + if (this._labelNoHide) { + this.showLabel(); + } + }, + _addLabelRevealHandlers: function () { this .on('mouseover', this.showLabel, this) .on('mouseout', this.hideLabel, this); @@ -243,43 +378,93 @@ }, _removeLabelRevealHandlers: function () { this .off('mouseover', this.showLabel, this) - .off('mouseout', this.hideLabel, this) - .off('remove', this.hideLabel, this) - .off('move', this._moveLabel, this); + .off('mouseout', this.hideLabel, this); if (L.Browser.touch) { this.off('click', this.showLabel, this); } }, _moveLabel: function (e) { - this._label.setLatLng(e.latlng); - }, + this.label.setLatLng(e.latlng); + } +}; +// 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(L.BaseMarkerMethods); +L.Marker.include({ _originalUpdateZIndex: L.Marker.prototype._updateZIndex, _updateZIndex: function (offset) { var zIndex = this._zIndex + offset; this._originalUpdateZIndex(offset); - if (this._label) { - this._label.updateZIndex(zIndex); + if (this.label) { + this.label.updateZIndex(zIndex); } + }, + + _originalSetOpacity: L.Marker.prototype.setOpacity, + + setOpacity: function (opacity, labelHasSemiTransparency) { + this.options.labelHasSemiTransparency = labelHasSemiTransparency; + + this._originalSetOpacity(opacity); + }, + + _originalUpdateOpacity: L.Marker.prototype._updateOpacity, + + _updateOpacity: function () { + var absoluteOpacity = this.options.opacity === 0 ? 0 : 1; + + this._originalUpdateOpacity(); + + if (this.label) { + this.label.setOpacity(this.options.labelHasSemiTransparency ? this.options.opacity : absoluteOpacity); + } + }, + + _originalSetLatLng: L.Marker.prototype.setLatLng, + + setLatLng: function (latlng) { + if (this.label && !this._labelNoHide) { + this.hideLabel(); + } + + return this._originalSetLatLng(latlng); } }); +// Add in an option to icon that is used to set where the label anchor is +L.CircleMarker.mergeOptions({ + labelAnchor: new L.Point(0, 0) +}); + + +L.CircleMarker.include(L.BaseMarkerMethods); + L.Path.include({ bindLabel: function (content, options) { - if (!this._label || this._label.options !== options) { - this._label = new L.Label(options, this); + if (!this.label || this.label.options !== options) { + this.label = new L.Label(options, this); } - this._label.setContent(content); + this.label.setContent(content); if (!this._showLabelAdded) { this .on('mouseover', this._showLabel, this) .on('mousemove', this._moveLabel, this) @@ -293,45 +478,43 @@ return this; }, unbindLabel: function () { - if (this._label) { + if (this.label) { this._hideLabel(); - this._label = null; + 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); + if (this.label) { + this.label.setContent(content); } }, _showLabel: function (e) { - this._label.setLatLng(e.latlng); - this._map.showLabel(this._label); + this.label.setLatLng(e.latlng); + this._map.showLabel(this.label); }, _moveLabel: function (e) { - this._label.setLatLng(e.latlng); + this.label.setLatLng(e.latlng); }, _hideLabel: function () { - this._label.close(); + this.label.close(); } }); L.Map.include({ showLabel: function (label) { - this._label = label; - return this.addLayer(label); } }); L.FeatureGroup.include({ \ No newline at end of file