borders/web/app/static/lib/Leaflet.Editable.js

1055 lines
30 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

L.Editable = L.Class.extend({
includes: [L.Mixin.Events],
statics: {
FORWARD: 1,
BACKWARD: -1
},
options: {
zIndex: 1000,
polygonClass: L.Polygon,
polylineClass: L.Polyline,
markerClass: L.Marker,
drawingCSSClass: 'leaflet-editable-drawing'
},
initialize: function (map, options) {
L.setOptions(this, options);
this._lastZIndex = this.options.zIndex;
this.map = map;
this.editLayer = this.createEditLayer();
this.featuresLayer = this.createFeaturesLayer();
this.newClickHandler = this.createNewClickHandler();
this.forwardLineGuide = this.createLineGuide();
this.backwardLineGuide = this.createLineGuide();
},
fireAndForward: function (type, e) {
e = e || {};
e.editTools = this;
this.fire(type, e);
this.map.fire(type, e);
},
createLineGuide: function () {
var options = L.extend({dashArray: '5,10', weight: 1}, this.options.lineGuideOptions);
return L.polyline([], options);
},
createVertexIcon: function (options) {
return L.Browser.touch ? new L.Editable.TouchVertexIcon(options) : new L.Editable.VertexIcon(options);
},
createNewClickHandler: function () {
return L.marker(this.map.getCenter(), {
icon: this.createVertexIcon({className: 'leaflet-div-icon leaflet-drawing-icon'}),
opacity: 0,
zIndexOffset: this._lastZIndex
});
},
createEditLayer: function () {
return this.options.editLayer || new L.LayerGroup().addTo(this.map);
},
createFeaturesLayer: function () {
return this.options.featuresLayer || new L.LayerGroup().addTo(this.map);
},
moveForwardLineGuide: function (latlng) {
if (this.forwardLineGuide._latlngs.length) {
this.forwardLineGuide._latlngs[1] = latlng;
this.forwardLineGuide.redraw();
}
},
moveBackwardLineGuide: function (latlng) {
if (this.backwardLineGuide._latlngs.length) {
this.backwardLineGuide._latlngs[1] = latlng;
this.backwardLineGuide.redraw();
}
},
anchorForwardLineGuide: function (latlng) {
this.forwardLineGuide._latlngs[0] = latlng;
this.forwardLineGuide.redraw();
},
anchorBackwardLineGuide: function (latlng) {
this.backwardLineGuide._latlngs[0] = latlng;
this.backwardLineGuide.redraw();
},
attachForwardLineGuide: function () {
this.editLayer.addLayer(this.forwardLineGuide);
},
attachBackwardLineGuide: function () {
this.editLayer.addLayer(this.backwardLineGuide);
},
detachForwardLineGuide: function () {
this.forwardLineGuide._latlngs = [];
this.editLayer.removeLayer(this.forwardLineGuide);
},
detachBackwardLineGuide: function () {
this.backwardLineGuide._latlngs = [];
this.editLayer.removeLayer(this.backwardLineGuide);
},
updateNewClickHandlerZIndex: function () {
this._lastZIndex += 2;
this.newClickHandler.setZIndexOffset(this._lastZIndex);
},
registerForDrawing: function (editor) {
this.map.on('mousemove touchmove', editor.onMouseMove, editor);
if (this._drawingEditor) this.unregisterForDrawing(this._drawingEditor);
this._drawingEditor = editor;
this.editLayer.addLayer(this.newClickHandler);
this.newClickHandler.on('click', editor.onNewClickHandlerClicked, editor);
if (L.Browser.touch) this.map.on('click', editor.onTouch, editor);
L.DomUtil.addClass(this.map._container, this.options.drawingCSSClass);
this.updateNewClickHandlerZIndex();
},
unregisterForDrawing: function (editor) {
editor = editor || this._drawingEditor;
this.editLayer.removeLayer(this.newClickHandler);
if (!editor) return;
this.map.off('mousemove touchmove', editor.onMouseMove, editor);
this.newClickHandler.off('click', editor.onNewClickHandlerClicked, editor);
if (L.Browser.touch) this.map.off('click', editor.onTouch, editor);
if (editor !== this._drawingEditor) return;
delete this._drawingEditor;
if (editor.drawing) editor.cancelDrawing();
L.DomUtil.removeClass(this.map._container, this.options.drawingCSSClass);
},
stopDrawing: function () {
this.unregisterForDrawing();
},
connectCreatedToMap: function (layer) {
return this.featuresLayer.addLayer(layer);
},
startPolyline: function (latlng) {
var line = this.createPolyline([]);
this.connectCreatedToMap(line);
var editor = line.enableEdit();
editor.startDrawingForward();
if (latlng) editor.newPointForward(latlng);
return line;
},
startPolygon: function (latlng) {
var polygon = this.createPolygon([]);
this.connectCreatedToMap(polygon);
var editor = polygon.enableEdit();
editor.startDrawingForward();
if (latlng) editor.newPointForward(latlng);
return polygon;
},
startMarker: function (latlng) {
latlng = latlng || this.map.getCenter();
var marker = this.createMarker(latlng);
this.connectCreatedToMap(marker);
var editor = marker.enableEdit();
editor.startDrawing();
return marker;
},
startHole: function (editor, latlng) {
editor.newHole(latlng);
},
extendMultiPolygon: function (multi) {
var polygon = this.createPolygon([]);
multi.addLayer(polygon);
polygon.multi = multi;
var editor = polygon.enableEdit();
editor.startDrawingForward();
return polygon;
},
createPolyline: function (latlngs) {
var line = new this.options.polylineClass(latlngs, {editOptions: {editTools: this}});
this.fireAndForward('editable:created', {layer: line});
return line;
},
createPolygon: function (latlngs) {
var polygon = new this.options.polygonClass(latlngs, {editOptions: {editTools: this}});
this.fireAndForward('editable:created', {layer: polygon});
return polygon;
},
createMarker: function (latlng) {
var marker = new this.options.markerClass(latlng, {editOptions: {editTools: this}});
this.fireAndForward('editable:created', {layer: marker});
return marker;
}
});
L.Map.addInitHook(function () {
this.whenReady(function () {
if (this.options.editable) {
this.editTools = new L.Editable(this, this.options.editOptions);
}
});
});
L.Editable.VertexIcon = L.DivIcon.extend({
options: {
iconSize: new L.Point(8, 8)
}
});
L.Editable.TouchVertexIcon = L.Editable.VertexIcon.extend({
options: {
iconSize: new L.Point(20, 20)
}
});
L.Editable.VertexMarker = L.Marker.extend({
options: {
draggable: true,
className: 'leaflet-div-icon leaflet-vertex-icon'
},
initialize: function (latlng, latlngs, editor, options) {
this.latlng = latlng;
this.latlngs = latlngs;
this.editor = editor;
L.Marker.prototype.initialize.call(this, latlng, options);
this.options.icon = this.editor.tools.createVertexIcon({className: this.options.className});
this.latlng.__vertex = this;
this.editor.editLayer.addLayer(this);
this.setZIndexOffset(editor.tools._lastZIndex + 1);
},
onAdd: function (map) {
L.Marker.prototype.onAdd.call(this, map);
this.on('drag', this.onDrag);
this.on('dragstart', this.onDragStart);
this.on('dragend', this.onDragEnd);
this.on('click', this.onClick);
this.on('contextmenu', this.onContextMenu);
this.on('mousedown touchstart', this.onMouseDown);
this.addMiddleMarkers();
},
onRemove: function (map) {
if (this.middleMarker) this.middleMarker.delete();
delete this.latlng.__vertex;
this.off('drag', this.onDrag);
this.off('dragstart', this.onDragStart);
this.off('dragend', this.onDragEnd);
this.off('click', this.onClick);
this.off('contextmenu', this.onContextMenu);
this.off('mousedown touchstart', this.onMouseDown);
L.Marker.prototype.onRemove.call(this, map);
},
onDrag: function (e) {
e.vertex = this;
this.editor.onVertexMarkerDrag(e);
var iconPos = L.DomUtil.getPosition(this._icon),
latlng = this._map.layerPointToLatLng(iconPos);
this.latlng.lat = latlng.lat;
this.latlng.lng = latlng.lng;
this.editor.refresh();
if (this.middleMarker) {
this.middleMarker.updateLatLng();
}
var next = this.getNext();
if (next && next.middleMarker) {
next.middleMarker.updateLatLng();
}
},
onDragStart: function (e) {
e.vertex = this;
this.editor.onVertexMarkerDragStart(e);
},
onDragEnd: function (e) {
e.vertex = this;
this.editor.onVertexMarkerDragEnd(e);
},
onClick: function (e) {
e.vertex = this;
this.editor.onVertexMarkerClick(e);
},
onContextMenu: function (e) {
e.vertex = this;
this.editor.onVertexMarkerContextMenu(e);
},
onMouseDown: function (e) {
e.vertex = this;
this.editor.onVertexMarkerMouseDown(e);
},
delete: function () {
var next = this.getNext(); // Compute before changing latlng
this.latlngs.splice(this.latlngs.indexOf(this.latlng), 1);
this.editor.editLayer.removeLayer(this);
this.editor.onVertexDeleted({latlng: this.latlng, vertex: this});
if (next) next.resetMiddleMarker();
},
getIndex: function () {
return this.latlngs.indexOf(this.latlng);
},
getLastIndex: function () {
return this.latlngs.length - 1;
},
getPrevious: function () {
if (this.latlngs.length < 2) return;
var index = this.getIndex(),
previousIndex = index - 1;
if (index === 0 && this.editor.CLOSED) previousIndex = this.getLastIndex();
var previous = this.latlngs[previousIndex];
if (previous) return previous.__vertex;
},
getNext: function () {
if (this.latlngs.length < 2) return;
var index = this.getIndex(),
nextIndex = index + 1;
if (index === this.getLastIndex() && this.editor.CLOSED) nextIndex = 0;
var next = this.latlngs[nextIndex];
if (next) return next.__vertex;
},
addMiddleMarker: function (previous) {
previous = previous || this.getPrevious();
if (previous && !this.middleMarker) this.middleMarker = this.editor.addMiddleMarker(previous, this, this.latlngs, this.editor);
},
addMiddleMarkers: function () {
if (this.editor.tools.options.skipMiddleMarkers) return;
var previous = this.getPrevious();
if (previous) {
this.addMiddleMarker(previous);
}
var next = this.getNext();
if (next) {
next.resetMiddleMarker();
}
},
resetMiddleMarker: function () {
if (this.middleMarker) this.middleMarker.delete();
this.addMiddleMarker();
},
_initInteraction: function () {
L.Marker.prototype._initInteraction.call(this);
L.DomEvent.on(this._icon, 'touchstart', function (e) {this._fireMouseEvent(e);}, this);
}
});
L.Editable.mergeOptions({
vertexMarkerClass: L.Editable.VertexMarker
});
L.Editable.MiddleMarker = L.Marker.extend({
options: {
opacity: 0.5,
className: 'leaflet-div-icon leaflet-middle-icon'
},
initialize: function (left, right, latlngs, editor, options) {
this.left = left;
this.right = right;
this.editor = editor;
this.latlngs = latlngs;
L.Marker.prototype.initialize.call(this, this.computeLatLng(), options);
this._opacity = this.options.opacity;
this.options.icon = this.editor.tools.createVertexIcon({className: this.options.className});
this.editor.editLayer.addLayer(this);
this.setVisibility();
},
setVisibility: function () {
var leftPoint = this._map.latLngToContainerPoint(this.left.latlng),
rightPoint = this._map.latLngToContainerPoint(this.right.latlng),
size = L.point(this.options.icon.options.iconSize);
if (leftPoint.distanceTo(rightPoint) < size.x * 3) {
this.hide();
} else {
this.show();
}
},
show: function () {
this.setOpacity(this._opacity);
},
hide: function () {
this.setOpacity(0);
},
updateLatLng: function () {
this.setLatLng(this.computeLatLng());
this.setVisibility();
},
computeLatLng: function () {
var leftPoint = this.editor.map.latLngToContainerPoint(this.left.latlng),
rightPoint = this.editor.map.latLngToContainerPoint(this.right.latlng),
y = (leftPoint.y + rightPoint.y) / 2,
x = (leftPoint.x + rightPoint.x) / 2;
return this.editor.map.containerPointToLatLng([x, y]);
},
onAdd: function (map) {
L.Marker.prototype.onAdd.call(this, map);
this.on('mousedown touchstart', this.onMouseDown);
map.on('zoomend', this.setVisibility, this);
},
onRemove: function (map) {
delete this.right.middleMarker;
this.off('mousedown touchstart', this.onMouseDown);
map.off('zoomend', this.setVisibility, this);
L.Marker.prototype.onRemove.call(this, map);
},
onMouseDown: function (e) {
this.editor.onMiddleMarkerMouseDown(e, this);
this.latlngs.splice(this.index(), 0, e.latlng);
this.editor.refresh();
var marker = this.editor.addVertexMarker(e.latlng, this.latlngs);
marker.dragging._draggable._onDown(e.originalEvent); // Transfer ongoing dragging to real marker
this.delete();
},
delete: function () {
this.editor.editLayer.removeLayer(this);
},
index: function () {
return this.latlngs.indexOf(this.right.latlng);
},
_initInteraction: function () {
L.Marker.prototype._initInteraction.call(this);
L.DomEvent.on(this._icon, 'touchstart', function (e) {this._fireMouseEvent(e);}, this);
}
});
L.Editable.mergeOptions({
middleMarkerClass: L.Editable.MiddleMarker
});
L.Editable.BaseEditor = L.Class.extend({
initialize: function (map, feature, options) {
L.setOptions(this, options);
this.map = map;
this.feature = feature;
this.feature.editor = this;
this.editLayer = new L.LayerGroup();
this.tools = this.options.editTools || map.editTools;
},
enable: function () {
if (this._enabled) return this;
this.tools.editLayer.addLayer(this.editLayer);
this.onEnable();
this._enabled = true;
this.feature.on('remove', this.disable, this);
return this;
},
disable: function () {
this.feature.off('remove', this.disable, this);
this.editLayer.clearLayers();
this.tools.editLayer.removeLayer(this.editLayer);
this.onDisable();
delete this._enabled;
if (this.drawing) this.cancelDrawing();
return this;
},
fireAndForward: function (type, e) {
e = e || {};
e.layer = this.feature;
this.feature.fire(type, e);
if (this.feature.multi) this.feature.multi.fire(type, e);
this.tools.fireAndForward(type, e);
},
onEnable: function () {
this.fireAndForward('editable:enable');
},
onDisable: function () {
this.fireAndForward('editable:disable');
},
onEditing: function () {
this.fireAndForward('editable:editing');
},
onStartDrawing: function () {
this.fireAndForward('editable:drawing:start');
},
onEndDrawing: function () {
this.fireAndForward('editable:drawing:end');
},
onCancelDrawing: function () {
this.fireAndForward('editable:drawing:cancel');
},
onCommitDrawing: function () {
this.fireAndForward('editable:drawing:commit');
},
startDrawing: function () {
if (!this.drawing) this.drawing = L.Editable.FORWARD;
this.tools.registerForDrawing(this);
this.onStartDrawing();
},
commitDrawing: function () {
this.onCommitDrawing();
this.endDrawing();
},
cancelDrawing: function () {
this.onCancelDrawing();
this.endDrawing();
},
endDrawing: function () {
this.drawing = false;
this.tools.unregisterForDrawing(this);
this.onEndDrawing();
},
onMouseMove: function (e) {
if (this.drawing) {
this.tools.newClickHandler.setLatLng(e.latlng);
}
},
onTouch: function (e) {
this.onMouseMove(e);
if (this.drawing) this.tools.newClickHandler._fireMouseEvent(e);
},
onNewClickHandlerClicked: function (e) {
this.fireAndForward('editable:drawing:click', e);
},
isNewClickValid: function (latlng) {
return true;
}
});
L.Editable.MarkerEditor = L.Editable.BaseEditor.extend({
enable: function () {
if (this._enabled) return this;
L.Editable.BaseEditor.prototype.enable.call(this);
this.feature.dragging.enable();
this.feature.on('dragstart', this.onEditing, this);
return this;
},
disable: function () {
L.Editable.BaseEditor.prototype.disable.call(this);
this.feature.dragging.disable();
this.feature.off('dragstart', this.onEditing, this);
return this;
},
onMouseMove: function (e) {
if (this.drawing) {
L.Editable.BaseEditor.prototype.onMouseMove.call(this, e);
this.feature.setLatLng(e.latlng);
this.tools.newClickHandler._bringToFront();
}
},
onNewClickHandlerClicked: function (e) {
if (!this.isNewClickValid(e.latlng)) return;
// Send event before finishing drawing
L.Editable.BaseEditor.prototype.onNewClickHandlerClicked.call(this, e);
this.commitDrawing();
}
});
L.Editable.PathEditor = L.Editable.BaseEditor.extend({
CLOSED: false,
MIN_VERTEX: 2,
enable: function () {
if (this._enabled) return this;
L.Editable.BaseEditor.prototype.enable.call(this);
if (this.feature) {
this.initVertexMarkers();
}
return this;
},
disable: function () {
return L.Editable.BaseEditor.prototype.disable.call(this);
},
initVertexMarkers: function () {
// groups can be only latlngs (for polyline or symple polygon,
// or latlngs plus many holes, in case of a complex polygon)
var latLngGroups = this.getLatLngsGroups();
for (var i = 0; i < latLngGroups.length; i++) {
this.addVertexMarkers(latLngGroups[i]);
}
},
getLatLngsGroups: function () {
return [this.getLatLngs()];
},
getLatLngs: function () {
return this.feature.getLatLngs();
},
reset: function () {
this.editLayer.clearLayers();
this.initVertexMarkers();
},
addVertexMarker: function (latlng, latlngs) {
return new this.tools.options.vertexMarkerClass(latlng, latlngs, this);
},
addVertexMarkers: function (latlngs) {
for (var i = 0; i < latlngs.length; i++) {
this.addVertexMarker(latlngs[i], latlngs);
}
},
addMiddleMarker: function (left, right, latlngs) {
return new this.tools.options.middleMarkerClass(left, right, latlngs, this);
},
onVertexMarkerClick: function (e) {
var index = e.vertex.getIndex();
if (e.originalEvent.ctrlKey) {
this.onVertexMarkerCtrlClick(e);
} else if (e.originalEvent.altKey) {
this.onVertexMarkerAltClick(e);
} else if (e.originalEvent.shiftKey) {
this.onVertexMarkerShiftClick(e);
} else if (index >= 1 && index === e.vertex.getLastIndex() && this.drawing === L.Editable.FORWARD) {
this.commitDrawing();
} else if (index === 0 && this.drawing === L.Editable.BACKWARD && this._drawnLatLngs.length >= this.MIN_VERTEX) {
this.commitDrawing();
} else if (index === 0 && this.drawing === L.Editable.FORWARD && this._drawnLatLngs.length >= this.MIN_VERTEX && this.CLOSED) {
this.commitDrawing(); // Allow to close on first point also for polygons
} else {
this.onVertexRawMarkerClick(e);
}
},
onVertexRawMarkerClick: function (e) {
if (!this.vertexCanBeDeleted(e.vertex)) return;
e.vertex.delete();
this.refresh();
},
vertexCanBeDeleted: function (vertex) {
return vertex.latlngs.length > this.MIN_VERTEX;
},
onVertexDeleted: function (e) {
this.fireAndForward('editable:vertex:deleted', e);
},
onVertexMarkerCtrlClick: function (e) {
this.fireAndForward('editable:vertex:ctrlclick', e);
},
onVertexMarkerShiftClick: function (e) {
this.fireAndForward('editable:vertex:shiftclick', e);
},
onVertexMarkerAltClick: function (e) {
this.fireAndForward('editable:vertex:altclick', e);
},
onVertexMarkerContextMenu: function (e) {
this.fireAndForward('editable:vertex:contextmenu', e);
},
onVertexMarkerMouseDown: function (e) {
this.fireAndForward('editable:vertex:mousedown', e);
},
onMiddleMarkerMouseDown: function (e) {
this.fireAndForward('editable:middlemarker:mousedown', e);
},
onVertexMarkerDrag: function (e) {
this.fireAndForward('editable:vertex:drag', e);
},
onVertexMarkerDragStart: function (e) {
this.fireAndForward('editable:vertex:dragstart', e);
},
onVertexMarkerDragEnd: function (e) {
this.fireAndForward('editable:vertex:dragend', e);
},
startDrawing: function () {
if (!this._drawnLatLngs) this._drawnLatLngs = this.getLatLngs();
L.Editable.BaseEditor.prototype.startDrawing.call(this);
},
startDrawingForward: function () {
this.startDrawing();
this.tools.attachForwardLineGuide();
},
endDrawing: function () {
L.Editable.BaseEditor.prototype.endDrawing.call(this);
this.tools.detachForwardLineGuide();
this.tools.detachBackwardLineGuide();
delete this._drawnLatLngs;
},
addLatLng: function (latlng) {
if (this.drawing === L.Editable.FORWARD) this._drawnLatLngs.push(latlng);
else this._drawnLatLngs.unshift(latlng);
this.refresh();
this.addVertexMarker(latlng, this._drawnLatLngs);
},
newPointForward: function (latlng) {
this.addLatLng(latlng);
this.tools.anchorForwardLineGuide(latlng);
if (!this.tools.backwardLineGuide._latlngs[0]) {
this.tools.anchorBackwardLineGuide(latlng);
}
},
newPointBackward: function (latlng) {
this.addLatLng(latlng);
this.tools.anchorBackwardLineGuide(latlng);
},
onNewClickHandlerClicked: function (e) {
if (!this.isNewClickValid(e.latlng)) return;
if (this.drawing === L.Editable.FORWARD) this.newPointForward(e.latlng);
else this.newPointBackward(e.latlng);
L.Editable.BaseEditor.prototype.onNewClickHandlerClicked.call(this, e);
},
onMouseMove: function (e) {
if (this.drawing) {
L.Editable.BaseEditor.prototype.onMouseMove.call(this, e);
this.tools.moveForwardLineGuide(e.latlng);
this.tools.moveBackwardLineGuide(e.latlng);
}
},
refresh: function () {
this.feature.redraw();
this.onEditing();
}
});
L.Editable.PolylineEditor = L.Editable.PathEditor.extend({
startDrawingBackward: function () {
this.drawing = L.Editable.BACKWARD;
this.startDrawing();
this.tools.attachBackwardLineGuide();
},
continueBackward: function () {
this.tools.anchorBackwardLineGuide(this.getFirstLatLng());
this.startDrawingBackward();
},
continueForward: function () {
this.tools.anchorForwardLineGuide(this.getLastLatLng());
this.startDrawingForward();
},
getLastLatLng: function () {
return this.getLatLngs()[this.getLatLngs().length - 1];
},
getFirstLatLng: function () {
return this.getLatLngs()[0];
}
});
L.Editable.PolygonEditor = L.Editable.PathEditor.extend({
CLOSED: true,
MIN_VERTEX: 3,
getLatLngsGroups: function () {
var groups = L.Editable.PathEditor.prototype.getLatLngsGroups.call(this);
if (this.feature._holes) {
for (var i = 0; i < this.feature._holes.length; i++) {
groups.push(this.feature._holes[i]);
}
}
return groups;
},
startDrawingForward: function () {
L.Editable.PathEditor.prototype.startDrawingForward.call(this);
this.tools.attachBackwardLineGuide();
},
addNewEmptyHole: function () {
var holes = Array();
if (!this.feature._holes) {
this.feature._holes = [];
}
this.feature._holes.push(holes);
return holes;
},
newHole: function (latlng) {
this._drawnLatLngs = this.addNewEmptyHole();
this.startDrawingForward();
if (latlng) this.newPointForward(latlng);
},
checkContains: function (latlng) {
return this.feature._containsPoint(this.map.latLngToLayerPoint(latlng));
},
vertexCanBeDeleted: function (vertex) {
if (vertex.latlngs === this.getLatLngs()) return L.Editable.PathEditor.prototype.vertexCanBeDeleted.call(this, vertex);
else return true; // Holes can be totally deleted without removing the layer itself
},
isNewClickValid: function (latlng) {
if (this._drawnLatLngs !== this.getLatLngs()) return this.checkContains(latlng);
return true;
},
onVertexDeleted: function (e) {
L.Editable.PathEditor.prototype.onVertexDeleted.call(this, e);
if (!e.vertex.latlngs.length && e.vertex.latlngs !== this.getLatLngs()) {
this.feature._holes.splice(this.feature._holes.indexOf(e.vertex.latlngs), 1);
}
}
});
L.Map.mergeOptions({
polylineEditorClass: L.Editable.PolylineEditor
});
L.Map.mergeOptions({
polygonEditorClass: L.Editable.PolygonEditor
});
L.Map.mergeOptions({
markerEditorClass: L.Editable.MarkerEditor
});
var EditableMixin = {
createEditor: function (map) {
map = map || this._map;
var Klass = this.options.editorClass || this.getEditorClass(map);
return new Klass(map, this, this.options.editOptions);
},
enableEdit: function () {
if (!this.editor) this.createEditor();
if (this.multi) this.multi.onEditEnabled();
return this.editor.enable();
},
editEnabled: function () {
return this.editor && this.editor._enabled;
},
disableEdit: function () {
if (this.editor) {
if (this.multi) this.multi.onEditDisabled();
this.editor.disable();
delete this.editor;
}
},
toggleEdit: function () {
if (this.editEnabled()) {
this.disableEdit();
} else {
this.enableEdit();
}
}
};
L.Polyline.include(EditableMixin);
L.Polygon.include(EditableMixin);
L.Marker.include(EditableMixin);
L.Polyline.include({
_containsPoint: function (p, closed) { // Copy-pasted from Leaflet
var i, j, k, len, len2, dist, part,
w = this.options.weight / 2;
if (L.Browser.touch) {
w += 10; // polyline click tolerance on touch devices
}
for (i = 0, len = this._parts.length; i < len; i++) {
part = this._parts[i];
for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
if (!closed && (j === 0)) {
continue;
}
dist = L.LineUtil.pointToSegmentDistance(p, part[k], part[j]);
if (dist <= w) {
return true;
}
}
}
return false;
},
getEditorClass: function (map) {
return map.options.polylineEditorClass;
}
});
L.Polygon.include({
_containsPoint: function (p) { // Copy-pasted from Leaflet
var inside = false,
part, p1, p2,
i, j, k,
len, len2;
// TODO optimization: check if within bounds first
if (L.Polyline.prototype._containsPoint.call(this, p, true)) {
// click on polygon border
return true;
}
// ray casting algorithm for detecting if point is in polygon
for (i = 0, len = this._parts.length; i < len; i++) {
part = this._parts[i];
for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
p1 = part[j];
p2 = part[k];
if (((p1.y > p.y) !== (p2.y > p.y)) &&
(p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) {
inside = !inside;
}
}
}
return inside;
},
getEditorClass: function (map) {
return map.options.polygonEditorClass;
}
});
L.Marker.include({
getEditorClass: function (map) {
return map.options.markerEditorClass;
}
});
var MultiEditableMixin = {
enableEdit: function () {
this.eachLayer(function(layer) {
layer.multi = this;
layer.enableEdit();
}, this);
},
disableEdit: function () {
this.eachLayer(function(layer) {
layer.disableEdit();
});
},
toggleEdit: function (e) {
if (!e.layer.editor) {
this.enableEdit(e);
} else {
this.disableEdit();
}
},
onEditEnabled: function () {
if (!this._editEnabled) {
this._editEnabled = true;
this.fire('editable:multi:edit:enabled');
}
},
onEditDisabled: function () {
if (this._editEnabled) {
this._editEnabled = false;
this.fire('editable:multi:edit:disabled');
}
},
editEnabled: function () {
return !!this._editEnabled;
}
};
L.MultiPolygon.include(MultiEditableMixin);
L.MultiPolyline.include(MultiEditableMixin);