leaflet-routing-machine
Version:
359 lines (307 loc) • 9.18 kB
JavaScript
(function() {
'use strict';
var L = require('leaflet');
var GeocoderElement = require('./geocoder-element');
var Waypoint = require('./waypoint');
module.exports = (L.Layer || L.Class).extend({
includes: ((typeof L.Evented !== 'undefined' && L.Evented.prototype) || L.Mixin.Events),
options: {
dragStyles: [
{color: 'black', opacity: 0.15, weight: 9},
{color: 'white', opacity: 0.8, weight: 6},
{color: 'red', opacity: 1, weight: 2, dashArray: '7,12'}
],
draggableWaypoints: true,
routeWhileDragging: false,
addWaypoints: true,
reverseWaypoints: false,
addButtonClassName: '',
language: 'en',
createGeocoderElement: function(wp, i, nWps, plan) {
return new GeocoderElement(wp, i, nWps, plan);
},
createMarker: function(i, wp) {
var options = {
draggable: this.draggableWaypoints
},
marker = L.marker(wp.latLng, options);
return marker;
},
geocodersClassName: ''
},
initialize: function(waypoints, options) {
L.Util.setOptions(this, options);
this._waypoints = [];
this.setWaypoints(waypoints);
},
isReady: function() {
var i;
for (i = 0; i < this._waypoints.length; i++) {
if (!this._waypoints[i].latLng) {
return false;
}
}
return true;
},
getWaypoints: function() {
var i,
wps = [];
for (i = 0; i < this._waypoints.length; i++) {
wps.push(this._waypoints[i]);
}
return wps;
},
setWaypoints: function(waypoints) {
var args = [0, this._waypoints.length].concat(waypoints);
this.spliceWaypoints.apply(this, args);
return this;
},
spliceWaypoints: function() {
var args = [arguments[0], arguments[1]],
i;
for (i = 2; i < arguments.length; i++) {
args.push(arguments[i] && arguments[i].hasOwnProperty('latLng') ? arguments[i] : new Waypoint(arguments[i]));
}
[].splice.apply(this._waypoints, args);
// Make sure there's always at least two waypoints
while (this._waypoints.length < 2) {
this.spliceWaypoints(this._waypoints.length, 0, null);
}
this._updateMarkers();
this._fireChanged.apply(this, args);
},
onAdd: function(map) {
this._map = map;
this._updateMarkers();
},
onRemove: function() {
var i;
this._removeMarkers();
if (this._newWp) {
for (i = 0; i < this._newWp.lines.length; i++) {
this._map.removeLayer(this._newWp.lines[i]);
}
}
delete this._map;
},
createGeocoders: function() {
var container = L.DomUtil.create('div', 'leaflet-routing-geocoders ' + this.options.geocodersClassName),
waypoints = this._waypoints,
addWpBtn,
reverseBtn;
this._geocoderContainer = container;
this._geocoderElems = [];
if (this.options.addWaypoints) {
addWpBtn = L.DomUtil.create('button', 'leaflet-routing-add-waypoint ' + this.options.addButtonClassName, container);
addWpBtn.setAttribute('type', 'button');
L.DomEvent.addListener(addWpBtn, 'click', function() {
this.spliceWaypoints(waypoints.length, 0, null);
}, this);
}
if (this.options.reverseWaypoints) {
reverseBtn = L.DomUtil.create('button', 'leaflet-routing-reverse-waypoints', container);
reverseBtn.setAttribute('type', 'button');
L.DomEvent.addListener(reverseBtn, 'click', function() {
this._waypoints.reverse();
this.setWaypoints(this._waypoints);
}, this);
}
this._updateGeocoders();
this.on('waypointsspliced', this._updateGeocoders);
return container;
},
_createGeocoder: function(i) {
var geocoder = this.options.createGeocoderElement(this._waypoints[i], i, this._waypoints.length, this.options);
geocoder
.on('delete', function() {
if (i > 0 || this._waypoints.length > 2) {
this.spliceWaypoints(i, 1);
} else {
this.spliceWaypoints(i, 1, new Waypoint());
}
}, this)
.on('geocoded', function(e) {
this._updateMarkers();
this._fireChanged();
this._focusGeocoder(i + 1);
this.fire('waypointgeocoded', {
waypointIndex: i,
waypoint: e.waypoint
});
}, this)
.on('reversegeocoded', function(e) {
this.fire('waypointgeocoded', {
waypointIndex: i,
waypoint: e.waypoint
});
}, this);
return geocoder;
},
_updateGeocoders: function() {
var elems = [],
i,
geocoderElem;
for (i = 0; i < this._geocoderElems.length; i++) {
this._geocoderContainer.removeChild(this._geocoderElems[i].getContainer());
}
for (i = this._waypoints.length - 1; i >= 0; i--) {
geocoderElem = this._createGeocoder(i);
this._geocoderContainer.insertBefore(geocoderElem.getContainer(), this._geocoderContainer.firstChild);
elems.push(geocoderElem);
}
this._geocoderElems = elems.reverse();
},
_removeMarkers: function() {
var i;
if (this._markers) {
for (i = 0; i < this._markers.length; i++) {
if (this._markers[i]) {
this._map.removeLayer(this._markers[i]);
}
}
}
this._markers = [];
},
_updateMarkers: function() {
var i,
m;
if (!this._map) {
return;
}
this._removeMarkers();
for (i = 0; i < this._waypoints.length; i++) {
if (this._waypoints[i].latLng) {
m = this.options.createMarker(i, this._waypoints[i], this._waypoints.length);
if (m) {
m.addTo(this._map);
if (this.options.draggableWaypoints) {
this._hookWaypointEvents(m, i);
}
}
} else {
m = null;
}
this._markers.push(m);
}
},
_fireChanged: function() {
this.fire('waypointschanged', {waypoints: this.getWaypoints()});
if (arguments.length >= 2) {
this.fire('waypointsspliced', {
index: Array.prototype.shift.call(arguments),
nRemoved: Array.prototype.shift.call(arguments),
added: arguments
});
}
},
_hookWaypointEvents: function(m, i, trackMouseMove) {
var eventLatLng = function(e) {
return trackMouseMove ? e.latlng : e.target.getLatLng();
},
dragStart = L.bind(function(e) {
this.fire('waypointdragstart', {index: i, latlng: eventLatLng(e)});
}, this),
drag = L.bind(function(e) {
this._waypoints[i].latLng = eventLatLng(e);
this.fire('waypointdrag', {index: i, latlng: eventLatLng(e)});
}, this),
dragEnd = L.bind(function(e) {
this._waypoints[i].latLng = eventLatLng(e);
this._waypoints[i].name = '';
if (this._geocoderElems) {
this._geocoderElems[i].update(true);
}
this.fire('waypointdragend', {index: i, latlng: eventLatLng(e)});
this._fireChanged();
}, this),
mouseMove,
mouseUp;
if (trackMouseMove) {
mouseMove = L.bind(function(e) {
this._markers[i].setLatLng(e.latlng);
drag(e);
}, this);
mouseUp = L.bind(function(e) {
this._map.dragging.enable();
this._map.off('mouseup', mouseUp);
this._map.off('mousemove', mouseMove);
dragEnd(e);
}, this);
this._map.dragging.disable();
this._map.on('mousemove', mouseMove);
this._map.on('mouseup', mouseUp);
dragStart({latlng: this._waypoints[i].latLng});
} else {
m.on('dragstart', dragStart);
m.on('drag', drag);
m.on('dragend', dragEnd);
}
},
dragNewWaypoint: function(e) {
var newWpIndex = e.afterIndex + 1;
if (this.options.routeWhileDragging) {
this.spliceWaypoints(newWpIndex, 0, e.latlng);
this._hookWaypointEvents(this._markers[newWpIndex], newWpIndex, true);
} else {
this._dragNewWaypoint(newWpIndex, e.latlng);
}
},
_dragNewWaypoint: function(newWpIndex, initialLatLng) {
var wp = new Waypoint(initialLatLng),
prevWp = this._waypoints[newWpIndex - 1],
nextWp = this._waypoints[newWpIndex],
marker = this.options.createMarker(newWpIndex, wp, this._waypoints.length + 1),
lines = [],
draggingEnabled = this._map.dragging.enabled(),
mouseMove = L.bind(function(e) {
var i,
latLngs;
if (marker) {
marker.setLatLng(e.latlng);
}
for (i = 0; i < lines.length; i++) {
latLngs = lines[i].getLatLngs();
latLngs.splice(1, 1, e.latlng);
lines[i].setLatLngs(latLngs);
}
L.DomEvent.stop(e);
}, this),
mouseUp = L.bind(function(e) {
var i;
if (marker) {
this._map.removeLayer(marker);
}
for (i = 0; i < lines.length; i++) {
this._map.removeLayer(lines[i]);
}
this._map.off('mousemove', mouseMove);
this._map.off('mouseup', mouseUp);
this.spliceWaypoints(newWpIndex, 0, e.latlng);
if (draggingEnabled) {
this._map.dragging.enable();
}
L.DomEvent.stop(e);
}, this),
i;
if (marker) {
marker.addTo(this._map);
}
for (i = 0; i < this.options.dragStyles.length; i++) {
lines.push(L.polyline([prevWp.latLng, initialLatLng, nextWp.latLng],
this.options.dragStyles[i]).addTo(this._map));
}
if (draggingEnabled) {
this._map.dragging.disable();
}
this._map.on('mousemove', mouseMove);
this._map.on('mouseup', mouseUp);
},
_focusGeocoder: function(i) {
if (this._geocoderElems[i]) {
this._geocoderElems[i].focus();
} else {
document.activeElement.blur();
}
}
});
})();