umap/umap/static/umap/js/umap.features.js

1024 lines
33 KiB
JavaScript

L.Storage.FeatureMixin = {
staticOptions: {},
initialize: function (map, latlng, options) {
this.map = map;
if(typeof options === 'undefined') {
options = {};
}
// DataLayer the marker belongs to
this.datalayer = options.datalayer || null;
this.properties = {_storage_options: {}};
if (options.geojson) {
this.populate(options.geojson);
}
var isDirty = false,
self = this;
try {
Object.defineProperty(this, 'isDirty', {
get: function () {
return isDirty;
},
set: function (status) {
if (!isDirty && status) {
self.fire('isdirty');
}
isDirty = status;
if (self.datalayer) {
self.datalayer.isDirty = status;
}
}
});
}
catch (e) {
// Certainly IE8, which has a limited version of defineProperty
}
this.preInit();
this.addInteractions();
this.parentClass.prototype.initialize.call(this, latlng, options);
},
preInit: function () {},
isReadOnly: function () {
return this.datalayer && this.datalayer.isRemoteLayer();
},
view: function(e) {
if (this.map.editEnabled) return;
var outlink = this.properties._storage_options.outlink,
target = this.properties._storage_options.outlinkTarget
if (outlink) {
switch (target) {
case 'self':
window.location = outlink;
break;
case 'parent':
window.top.location = outlink;
break;
default:
var win = window.open(this.properties._storage_options.outlink);
}
return;
}
if (this.map.slideshow) this.map.slideshow.current = this;
this.attachPopup();
this.openPopup(e && e.latlng || this.getCenter());
},
openPopup: function () {
if (this.map.editEnabled) return;
this.parentClass.prototype.openPopup.apply(this, arguments);
},
edit: function(e) {
if(!this.map.editEnabled || this.isReadOnly()) return;
var container = L.DomUtil.create('div');
var builder = new L.S.FormBuilder(this, ['datalayer'], {
callback: function () {this.edit(e);} // removeLayer step will close the edit panel, let's reopen it
});
container.appendChild(builder.build());
var properties = [], property;
for (var i = 0; i < this.datalayer._propertiesIndex.length; i++) {
property = this.datalayer._propertiesIndex[i];
if (L.Util.indexOf(['name', 'description'], property) !== -1) {continue;}
properties.push(['properties.' + property, {label: property}]);
}
// We always want name and description for now (properties management to come)
properties.unshift('properties.description');
properties.unshift('properties.name');
builder = new L.S.FormBuilder(this, properties,
{
id: 'storage-feature-properties',
callback: this.resetTooltip
}
);
container.appendChild(builder.build());
this.appendEditFieldsets(container);
var advancedActions = L.DomUtil.createFieldset(container, L._('Advanced actions'));
this.getAdvancedEditActions(advancedActions);
this.map.ui.openPanel({data: {html: container}, className: 'dark'});
this.map.editedFeature = this;
if (!this.isOnScreen()) this.bringToCenter(e);
},
getAdvancedEditActions: function (container) {
var deleteLink = L.DomUtil.create('a', 'button storage-delete', container);
deleteLink.href = '#';
deleteLink.innerHTML = L._('Delete');
L.DomEvent.on(deleteLink, 'click', function (e) {
L.DomEvent.stop(e);
if (this.confirmDelete()) this.map.ui.closePanel();
}, this);
},
appendEditFieldsets: function (container) {
var optionsFields = this.getShapeOptions();
var builder = new L.S.FormBuilder(this, optionsFields, {
id: 'storage-feature-shape-properties',
callback: this._redraw
});
var shapeProperties = L.DomUtil.createFieldset(container, L._('Shape properties'));
shapeProperties.appendChild(builder.build());
var advancedOptions = this.getAdvancedOptions();
var builder = new L.S.FormBuilder(this, advancedOptions, {
id: 'storage-feature-advanced-properties',
callback: this._redraw
});
var advancedProperties = L.DomUtil.createFieldset(container, L._('Advanced properties'));
advancedProperties.appendChild(builder.build());
var interactionOptions = this.getInteractionOptions();
builder = new L.S.FormBuilder(this, interactionOptions, {
callback: this._redraw
});
var popupFieldset = L.DomUtil.createFieldset(container, L._('Interaction options'));
popupFieldset.appendChild(builder.build());
},
getInteractionOptions: function () {
return [
'properties._storage_options.popupTemplate',
'properties._storage_options.showLabel',
'properties._storage_options.labelDirection',
'properties._storage_options.labelHover',
'properties._storage_options.labelInteractive'
];
},
endEdit: function () {},
getDisplayName: function (fallback) {
if (fallback === undefined) fallback = this.datalayer.options.name;
var key = this.getOption('labelKey') || 'name';
return this.properties[key] || this.properties.title || fallback;
},
hasPopupFooter: function () {
if (L.Browser.ielt9) return false;
if (this.datalayer.isRemoteLayer() && this.datalayer.options.remoteData.dynamic) return false;
return this.map.options.displayPopupFooter;
},
getPopupClass: function () {
return L.Storage.Popup[this.getOption('popupTemplate')] || L.Storage.Popup;
},
attachPopup: function () {
var Class = this.getPopupClass();
this.bindPopup(new Class(this));
},
confirmDelete: function () {
if (confirm(L._('Are you sure you want to delete the feature?'))) {
this.del();
return true;
}
return false;
},
del: function () {
this.isDirty = true;
this.map.closePopup();
if (this.datalayer) {
this.datalayer.removeLayer(this);
this.disconnectFromDataLayer(this.datalayer);
}
},
connectToDataLayer: function (datalayer) {
this.datalayer = datalayer;
this.options.renderer = this.datalayer.renderer;
},
disconnectFromDataLayer: function (datalayer) {
if (this.datalayer === datalayer) {
this.datalayer = null;
}
},
populate: function (feature) {
this.properties = L.extend({}, feature.properties);
this.properties._storage_options = L.extend({}, this.properties._storage_options);
// Retrocompat
if (this.properties._storage_options.clickable === false) {
this.properties._storage_options.interactive = false;
delete this.properties._storage_options.clickable;
}
},
changeDataLayer: function(datalayer) {
if(this.datalayer) {
this.datalayer.isDirty = true;
this.datalayer.removeLayer(this);
}
datalayer.addLayer(this);
datalayer.isDirty = true;
this._redraw();
},
getOption: function (option, fallback) {
var value = fallback || null;
if (typeof this.staticOptions[option] !== 'undefined') {
value = this.staticOptions[option];
}
else if (L.Util.usableOption(this.properties._storage_options, option)) {
value = this.properties._storage_options[option];
}
else if (this.datalayer) {
value = this.datalayer.getOption(option);
}
else {
value = this.map.getOption(option);
}
return value;
},
bringToCenter: function (e) {
e = e || {};
var latlng = e.latlng || this.getCenter();
this.map.setView(latlng, e.zoomTo || this.map.getZoom());
if (e.callback) e.callback.call(this);
},
zoomTo: function (e) {
e = e || {};
var easing = e.easing !== undefined ? e.easing : this.map.options.easing;
if (easing) this.flyTo();
else this.bringToCenter({zoomTo: this.getBestZoom(), callback: e.callback});
},
getBestZoom: function () {
return this.getOption('zoomTo');
},
flyTo: function () {
this.map.flyTo(this.getCenter(), this.getBestZoom());
},
getNext: function () {
return this.datalayer.getNextFeature(this);
},
getPrevious: function () {
return this.datalayer.getPreviousFeature(this);
},
cloneProperties: function () {
var properties = L.extend({}, this.properties);
properties._storage_options = L.extend({}, properties._storage_options);
if (Object.keys && Object.keys(properties._storage_options).length === 0) {
delete properties._storage_options; // It can make a difference on big data sets
}
return properties;
},
deleteProperty: function (property) {
delete this.properties[property];
this.makeDirty();
},
renameProperty: function (from, to) {
this.properties[to] = this.properties[from];
this.deleteProperty(from);
},
toGeoJSON: function () {
var geojson = this.parentClass.prototype.toGeoJSON.call(this);
geojson.properties = this.cloneProperties();
return geojson;
},
addInteractions: function () {
this.on('contextmenu editable:vertex:contextmenu', this._showContextMenu, this);
this.on('click', this._onClick);
},
_onClick: function (e) {
if (this.map.measureTools && this.map.measureTools.enabled()) return;
this._popupHandlersAdded = true; // Prevent leaflet from managing event
if(!this.map.editEnabled) {
this.view(e);
} else if (!this.isReadOnly()) {
if(e.originalEvent.shiftKey) {
if(this._toggleEditing)
this._toggleEditing(e);
else
this.edit(e);
}
else {
new L.Toolbar.Popup(e.latlng, {
className: 'leaflet-inplace-toolbar',
anchor: this.getPopupToolbarAnchor(),
actions: this.getInplaceToolbarActions(e)
}).addTo(this.map, this, e.latlng);
}
}
L.DomEvent.stop(e);
},
getPopupToolbarAnchor: function () {
return [0, 0];
},
getInplaceToolbarActions: function (e) {
return [L.S.ToggleEditAction, L.S.DeleteFeatureAction];
},
_showContextMenu: function (e) {
L.DomEvent.stop(e);
var pt = this.map.mouseEventToContainerPoint(e.originalEvent);
e.relatedTarget = this;
this.map.contextmenu.showAt(pt, e);
},
makeDirty: function () {
this.isDirty = true;
},
getMap: function () {
return this.map;
},
getContextMenuItems: function (e) {
var items = [];
if (this.map.editEnabled && !this.isReadOnly()) {
items = items.concat(this.getContextMenuEditItems(e));
}
return items;
},
getContextMenuEditItems: function () {
var items = ['-'];
if (this.map.editedFeature !== this) {
items.push(
{
text: L._('Edit this feature'),
callback: this.edit,
context: this,
iconCls: 'storage-edit'
}
);
}
items = items.concat(
{
text: L._('Edit feature\'s layer'),
callback: this.datalayer.edit,
context: this.datalayer,
iconCls: 'storage-edit'
},
{
text: L._('Delete this feature'),
callback: this.confirmDelete,
context: this,
iconCls: 'storage-delete'
}
);
return items;
},
onRemove: function (map) {
this.parentClass.prototype.onRemove.call(this, map);
if (this.map.editedFeature === this) {
this.endEdit();
this.map.ui.closePanel();
}
},
resetTooltip: function () {
var displayName = this.getDisplayName(null),
options = {
permanent: !this.getOption('labelHover'),
direction: this.getOption('labelDirection'),
interactive: this.getOption('labelInteractive')
};
this.unbindTooltip();
if (this.getOption('showLabel') && displayName) this.bindTooltip(L.Util.escapeHTML(displayName), options);
},
matchFilter: function (filter, keys) {
filter = filter.toLowerCase();
for (var i = 0; i < keys.length; i++) {
if ((this.properties[keys[i]] || '').toLowerCase().indexOf(filter) !== -1) return true;
}
return false;
},
onVertexRawClick: function (e) {
new L.Toolbar.Popup(e.latlng, {
className: 'leaflet-inplace-toolbar',
actions: this.getVertexActions(e)
}).addTo(this.map, this, e.latlng, e.vertex);
},
getVertexActions: function () {
return [L.S.DeleteVertexAction];
},
isMulti: function () {
return false;
}
};
L.Storage.Marker = L.Marker.extend({
parentClass: L.Marker,
includes: [L.Storage.FeatureMixin, L.Mixin.Events],
preInit: function () {
this.setIcon(this.getIcon());
},
addInteractions: function () {
L.Storage.FeatureMixin.addInteractions.call(this);
this.on('dragend', function (e) {
this.isDirty = true;
this.edit(e);
}, this);
if (!this.isReadOnly()) this.on('mouseover', this._enableDragging);
this.on('mouseout', this._onMouseOut);
this._popupHandlersAdded = true; // prevent Leaflet from binding event on bindPopup
},
_onMouseOut: function () {
if(this.dragging && this.dragging._draggable && !this.dragging._draggable._moving) {
// Do not disable if the mouse went out while dragging
this._disableDragging();
}
},
_enableDragging: function () {
// TODO: start dragging after 1 second on mouse down
if(this.map.editEnabled) {
if (!this.editEnabled()) this.enableEdit();
// Enabling dragging on the marker override the Draggable._OnDown
// event, which, as it stopPropagation, refrain the call of
// _onDown with map-pane element, which is responsible to
// set the _moved to false, and thus to enable the click.
// We should find a cleaner way to handle this.
this.map.dragging._draggable._moved = false;
}
},
_disableDragging: function () {
if(this.map.editEnabled) {
if (this.editor && this.editor.drawing) return; // when creating a new marker, the mouse can trigger the mouseover/mouseout event
// do not listen to them
this.disableEdit();
}
},
_redraw: function() {
if (this.datalayer && this.datalayer.isVisible()) {
this._initIcon();
this.update();
}
},
_initIcon: function () {
this.options.icon = this.getIcon();
L.Marker.prototype._initIcon.call(this);
this.resetTooltip();
},
disconnectFromDataLayer: function (datalayer) {
this.options.icon.datalayer = null;
L.Storage.FeatureMixin.disconnectFromDataLayer.call(this, datalayer);
},
_getIconUrl: function (name) {
if (typeof name === 'undefined') name = 'icon';
return this.getOption(name + 'Url');
},
getIconClass: function () {
return this.getOption('iconClass');
},
getIcon: function () {
var Class = L.Storage.Icon[this.getIconClass()] || L.Storage.Icon.Default;
return new Class(this.map, {feature: this});
},
getCenter: function () {
return this._latlng;
},
getClassName: function () {
return 'marker';
},
getShapeOptions: function () {
return [
'properties._storage_options.color',
'properties._storage_options.iconClass',
'properties._storage_options.iconUrl'
];
},
getAdvancedOptions: function () {
return [
'properties._storage_options.zoomTo'
];
},
appendEditFieldsets: function (container) {
L.Storage.FeatureMixin.appendEditFieldsets.call(this, container);
var coordinatesOptions = [
['_latlng.lat', {handler: 'FloatInput', label: L._('Latitude')}],
['_latlng.lng', {handler: 'FloatInput', label: L._('Longitude')}]
];
var builder = new L.S.FormBuilder(this, coordinatesOptions, {
callback: function () {
this._redraw();
this.bringToCenter();
},
callbackContext: this
});
var fieldset = L.DomUtil.createFieldset(container, L._('Coordinates'));
fieldset.appendChild(builder.build());
},
bringToCenter: function (e) {
if (this.datalayer.isClustered() && !this._icon) {
// callback is mandatory for zoomToShowLayer
this.datalayer.layer.zoomToShowLayer(this, e.callback || function (){});
} else {
L.Storage.FeatureMixin.bringToCenter.call(this, e);
}
},
isOnScreen: function () {
var bounds = this.map.getBounds();
return bounds.contains(this._latlng);
},
getPopupToolbarAnchor: function () {
return this.options.icon.options.popupAnchor;
}
});
L.Storage.PathMixin = {
connectToDataLayer: function (datalayer) {
L.S.FeatureMixin.connectToDataLayer.call(this, datalayer);
// We keep markers on their own layer on top of the paths.
this.options.pane = this.datalayer.pane;
},
edit: function (e) {
if(this.map.editEnabled) {
if (!this.editEnabled()) this.enableEdit();
L.Storage.FeatureMixin.edit.call(this, e);
}
},
_toggleEditing: function(e) {
if(this.map.editEnabled) {
if(this.editEnabled()) {
this.endEdit();
this.map.ui.closePanel();
}
else {
this.edit(e);
}
}
// FIXME: disable when disabling global edit
L.DomEvent.stop(e);
},
styleOptions: [
'smoothFactor',
'color',
'opacity',
'stroke',
'weight',
'fill',
'fillColor',
'fillOpacity',
'dashArray',
'interactive'
],
getShapeOptions: function () {
return [
'properties._storage_options.color',
'properties._storage_options.opacity',
'properties._storage_options.weight'
];
},
getAdvancedOptions: function () {
return [
'properties._storage_options.smoothFactor',
'properties._storage_options.dashArray',
'properties._storage_options.zoomTo'
];
},
setStyle: function (options) {
options = options || {};
var option;
for (var idx in this.styleOptions) {
option = this.styleOptions[idx];
options[option] = this.getOption(option);
}
if (options.interactive) this.options.pointerEvents = 'visiblePainted';
else this.options.pointerEvents = 'stroke';
this.parentClass.prototype.setStyle.call(this, options);
},
_redraw: function () {
this.setStyle();
this.resetTooltip();
},
onAdd: function (map) {
this._container = null;
this.setStyle();
// Show tooltip again when Leaflet.label allow static label on path.
// cf https://github.com/Leaflet/Leaflet/pull/3952
// this.map.on('showmeasure', this.showMeasureTooltip, this);
// this.map.on('hidemeasure', this.removeTooltip, this);
this.parentClass.prototype.onAdd.call(this, map);
if (this.editing && this.editing.enabled()) this.editing.addHooks();
this.resetTooltip();
},
onRemove: function (map) {
// this.map.off('showmeasure', this.showMeasureTooltip, this);
// this.map.off('hidemeasure', this.removeTooltip, this);
if (this.editing && this.editing.enabled()) this.editing.removeHooks();
L.S.FeatureMixin.onRemove.call(this, map);
},
getBestZoom: function () {
if (this.options.zoomTo) return this.options.zoomTo;
var bounds = this.getBounds();
return this.map.getBoundsZoom(bounds, true);
},
endEdit: function () {
this.disableEdit();
L.S.FeatureMixin.endEdit.call(this);
},
_onMouseOver: function () {
if (this.map.measureTools && this.map.measureTools.enabled()) {
this.map.ui.tooltip({content: this.getMeasure(), anchor: this});
} else if (this.map.editEnabled && !this.map.editedFeature) {
this.map.ui.tooltip({content: L._('Click to edit'), anchor: this});
}
},
addInteractions: function () {
L.S.FeatureMixin.addInteractions.call(this);
this.on('mouseover', this._onMouseOver);
this.on('edit', this.makeDirty);
this.on('drag editable:drag', this._onDrag);
},
_onDrag: function () {
if (this._tooltip) this._tooltip.setLatLng(this.getCenter());
},
transferShape: function (at, to) {
var shape = this.enableEdit().deleteShapeAt(at);
this.disableEdit();
if (!shape) return;
to.enableEdit().appendShape(shape);
if (!this._latlngs.length || !this._latlngs[0].length) this.del();
},
isolateShape: function (at) {
if (!this.isMulti()) return;
var shape = this.enableEdit().deleteShapeAt(at);
this.disableEdit();
if (!shape) return;
var properties = this.cloneProperties();
var other = new (this instanceof L.S.Polyline ? L.S.Polyline : L.S.Polygon)(this.map, shape, {geojson: {properties: properties}});
this.datalayer.addLayer(other);
other.edit();
return other;
},
getContextMenuItems: function (e) {
var items = L.S.FeatureMixin.getContextMenuItems.call(this, e);
items.push({
text: L._('Display measure'),
callback: function () {
this.map.ui.alert({content: this.getMeasure(), level: 'info'})
},
context: this
})
if (this.map.editEnabled && !this.isReadOnly() && this.isMulti()) {
items = items.concat(this.getContextMenuMultiItems(e));
}
return items;
},
getContextMenuMultiItems: function (e) {
var items = ['-', {
text: L._('Remove shape from the multi'),
callback: function () {
this.enableEdit().deleteShapeAt(e.latlng);
},
context: this
}];
var shape = this.shapeAt(e.latlng);
if (this._latlngs.indexOf(shape) > 0) {
items.push({
text: L._('Make main shape'),
callback: function () {
this.enableEdit().deleteShape(shape);
this.editor.prependShape(shape);
},
context: this
});
}
return items;
},
getContextMenuEditItems: function (e) {
var items = L.S.FeatureMixin.getContextMenuEditItems.call(this, e);
items.push({
text: L._('Clone this feature'),
callback: this.clone,
context: this
});
if (this.map.editedFeature && this.isSameClass(this.map.editedFeature) && this.map.editedFeature !== this) {
items.push({
text: L._('Transfer shape to edited feature'),
callback: function () {
this.transferShape(e.latlng, this.map.editedFeature);
},
context: this
});
}
if (this.isMulti()) {
items.push({
text: L._('Extract shape to separate feature'),
callback: function () {
this.isolateShape(e.latlng, this.map.editedFeature);
},
context: this
});
}
return items;
},
getInplaceToolbarActions: function (e) {
var items = L.S.FeatureMixin.getInplaceToolbarActions.call(this, e);
if (this.isMulti()) {
items.push(L.S.DeleteShapeAction);
items.push(L.S.ExtractShapeFromMultiAction);
}
return items;
},
isOnScreen: function () {
var bounds = this.map.getBounds();
return bounds.overlaps(this.getBounds());
},
clone: function () {
var layer = this.datalayer.geojsonToFeatures(this.toGeoJSON());
layer.isDirty = true;
layer.edit();
return layer;
},
};
L.Storage.Polyline = L.Polyline.extend({
parentClass: L.Polyline,
includes: [L.Storage.FeatureMixin, L.Storage.PathMixin, L.Mixin.Events],
staticOptions: {
stroke: true,
fill: false
},
isSameClass: function (other) {
return other instanceof L.S.Polyline;
},
getClassName: function () {
return 'polyline';
},
getMeasure: function () {
var length = L.GeoUtil.lineLength(this.map, this._defaultShape());
return L.GeoUtil.readableDistance(length, this.map.measureTools.getMeasureUnit());
},
getContextMenuEditItems: function (e) {
var items = L.S.PathMixin.getContextMenuEditItems.call(this, e),
vertexClicked = e.vertex, index;
if (!this.isMulti()) {
items.push({
text: L._('Transform to polygon'),
callback: this.toPolygon,
context: this
});
}
if (vertexClicked) {
index = e.vertex.getIndex();
if (index !== 0 && index !== e.vertex.getLastIndex()) {
items.push({
text: L._('Split line'),
callback: e.vertex.split,
context: e.vertex
});
} else if (index === 0 || index === e.vertex.getLastIndex()) {
items.push({
text: L._('Continue line (Ctrl-click)'),
callback: e.vertex.continue,
context: e.vertex.continue
});
}
}
return items;
},
getContextMenuMultiItems: function (e) {
var items = L.S.PathMixin.getContextMenuMultiItems.call(this, e);
items.push({
text: L._('Merge lines'),
callback: this.mergeShapes,
context: this
});
return items;
},
toPolygon: function () {
var geojson = this.toGeoJSON();
geojson.geometry.type = 'Polygon';
geojson.geometry.coordinates = [L.Util.flattenCoordinates(geojson.geometry.coordinates)];
var polygon = this.datalayer.geojsonToFeatures(geojson);
polygon.edit();
this.del();
},
getAdvancedEditActions: function (container) {
L.Storage.FeatureMixin.getAdvancedEditActions.call(this, container);
var toPolygon = L.DomUtil.create('a', 'button storage-to-polygon', container);
toPolygon.href = '#';
toPolygon.innerHTML = L._('Transform to polygon');
L.DomEvent.on(toPolygon, 'click', this.toPolygon, this);
},
_mergeShapes: function (from, to) {
var toLeft = to[0],
toRight = to[to.length - 1],
fromLeft = from[0],
fromRight = from[from.length - 1],
l2ldistance = toLeft.distanceTo(fromLeft),
l2rdistance = toLeft.distanceTo(fromRight),
r2ldistance = toRight.distanceTo(fromLeft),
r2rdistance = toRight.distanceTo(fromRight),
toMerge;
if (l2rdistance < Math.min(l2ldistance, r2ldistance, r2rdistance)) {
toMerge = [from, to];
} else if (r2ldistance < Math.min(l2ldistance, l2rdistance, r2rdistance)) {
toMerge = [to, from];
} else if (r2rdistance < Math.min(l2ldistance, l2rdistance, r2ldistance)) {
from.reverse();
toMerge = [to, from];
} else {
from.reverse();
toMerge = [from, to];
}
var a = toMerge[0],
b = toMerge[1],
p1 = this.map.latLngToContainerPoint(a[a.length - 1]),
p2 = this.map.latLngToContainerPoint(b[0]),
tolerance = 5; // px on screen
if (Math.abs(p1.x - p2.x) <= tolerance && Math.abs(p1.y - p2.y) <= tolerance) {
a.pop();
}
return a.concat(b);
},
mergeShapes: function () {
if (!this.isMulti()) return;
var latlngs = this.getLatLngs();
if (!latlngs.length) return;
while (latlngs.length > 1) {
latlngs.splice(0, 2, this._mergeShapes(latlngs[1], latlngs[0]));
}
this.setLatLngs(latlngs[0]);
if (!this.editEnabled()) this.edit();
this.editor.reset();
this.isDirty = true;
},
isMulti: function () {
return !L.LineUtil.isFlat(this._latlngs) && this._latlngs.length > 1;
},
getVertexActions: function (e) {
var actions = L.S.FeatureMixin.getVertexActions.call(this, e),
index = e.vertex.getIndex();
if (index === 0 || index === e.vertex.getLastIndex()) actions.push(L.S.ContinueLineAction);
else actions.push(L.S.SplitLineAction);
return actions;
}
});
L.Storage.Polygon = L.Polygon.extend({
parentClass: L.Polygon,
includes: [L.Storage.FeatureMixin, L.Storage.PathMixin, L.Mixin.Events],
isSameClass: function (other) {
return other instanceof L.S.Polygon;
},
getClassName: function () {
return 'polygon';
},
getShapeOptions: function () {
var options = L.Storage.PathMixin.getShapeOptions();
options.push('properties._storage_options.stroke',
'properties._storage_options.fill',
'properties._storage_options.fillColor',
'properties._storage_options.fillOpacity'
);
return options;
},
getInteractionOptions: function () {
var options = [
['properties._storage_options.interactive', {handler: 'Switch', label: L._('Allow interactions'), helpEntries: 'interactive', inheritable: true}],
['properties._storage_options.outlink', {label: L._('Link to…'), helpEntries: 'outlink', placeholder: 'http://...', inheritable: true}],
['properties._storage_options.outlinkTarget', {handler: 'OutlinkTarget', label: L._('Open link in…'), inheritable: true}]
];
return options.concat(L.Storage.FeatureMixin.getInteractionOptions());
},
getMeasure: function () {
var area = L.GeoUtil.geodesicArea(this._defaultShape());
return L.GeoUtil.readableArea(area, this.map.measureTools.getMeasureUnit());
},
getContextMenuEditItems: function (e) {
var items = L.S.PathMixin.getContextMenuEditItems.call(this, e),
shape = this.shapeAt(e.latlng);
// No multi and no holes.
if (shape && !this.isMulti() && (L.LineUtil.isFlat(shape) || shape.length === 1)) {
items.push({
text: L._('Transform to lines'),
callback: this.toPolyline,
context: this
});
}
items.push({
text: L._('Start a hole here'),
callback: this.startHole,
context: this
});
return items;
},
startHole: function (e) {
this.enableEdit().newHole(e.latlng);
},
toPolyline: function () {
var geojson = this.toGeoJSON();
geojson.geometry.type = 'LineString';
geojson.geometry.coordinates = L.Util.flattenCoordinates(geojson.geometry.coordinates);
var polyline = this.datalayer.geojsonToFeatures(geojson);
polyline.edit();
this.del();
},
getAdvancedEditActions: function (container) {
L.Storage.FeatureMixin.getAdvancedEditActions.call(this, container);
var toPolyline = L.DomUtil.create('a', 'button storage-to-polyline', container);
toPolyline.href = '#';
toPolyline.innerHTML = L._('Transform to lines');
L.DomEvent.on(toPolyline, 'click', this.toPolyline, this);
},
isMulti: function () {
// Change me when Leaflet#3279 is merged.
return !L.LineUtil.isFlat(this._latlngs) && !L.LineUtil.isFlat(this._latlngs[0]) && this._latlngs.length > 1;
},
getInplaceToolbarActions: function (e) {
var items = L.S.PathMixin.getInplaceToolbarActions.call(this, e);
items.push(L.S.CreateHoleAction);
return items;
}
});