/** * @class L.EditToolbar.Edit * @aka EditToolbar.Edit */ L.EditToolbar.Edit = L.Handler.extend({ statics: { TYPE: 'edit' }, // @method intialize(): void initialize: function (map, options) { L.Handler.prototype.initialize.call(this, map); L.setOptions(this, options); // Store the selectable layer group for ease of access this._featureGroup = options.featureGroup; if (!(this._featureGroup instanceof L.FeatureGroup)) { throw new Error('options.featureGroup must be a L.FeatureGroup'); } this._uneditedLayerProps = {}; // Save the type so super can fire, need to do this as cannot do this.TYPE :( this.type = L.EditToolbar.Edit.TYPE; var version = L.version.split('.'); //If Version is >= 1.2.0 if (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) { L.EditToolbar.Edit.include(L.Evented.prototype); } else { L.EditToolbar.Edit.include(L.Mixin.Events); } }, // @method enable(): void // Enable the edit toolbar enable: function () { if (this._enabled || !this._hasAvailableLayers()) { return; } this.fire('enabled', {handler: this.type}); //this disable other handlers this._map.fire(L.Draw.Event.EDITSTART, {handler: this.type}); //allow drawLayer to be updated before beginning edition. L.Handler.prototype.enable.call(this); this._featureGroup .on('layeradd', this._enableLayerEdit, this) .on('layerremove', this._disableLayerEdit, this); }, // @method disable(): void // Disable the edit toolbar disable: function () { if (!this._enabled) { return; } this._featureGroup .off('layeradd', this._enableLayerEdit, this) .off('layerremove', this._disableLayerEdit, this); L.Handler.prototype.disable.call(this); this._map.fire(L.Draw.Event.EDITSTOP, {handler: this.type}); this.fire('disabled', {handler: this.type}); }, // @method addHooks(): void // Add listener hooks for this handler addHooks: function () { var map = this._map; if (map) { map.getContainer().focus(); this._featureGroup.eachLayer(this._enableLayerEdit, this); this._tooltip = new L.Draw.Tooltip(this._map); this._tooltip.updateContent({ text: L.drawLocal.edit.handlers.edit.tooltip.text, subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext }); // Quickly access the tooltip to update for intersection checking map._editTooltip = this._tooltip; this._updateTooltip(); this._map .on('mousemove', this._onMouseMove, this) .on('touchmove', this._onMouseMove, this) .on('MSPointerMove', this._onMouseMove, this) .on(L.Draw.Event.EDITVERTEX, this._updateTooltip, this); } }, // @method removeHooks(): void // Remove listener hooks for this handler removeHooks: function () { if (this._map) { // Clean up selected layers. this._featureGroup.eachLayer(this._disableLayerEdit, this); // Clear the backups of the original layers this._uneditedLayerProps = {}; this._tooltip.dispose(); this._tooltip = null; this._map .off('mousemove', this._onMouseMove, this) .off('touchmove', this._onMouseMove, this) .off('MSPointerMove', this._onMouseMove, this) .off(L.Draw.Event.EDITVERTEX, this._updateTooltip, this); } }, // @method revertLayers(): void // Revert each layer's geometry changes revertLayers: function () { this._featureGroup.eachLayer(function (layer) { this._revertLayer(layer); }, this); }, // @method save(): void // Save the layer geometries save: function () { var editedLayers = new L.LayerGroup(); this._featureGroup.eachLayer(function (layer) { if (layer.edited) { editedLayers.addLayer(layer); layer.edited = false; } }); this._map.fire(L.Draw.Event.EDITED, {layers: editedLayers}); }, _backupLayer: function (layer) { var id = L.Util.stamp(layer); if (!this._uneditedLayerProps[id]) { // Polyline, Polygon or Rectangle if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) { this._uneditedLayerProps[id] = { latlngs: L.LatLngUtil.cloneLatLngs(layer.getLatLngs()) }; } else if (layer instanceof L.Circle) { this._uneditedLayerProps[id] = { latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng()), radius: layer.getRadius() }; } else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker this._uneditedLayerProps[id] = { latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng()) }; } } }, _getTooltipText: function () { return ({ text: L.drawLocal.edit.handlers.edit.tooltip.text, subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext }); }, _updateTooltip: function () { this._tooltip.updateContent(this._getTooltipText()); }, _revertLayer: function (layer) { var id = L.Util.stamp(layer); layer.edited = false; if (this._uneditedLayerProps.hasOwnProperty(id)) { // Polyline, Polygon or Rectangle if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) { layer.setLatLngs(this._uneditedLayerProps[id].latlngs); } else if (layer instanceof L.Circle) { layer.setLatLng(this._uneditedLayerProps[id].latlng); layer.setRadius(this._uneditedLayerProps[id].radius); } else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker or CircleMarker layer.setLatLng(this._uneditedLayerProps[id].latlng); } layer.fire('revert-edited', {layer: layer}); } }, _enableLayerEdit: function (e) { var layer = e.layer || e.target || e, pathOptions, poly; // Back up this layer (if haven't before) this._backupLayer(layer); if (this.options.poly) { poly = L.Util.extend({}, this.options.poly); layer.options.poly = poly; } // Set different style for editing mode if (this.options.selectedPathOptions) { pathOptions = L.Util.extend({}, this.options.selectedPathOptions); // Use the existing color of the layer if (pathOptions.maintainColor) { pathOptions.color = layer.options.color; pathOptions.fillColor = layer.options.fillColor; } layer.options.original = L.extend({}, layer.options); layer.options.editing = pathOptions; } if (layer instanceof L.Marker) { if (layer.editing) { layer.editing.enable(); } layer.dragging.enable(); layer .on('dragend', this._onMarkerDragEnd) // #TODO: remove when leaflet finally fixes their draggable so it's touch friendly again. .on('touchmove', this._onTouchMove, this) .on('MSPointerMove', this._onTouchMove, this) .on('touchend', this._onMarkerDragEnd, this) .on('MSPointerUp', this._onMarkerDragEnd, this); } else { layer.editing.enable(); } }, _disableLayerEdit: function (e) { var layer = e.layer || e.target || e; layer.edited = false; if (layer.editing) { layer.editing.disable(); } delete layer.options.editing; delete layer.options.original; // Reset layer styles to that of before select if (this._selectedPathOptions) { if (layer instanceof L.Marker) { this._toggleMarkerHighlight(layer); } else { // reset the layer style to what is was before being selected layer.setStyle(layer.options.previousOptions); // remove the cached options for the layer object delete layer.options.previousOptions; } } if (layer instanceof L.Marker) { layer.dragging.disable(); layer .off('dragend', this._onMarkerDragEnd, this) .off('touchmove', this._onTouchMove, this) .off('MSPointerMove', this._onTouchMove, this) .off('touchend', this._onMarkerDragEnd, this) .off('MSPointerUp', this._onMarkerDragEnd, this); } else { layer.editing.disable(); } }, _onMouseMove: function (e) { this._tooltip.updatePosition(e.latlng); }, _onMarkerDragEnd: function (e) { var layer = e.target; layer.edited = true; this._map.fire(L.Draw.Event.EDITMOVE, {layer: layer}); }, _onTouchMove: function (e) { var touchEvent = e.originalEvent.changedTouches[0], layerPoint = this._map.mouseEventToLayerPoint(touchEvent), latlng = this._map.layerPointToLatLng(layerPoint); e.target.setLatLng(latlng); }, _hasAvailableLayers: function () { return this._featureGroup.getLayers().length !== 0; } });