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