diff --git a/ground/views/indexTemplate.php b/ground/views/indexTemplate.php
index e6d7fcd..20ee5b7 100644
--- a/ground/views/indexTemplate.php
+++ b/ground/views/indexTemplate.php
@@ -13,8 +13,8 @@
-
Мои маршруты
-
+
+
+
Мои маршруты
@@ -73,10 +68,13 @@
У вас пока нет сохраненных маршрутов.
-
+
+
+
-
= $user ? '' : self::renderPartial('modals/login.php', null, true) ?>
= $user ? self::renderPartial('modals/addPoint.php', array('categories' => $categories), true) : '' ?>
= self::renderPartial('modals/categories.php', array('categories' => $categories), true) ?>
diff --git a/public/css/leaflet-routing-machine.css b/public/css/leaflet-routing-machine.css
new file mode 100644
index 0000000..966f811
--- /dev/null
+++ b/public/css/leaflet-routing-machine.css
@@ -0,0 +1,102 @@
+.leaflet-routing-container {
+ width: 320px;
+ background-color: white;
+ padding-top: 4px;
+}
+
+.leaflet-routing-container h2 {
+ font-size: 14px;
+}
+
+.leaflet-routing-container h3 {
+ font-size: 12px;
+ font-weight: normal;
+}
+
+.leaflet-routing-alt, .leaflet-routing-geocoders {
+ padding: 6px;
+ margin-top: 2px;
+ margin-bottom: 6px;
+ border-bottom: 1px solid #ccc;
+ max-height: 320px;
+ overflow-y: auto;
+ transition: all 0.2s ease;
+}
+
+.leaflet-bar .leaflet-routing-alt:last-child {
+ border-bottom: none;
+}
+
+.leaflet-routing-alt-minimized {
+ color: #888;
+ max-height: 64px;
+ overflow: hidden;
+ cursor: pointer;
+}
+
+.leaflet-routing-alt table {
+ border-collapse: collapse;
+}
+
+.leaflet-routing-alt tr:hover {
+ background-color: #eee;
+ cursor: pointer;
+}
+
+.leaflet-routing-alt::-webkit-scrollbar {
+ width: 8px;
+}
+
+.leaflet-routing-alt::-webkit-scrollbar-track {
+ border-radius: 2px;
+ background-color: #eee;
+}
+
+.leaflet-routing-alt::-webkit-scrollbar-thumb {
+ border-radius: 2px;
+ background-color: #888;
+}
+
+.leaflet-routing-geocoders input {
+ width: 292px;
+ padding: 4px;
+ border: 1px solid #ccc;
+}
+
+.leaflet-routing-geocoders button {
+ font: bold 18px 'Lucida Console', Monaco, monospace;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ background-color: white;
+ margin: 0 6px;
+ float: right;
+ cursor: pointer;
+ transition: background-color 0.2s ease;
+}
+
+.leaflet-routing-geocoders button:hover {
+ background-color: #eee;
+}
+
+.leaflet-routing-geocoder-result {
+ position: absolute;
+ max-height: 0;
+ overflow: hidden;
+ transition: all 0.5s ease;
+}
+
+.leaflet-routing-geocoder-result table {
+ width: 100%;
+ border: 1px solid #ccc;
+ border-radius: 0 0 4px 4px;
+ background-color: white;
+ cursor: pointer;
+}
+
+.leaflet-routing-geocoder-result-open {
+ max-height: 800px;
+}
+
+.leaflet-routing-geocoder-selected, .leaflet-routing-geocoder-result tr:hover {
+ background-color: #eee;
+}
\ No newline at end of file
diff --git a/public/css/style.css b/public/css/style.css
index 29415e5..82c49c2 100644
--- a/public/css/style.css
+++ b/public/css/style.css
@@ -24,7 +24,7 @@ div.add {cursor: pointer;}
#wayPoints .wayPoint {cursor: pointer; border: solid 1px #999; padding: 2px 5px; margin-bottom: 3px; background-color: #fff;}
#wayPoints .wayPoint span {font-weight: bold; color: red;}
-#routes {margin-bottom: 10px; height: 200px; background-color: #ff9999;}
+#routes {margin-bottom: 10px; height: 200px;}
#routes .route {cursor: pointer; border: solid 1px #999; padding: 2px 5px; margin-bottom: 3px;}
#routes .route span {font-weight: bold; color: red;}
diff --git a/public/js/leaflet-routing-machine.js b/public/js/leaflet-routing-machine.js
new file mode 100644
index 0000000..f528762
--- /dev/null
+++ b/public/js/leaflet-routing-machine.js
@@ -0,0 +1,1207 @@
+// Packaging/modules magic dance. This code is inserted before all other
+// code when the dist is built.
+(function (factory) {
+ var L;
+ if (typeof define === 'function' && define.amd) {
+ // AMD
+ define(['leaflet'], factory);
+ } else if (typeof module !== 'undefined') {
+ // Node/CommonJS
+ L = require('leaflet');
+ module.exports = factory(L);
+ } else {
+ // Browser globals
+ if (typeof window.L === 'undefined')
+ throw 'Leaflet must be loaded first';
+ factory(window.L);
+ }
+}(function (L) {
+(function() {
+ 'use strict';
+
+ L.Routing = L.Routing || {};
+
+ L.Routing.Autocomplete = L.Class.extend({
+ options: {
+ timeout: 500
+ },
+
+ initialize: function(elem, callback, context, options) {
+ L.setOptions(this, options);
+
+ this._elem = elem;
+ this._resultFn = options.resultFn ? L.Util.bind(options.resultFn, options.resultContext) : null;
+ this._autocomplete = options.autocompleteFn ? L.Util.bind(options.autocompleteFn, options.autocompleteContext) : null;
+ this._selectFn = L.Util.bind(callback, context);
+ this._container = L.DomUtil.create('div', 'leaflet-routing-geocoder-result');
+ this._resultTable = L.DomUtil.create('table', '', this._container);
+
+ L.DomEvent.addListener(this._elem, 'keypress', this._keyPressed, this);
+ L.DomEvent.addListener(this._elem, 'keydown', this._keyDown, this);
+ L.DomEvent.addListener(this._elem, 'blur', function() {
+ if (this._isOpen) {
+ // TODO: setTimeout here looks like a hack, but is needed
+ // to get click events to fire before hiding the container.
+ setTimeout(L.Util.bind(function() { this.close(); }, this), 100);
+ }
+ }, this);
+ },
+
+ close: function() {
+ L.DomUtil.removeClass(this._container, 'leaflet-routing-geocoder-result-open');
+ this._isOpen = false;
+ },
+
+ _open: function() {
+ var sibling = this._elem.nextSibling;
+ if (!this._container.parentElement) {
+ if (sibling) {
+ this._elem.parentElement.insertBefore(this._container, sibling);
+ } else {
+ this._elem.parentElement.appendChild(this._container);
+ }
+ this._container.style.left = this._elem.offsetLeft + 'px';
+ this._container.style.top = (this._elem.offsetTop + this._elem.offsetHeight) + 'px';
+ this._container.style.width = this._elem.offsetWidth + 'px';
+ }
+
+ L.DomUtil.addClass(this._container, 'leaflet-routing-geocoder-result-open');
+ this._isOpen = true;
+ },
+
+ _setResults: function(results) {
+ var i,
+ tr,
+ td;
+
+ delete this._selection;
+ this._results = results;
+
+ while (this._resultTable.firstChild) {
+ this._resultTable.removeChild(this._resultTable.firstChild);
+ }
+
+ for (i = 0; i < results.length; i++) {
+ tr = L.DomUtil.create('tr', '', this._resultTable);
+ tr.setAttribute('data-result-index', i);
+ td = L.DomUtil.create('td', '', tr);
+ td.textContent = results[i].name;
+ L.DomEvent.addListener(td, 'click', this._resultSelected(results[i]), this);
+ }
+
+ if (results.length > 0) {
+ this._open();
+ // Select the first entry
+ this._select(1);
+ }
+ },
+
+ _resultSelected: function(r) {
+ return function() {
+ this.close();
+ this._selectFn(r);
+ };
+ },
+
+ _keyPressed: function(e) {
+ var index;
+
+ if (this._isOpen && e.keyCode === 13 && this._selection) {
+ index = parseInt(this._selection.getAttribute('data-result-index'), 10);
+ this._resultSelected(this._results[index]).call(this);
+ L.DomEvent.preventDefault(e);
+ return;
+ }
+
+ if (e.keyCode === 13) {
+ this._complete(this._resultFn, true);
+ }
+
+ if (this._autocomplete) {
+ if (this._timer) {
+ clearTimeout(this._timer);
+ }
+ this._timer = setTimeout(L.Util.bind(function() { this._complete(this._autocomplete); }, this),
+ this.options.timeout);
+ }
+ },
+
+ _select: function(dir) {
+ var sel = this._selection;
+ if (sel) {
+ L.DomUtil.removeClass(sel.firstChild, 'leaflet-routing-geocoder-selected');
+ sel = sel[dir > 0 ? 'nextSibling' : 'previousSibling'];
+ }
+ if (!sel) {
+ sel = this._resultTable[dir > 0 ? 'firstChild' : 'lastChild'];
+ }
+
+ if (sel) {
+ L.DomUtil.addClass(sel.firstChild, 'leaflet-routing-geocoder-selected');
+ this._selection = sel;
+ }
+ },
+
+ _keyDown: function(e) {
+ if (this._isOpen) {
+ switch (e.keyCode) {
+ // Up
+ case 38:
+ this._select(-1);
+ L.DomEvent.preventDefault(e);
+ return;
+ // Up
+ case 40:
+ this._select(1);
+ L.DomEvent.preventDefault(e);
+ return;
+ }
+ }
+ },
+
+ _complete: function(completeFn, trySelect) {
+ completeFn(this._elem.value, function(results) {
+ if (trySelect && results.length === 1) {
+ this._resultSelected(results[0]);
+ } else {
+ this._setResults(results);
+ }
+ }, this);
+ }
+ });
+})();
+(function() {
+ 'use strict';
+
+ L.Routing = L.Routing || {};
+
+ L.Routing._jsonpCallbackId = 0;
+ L.Routing._jsonp = function(url, callback, context, jsonpParam) {
+ var callbackId = '_l_routing_machine_' + (L.Routing._jsonpCallbackId++),
+ script;
+ url += '&' + jsonpParam + '=' + callbackId;
+ window[callbackId] = L.Util.bind(callback, context);
+ script = document.createElement('script');
+ script.type = 'text/javascript';
+ script.src = url;
+ script.id = callbackId;
+ document.getElementsByTagName('head')[0].appendChild(script);
+ };
+
+ L.Routing.OSRM = L.Class.extend({
+ options: {
+ serviceUrl: '//router.project-osrm.org/viaroute',
+ geometryPrecision: 6
+ },
+
+ initialize: function(options) {
+ L.Util.setOptions(this, options);
+ this._hints = {
+ locations: {}
+ };
+ },
+
+ route: function(waypoints, callback, context) {
+ var url = this._buildRouteUrl(waypoints);
+
+ L.Routing._jsonp(url, function(data) {
+ this._routeDone(data, waypoints, callback, context);
+ }, this, 'jsonp');
+ },
+
+ _routeDone: function(response, waypoints, callback, context) {
+ context = context || callback;
+ if (response.status !== 0) {
+ callback.call(context, {
+ status: response.status,
+ message: response.message
+ });
+ return;
+ }
+
+ var alts = [{
+ name: response.route_name.join(', '),
+ coordinates: this._decode(response.route_geometry, this.options.geometryPrecision),
+ instructions: this._convertInstructions(response.route_instructions),
+ summary: this._convertSummary(response.route_summary),
+ waypoints: response.via_points
+ }],
+ i;
+
+ if (response.alternative_geometries) {
+ for (i = 0; i < response.alternative_geometries.length; i++) {
+ alts.push({
+ name: response.alternative_names[i].join(', '),
+ coordinates: this._decode(response.alternative_geometries[i], this.options.geometryPrecision),
+ instructions: this._convertInstructions(response.alternative_instructions[i]),
+ summary: this._convertSummary(response.alternative_summaries[i]),
+ waypoints: response.via_points
+ });
+ }
+ }
+
+ this._saveHintData(response, waypoints);
+ callback.call(context, null, alts);
+ },
+
+ _buildRouteUrl: function(waypoints) {
+ var locs = [],
+ locationKey,
+ hint;
+
+ for (var i = 0; i < waypoints.length; i++) {
+ locationKey = this._locationKey(waypoints[i].latLng);
+ locs.push('loc=' + locationKey);
+
+ hint = this._hints.locations[locationKey];
+ if (hint) {
+ locs.push('hint=' + hint);
+ }
+ }
+
+ return this.options.serviceUrl + '?' +
+ 'instructions=true&' +
+ locs.join('&') +
+ (this._hints.checksum !== undefined ? '&checksum=' + this._hints.checksum : '');
+ },
+
+ _locationKey: function(location) {
+ return location.lat + ',' + location.lng;
+ },
+
+ _saveHintData: function(route, waypoints) {
+ var hintData = route.hint_data,
+ loc;
+ this._hints = {
+ checksum: hintData.checksum,
+ locations: {}
+ };
+ for (var i = hintData.locations.length - 1; i >= 0; i--) {
+ loc = waypoints[i].latLng;
+ this._hints.locations[this._locationKey(loc)] = hintData.locations[i];
+ }
+ },
+
+ // Adapted from
+ // https://github.com/DennisSchiefer/Project-OSRM-Web/blob/develop/WebContent/routing/OSRM.RoutingGeometry.js
+ _decode: function(encoded, precision) {
+ var len = encoded.length,
+ index=0,
+ lat=0,
+ lng = 0,
+ array = [];
+
+ precision = Math.pow(10, -precision);
+
+ while (index < len) {
+ var b,
+ shift = 0,
+ result = 0;
+ do {
+ b = encoded.charCodeAt(index++) - 63;
+ result |= (b & 0x1f) << shift;
+ shift += 5;
+ } while (b >= 0x20);
+ var dlat = ((result & 1) ? ~(result >> 1) : (result >> 1));
+ lat += dlat;
+ shift = 0;
+ result = 0;
+ do {
+ b = encoded.charCodeAt(index++) - 63;
+ result |= (b & 0x1f) << shift;
+ shift += 5;
+ } while (b >= 0x20);
+ var dlng = ((result & 1) ? ~(result >> 1) : (result >> 1));
+ lng += dlng;
+ //array.push( {lat: lat * precision, lng: lng * precision} );
+ array.push( [lat * precision, lng * precision] );
+ }
+ return array;
+ },
+
+ _convertSummary: function(osrmSummary) {
+ return {
+ totalDistance: osrmSummary.total_distance,
+ totalTime: osrmSummary.total_time
+ };
+ },
+
+ _convertInstructions: function(osrmInstructions) {
+ var result = [],
+ i,
+ instr,
+ type,
+ driveDir;
+
+ for (i = 0; i < osrmInstructions.length; i++) {
+ instr = osrmInstructions[i];
+ type = this._drivingDirectionType(instr[0]);
+ driveDir = instr[0].split('-');
+ if (type) {
+ result.push({
+ type: type,
+ distance: instr[2],
+ time: instr[4],
+ road: instr[1],
+ direction: instr[6],
+ exit: driveDir.length > 1 ? driveDir[1] : undefined,
+ index: instr[3]
+ });
+ }
+ }
+
+ return result;
+ },
+
+ _drivingDirectionType: function(d) {
+ switch (parseInt(d, 10)) {
+ case 1:
+ return 'Straight';
+ case 2:
+ return 'SlightRight';
+ case 3:
+ return 'Right';
+ case 4:
+ return 'SharpRight';
+ case 5:
+ return 'TurnAround';
+ case 6:
+ return 'SharpLeft';
+ case 7:
+ return 'Left';
+ case 8:
+ return 'SlightRight';
+ case 9:
+ return 'WaypointReached';
+ case 10:
+ // TODO: "Head on"
+ // https://github.com/DennisOSRM/Project-OSRM/blob/master/DataStructures/TurnInstructions.h#L48
+ return 'Straight';
+ case 11:
+ case 12:
+ return 'Roundabout';
+ case 15:
+ return 'DestinationReached';
+ default:
+ return null;
+ }
+ }
+ });
+
+ L.Routing.osrm = function(options) {
+ return new L.Routing.OSRM(options);
+ };
+})();
+(function() {
+ 'use strict';
+
+ L.Routing = L.Routing || {};
+
+ L.Routing.Line = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ options: {
+ styles: [
+ {color: 'black', opacity: 0.15, weight: 7},
+ {color: 'white', opacity: 0.8, weight: 4},
+ {color: 'orange', opacity: 1, weight: 2}
+ ],
+ addWaypoints: true
+ },
+
+ initialize: function(route, options) {
+ L.Util.setOptions(this, options);
+ this._route = route;
+
+ this._wpIndices = this._findWaypointIndices();
+ },
+
+ addTo: function(map) {
+ map.addLayer(this);
+ },
+
+ onAdd: function(map) {
+ var geom = this._route.coordinates,
+ i,
+ pl;
+
+ this._map = map;
+ this._layers = [];
+ for (i = 0; i < this.options.styles.length; i++) {
+ pl = L.polyline(geom, this.options.styles[i])
+ .addTo(map);
+ if (this.options.addWaypoints) {
+ pl.on('mousedown', this._onLineTouched, this);
+ }
+ this._layers.push(pl);
+ }
+ },
+
+ onRemove: function(map) {
+ var i;
+ for (i = 0; i < this._layers.length; i++) {
+ map.removeLayer(this._layers[i]);
+ }
+
+ delete this._map;
+ },
+
+ getBounds: function() {
+ return L.latLngBounds(this._route.coordinates);
+ },
+
+ _findWaypointIndices: function() {
+ var wps = this._route.waypoints,
+ indices = [],
+ i;
+ for (i = 0; i < wps.length; i++) {
+ indices.push(this._findClosestRoutePoint(L.latLng(wps[i])));
+ }
+
+ return indices;
+ },
+
+ _findClosestRoutePoint: function(latlng) {
+ var minDist = Number.MAX_VALUE,
+ minIndex,
+ i,
+ d;
+
+ for (i = this._route.coordinates.length - 1; i >= 0 ; i--) {
+ // TODO: maybe do this in pixel space instead?
+ d = latlng.distanceTo(this._route.coordinates[i]);
+ if (d < minDist) {
+ minIndex = i;
+ minDist = d;
+ }
+ }
+
+ return minIndex;
+ },
+
+ _findNearestWpBefore: function(i) {
+ var j = this._wpIndices.length - 1;
+ while (j >= 0 && this._wpIndices[j] > i) {
+ j--;
+ }
+
+ return j;
+ },
+
+ _onLineTouched: function(e) {
+ var afterIndex = this._findNearestWpBefore(this._findClosestRoutePoint(e.latlng));
+ this.fire('linetouched', {
+ afterIndex: afterIndex,
+ latlng: e.latlng
+ });
+ },
+ });
+
+ L.Routing.line = function(route, options) {
+ return new L.Routing.Line(route, options);
+ };
+})();
+(function() {
+ 'use strict';
+
+ L.Routing = L.Routing || {};
+
+ L.Routing.Itinerary = L.Control.extend({
+ includes: L.Mixin.Events,
+
+ options: {
+ units: 'metric',
+ pointMarkerStyle: {
+ radius: 5,
+ color: '#03f',
+ fillColor: 'white',
+ opacity: 1,
+ fillOpacity: 0.7
+ }
+ },
+
+ initialize: function(options) {
+ L.setOptions(this, options);
+ },
+
+ onAdd: function() {
+ this._container = L.DomUtil.create('div', 'leaflet-routing-container leaflet-bar');
+ L.DomEvent.disableClickPropagation(this._container);
+ L.DomEvent.addListener(this._container, 'mousewheel', function(e) {
+ L.DomEvent.stopPropagation(e);
+ });
+ return this._container;
+ },
+
+ onRemove: function() {
+ },
+
+ setAlternatives: function(routes) {
+ var i,
+ alt,
+ altDiv;
+
+ this._clearAlts();
+
+ this._routes = routes;
+
+ for (i = 0; i < this._routes.length; i++) {
+ alt = this._routes[i];
+ altDiv = L.DomUtil.create('div', 'leaflet-routing-alt' +
+ (i > 0 ? ' leaflet-routing-alt-minimized' : ''),
+ this._container);
+ altDiv.innerHTML = '
' + alt.name + '
' +
+ '
' + this._formatDistance(alt.summary.totalDistance) +
+ ', ' + this._formatTime(alt.summary.totalTime) + '
';
+ L.DomEvent.addListener(altDiv, 'click', this._onAltClicked, this);
+
+ altDiv.appendChild(this._createItineraryTable(alt));
+ this._altElements.push(altDiv);
+ }
+
+ this.fire('routeselected', {route: this._routes[0]});
+ },
+
+ _clearAlts: function() {
+ var i,
+ alt;
+ // TODO: this is really inelegant
+ for (i = 0; this._container && i < this._container.children.length; i++) {
+ alt = this._container.children[i];
+ if (L.DomUtil.hasClass(alt, 'leaflet-routing-alt')) {
+ this._container.removeChild(alt);
+ i--;
+ }
+ }
+
+ this._altElements = [];
+ },
+
+ _createItineraryTable: function(r) {
+ var table = L.DomUtil.create('table', ''),
+ body = L.DomUtil.create('tbody', '', table),
+ i,
+ instr,
+ row,
+ td;
+
+ for (i = 0; i < r.instructions.length; i++) {
+ instr = r.instructions[i];
+ row = L.DomUtil.create('tr', '', body);
+ td = L.DomUtil.create('td', '', row);
+ td.appendChild(document.createTextNode(this._instruction(instr, i)));
+ td = L.DomUtil.create('td', '', row);
+ td.appendChild(document.createTextNode(this._formatDistance(instr.distance)));
+ this._addRowListeners(row, r.coordinates[instr.index]);
+ }
+
+ return table;
+ },
+
+ _addRowListeners: function(row, coordinate) {
+ var _this = this,
+ marker;
+ L.DomEvent.addListener(row, 'mouseover', function() {
+ marker = L.circleMarker(coordinate,
+ _this.options.pointMarkerStyle).addTo(_this._map);
+ });
+ L.DomEvent.addListener(row, 'mouseout', function() {
+ if (marker) {
+ _this._map.removeLayer(marker);
+ marker = null;
+ }
+ });
+ L.DomEvent.addListener(row, 'click', function(e) {
+ _this._map.panTo(coordinate);
+ L.DomEvent.stopPropagation(e);
+ });
+ },
+
+ _onAltClicked: function(e) {
+ var altElem,
+ j,
+ n,
+ isCurrentSelection;
+
+ altElem = e.target;
+ while (!L.DomUtil.hasClass(altElem, 'leaflet-routing-alt')) {
+ altElem = altElem.parentElement;
+ }
+
+ if (L.DomUtil.hasClass(altElem, 'leaflet-routing-alt-minimized')) {
+ for (j = 0; j < this._altElements.length; j++) {
+ n = this._altElements[j];
+ isCurrentSelection = altElem === n;
+ L.DomUtil[isCurrentSelection ? 'removeClass' : 'addClass'](n, 'leaflet-routing-alt-minimized');
+
+ if (isCurrentSelection) {
+ // TODO: don't fire if the currently active is clicked
+ this.fire('routeselected', {route: this._routes[j]});
+ }
+ }
+ }
+
+ L.DomEvent.stop(e);
+ },
+
+ _formatDistance: function(d /* Number (meters) */) {
+ var v;
+
+ if (this.options.units === 'imperial') {
+ d = d / 1.609344;
+ if (d >= 1000) {
+ return (this._round(d) / 1000) + ' mi';
+ } else {
+ return this._round(d / 1.760) + ' yd';
+ }
+ } else {
+ v = this._round(d);
+ return v >= 1000 ? ((v / 1000) + ' km') : (v + ' m');
+ }
+ },
+
+ _round: function(d) {
+ var pow10 = Math.pow(10, (Math.floor(d) + '').length - 1),
+ r = Math.floor(d / pow10 * 2),
+ p = r % 2 ? pow10 / 2 : pow10;
+
+ return Math.round(d / p) * p;
+ },
+
+ _formatTime: function(t /* Number (seconds) */) {
+ if (t > 86400) {
+ return Math.round(t / 3600) + ' h';
+ } else if (t > 3600) {
+ return Math.floor(t / 3600) + ' h ' +
+ Math.round((t % 3600) / 60) + ' min';
+ } else if (t > 300) {
+ return Math.round(t / 60) + ' min';
+ } else if (t > 60) {
+ return Math.floor(t / 60) + ' min ' +
+ (t % 60) + ' s';
+ } else {
+ return t + ' s';
+ }
+ },
+
+ _instruction: function(instr, i) {
+ if (instr.type !== undefined) {
+ return L.Util.template(this._getInstructionTemplate(instr, i),
+ L.extend({exit: this._formatOrder(instr.exit), dir: this._dir[instr.direction]},
+ instr));
+ } else {
+ return instr.text;
+ }
+ },
+
+ _getInstructionTemplate: function(instr, i) {
+ switch (instr.type) {
+ case 'Straight':
+ return (i === 0 ? 'Head' : 'Continue') + ' {dir}' + (instr.road ? ' on {road}' : '');
+ case 'SlightRight':
+ return 'Slight right' + (instr.road ? ' onto {road}' : '');
+ case 'Right':
+ return 'Right' + (instr.road ? ' onto {road}' : '');
+ case 'SharpRight':
+ return 'Sharp right' + (instr.road ? ' onto {road}' : '');
+ case 'TurnAround':
+ return 'Turn around';
+ case 'SharpLeft':
+ return 'Sharp left' + (instr.road ? ' onto {road}' : '');
+ case 'Left':
+ return 'Left' + (instr.road ? ' onto {road}' : '');
+ case 'SlightLeft':
+ return 'Slight left' + (instr.road ? ' onto {road}' : '');
+ case 'WaypointReached':
+ return 'Waypoint reached';
+ case 'Roundabout':
+ return 'Take the {exit} exit in the roundabout';
+ case 'DestinationReached':
+ return 'Destination reached';
+ }
+ },
+
+ _formatOrder: function(n) {
+ var i = n % 10 - 1,
+ suffix = ['st', 'nd', 'rd'];
+
+ return suffix[i] ? n + suffix[i] : n + 'th';
+ },
+
+ _dir: {
+ N: 'north',
+ NE: 'northeast',
+ E: 'east',
+ SE: 'southeast',
+ S: 'south',
+ SW: 'southwest',
+ W: 'west',
+ NW: 'northwest'
+ }
+ });
+
+ L.Routing.Itinerary._instructions = {
+ };
+
+ L.Routing.itinerary = function(router) {
+ return new L.Routing.Itinerary(router);
+ };
+})();
+(function() {
+ 'use strict';
+
+ var Waypoint = L.Class.extend({
+ initialize: function(latLng, name) {
+ this.latLng = latLng;
+ this.name = name;
+ }
+ });
+
+ L.Routing = L.Routing || {};
+
+ L.Routing.Plan = L.Class.extend({
+ includes: L.Mixin.Events,
+
+ options: {
+ dragStyles: [
+ {color: 'black', opacity: 0.15, weight: 7},
+ {color: 'white', opacity: 0.8, weight: 4},
+ {color: 'orange', opacity: 1, weight: 2, dashArray: '7,12'}
+ ],
+ draggableWaypoints: true,
+ addWaypoints: true
+ },
+
+ 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);
+ },
+
+ spliceWaypoints: function() {
+ var args = [arguments[0], arguments[1]],
+ i,
+ wp;
+
+ for (i = 2; i < arguments.length; i++) {
+ args.push(arguments[i] && arguments[i].latLng ? arguments[i] : new Waypoint(arguments[i]));
+ }
+
+ [].splice.apply(this._waypoints, args);
+
+ while (this._waypoints.length < 2) {
+ wp = new Waypoint();
+ this._waypoints.push(wp);
+ args.push(wp);
+ }
+
+ 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'),
+ waypoints = this._waypoints,
+ i,
+ geocoderElem,
+ addWpBtn;
+
+ this._geocoderContainer = container;
+ this._geocoderElems = [];
+
+ for (i = 0; i < waypoints.length; i++) {
+ geocoderElem = this._createGeocoder(i);
+ container.appendChild(geocoderElem);
+ this._geocoderElems.push(geocoderElem);
+ }
+
+ addWpBtn = L.DomUtil.create('button', '', container);
+ addWpBtn.setAttribute('type', 'button');
+ addWpBtn.innerHTML = '+';
+ L.DomEvent.addListener(addWpBtn, 'click', function() {
+ this.spliceWaypoints(waypoints.length, 0, null);
+ }, this);
+
+ this.on('waypointsspliced', this._updateGeocoders);
+
+ return container;
+ },
+
+ _createGeocoder: function(i) {
+ var geocoderElem;
+
+ geocoderElem = L.DomUtil.create('input', '');
+ geocoderElem.placeholder = this._geocoderPlaceholder(i);
+
+ this._updateWaypointName(i);
+
+ L.DomEvent.addListener(geocoderElem, 'click', function() {
+ this.select();
+ }, geocoderElem);
+
+ new L.Routing.Autocomplete(geocoderElem, function(r) {
+ geocoderElem.value = r.name;
+ this._waypoints[i].name = r.name;
+ this._waypoints[i].latLng = r.center;
+ this._updateMarkers();
+ this._fireChanged();
+ }, this, {
+ resultFn: this.options.geocoder.geocode,
+ resultContext: this.options.geocoder,
+ autocompleteFn: this.options.geocoder.suggest,
+ autocompleteContext: this.options.geocoder
+ });
+
+ return geocoderElem;
+ },
+
+ _updateGeocoders: function(e) {
+ var newElems = [],
+ i,
+ geocoderElem,
+ beforeElem;
+ for (i = e.added.length - 1; i >= 0 ; i--) {
+ geocoderElem = this._createGeocoder(e.index + i);
+ if (e.index >= this._geocoderElems.length) {
+ // lastChild is the "add new wp" button
+ beforeElem = this._geocoderContainer.lastChild;
+ } else {
+ beforeElem = this._geocoderElems[e.index];
+ }
+ this._geocoderContainer.insertBefore(geocoderElem, beforeElem);
+ newElems.push(geocoderElem);
+ }
+ newElems.reverse();
+
+ for (i = e.index; i < e.index + e.nRemoved; i++) {
+ this._geocoderContainer.removeChild(this._geocoderElems[i]);
+ }
+
+ newElems.splice(0, 0, e.index, e.nRemoved);
+ [].splice.apply(this._geocoderElems, newElems);
+
+ for (i = 0; i < this._geocoderElems.length; i++) {
+ this._geocoderElems[i].placeholder = this._geocoderPlaceholder(i);
+ }
+ },
+
+ _geocoderPlaceholder: function(i) {
+ return i === 0 ?
+ 'Start' :
+ (i < this._geocoderElems.length - 1 ?
+ 'Via ' + i :
+ 'End');
+ },
+
+ _updateWaypointName: function(i, force) {
+ var wp = this._waypoints[i];
+
+ function updateGeocoder() {
+ var value = wp && wp.name ? wp.name : '';
+ if (this._geocoderElems[i]) {
+ this._geocoderElems[i].value = value;
+ }
+ }
+
+ if (wp.latLng && (force || !wp.name)) {
+ if (this.options.geocoder && this.options.geocoder.reverse) {
+ this.options.geocoder.reverse(wp.latLng, 67108864 /* zoom 18 */, function(rs) {
+ if (rs.length > 0 && rs[0].center.distanceTo(wp.latLng) < 200) {
+ wp.name = rs[0].name;
+ } else {
+ wp.name = '';
+ }
+ updateGeocoder.call(this);
+ }, this);
+ } else {
+ wp.name = '';
+ }
+
+ updateGeocoder.call(this);
+ }
+ },
+
+ _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,
+ icon,
+ options,
+ m;
+
+ if (!this._map) {
+ return;
+ }
+
+ this._removeMarkers();
+
+ for (i = 0; i < this._waypoints.length; i++) {
+ if (this._waypoints[i].latLng) {
+ icon = (typeof(this.options.waypointIcon) === 'function') ?
+ this.options.waypointIcon(i, this._waypoints[i].name, this._waypoints.length) :
+ this.options.waypointIcon;
+ options = {
+ draggable: true
+ };
+ if (icon) {
+ options.icon = icon;
+ }
+
+ m = L.marker(this._waypoints[i].latLng, options).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) {
+ m.on('dragstart', function(e) {
+ this.fire('waypointdragstart', this._createWaypointEvent(i, e));
+ }, this);
+ m.on('drag', function(e) {
+ this.fire('waypointdrag', this._createWaypointEvent(i, e));
+ }, this);
+ m.on('dragend', function(e) {
+ this.fire('waypointdragend', this._createWaypointEvent(i, e));
+ this._waypoints[i].latLng = e.target.getLatLng();
+ this._waypoints[i].name = '';
+ this._updateWaypointName(i, true);
+ this._fireChanged();
+ }, this);
+ },
+
+ _createWaypointEvent: function(i, e) {
+ return {index: i, latlng: e.target.getLatLng()};
+ },
+
+ dragNewWaypoint: function(e) {
+ var i;
+ this._newWp = {
+ afterIndex: e.afterIndex,
+ marker: L.marker(e.latlng).addTo(this._map),
+ lines: []
+ };
+
+ for (i = 0; i < this.options.dragStyles.length; i++) {
+ this._newWp.lines.push(L.polyline([
+ this._waypoints[e.afterIndex].latLng,
+ e.latlng,
+ this._waypoints[e.afterIndex + 1].latLng
+ ], this.options.dragStyles[i]).addTo(this._map));
+ }
+
+ this._markers.splice(e.afterIndex + 1, 0, this._newWp.marker);
+ this._map.on('mousemove', this._onDragNewWp, this);
+ this._map.on('mouseup', this._onWpRelease, this);
+ },
+
+ _onDragNewWp: function(e) {
+ var i;
+ this._newWp.marker.setLatLng(e.latlng);
+ for (i = 0; i < this._newWp.lines.length; i++) {
+ this._newWp.lines[i].spliceLatLngs(1, 1, e.latlng);
+ }
+ },
+
+ _onWpRelease: function(e) {
+ var i;
+ this._map.off('mouseup', this._onWpRelease, this);
+ this._map.off('mousemove', this._onDragNewWp, this);
+ for (i = 0; i < this._newWp.lines.length; i++) {
+ this._map.removeLayer(this._newWp.lines[i]);
+ }
+ this.spliceWaypoints(this._newWp.afterIndex + 1, 0, e.latlng);
+ delete this._newWp;
+ }
+ });
+
+ L.Routing.plan = function(waypoints, options) {
+ return new L.Routing.Plan(waypoints, options);
+ };
+})();
+(function() {
+ 'use strict';
+
+ L.Routing.Control = L.Routing.Itinerary.extend({
+ options: {
+ fitSelectedRoutes: true
+ },
+
+ initialize: function(options) {
+ L.Util.setOptions(this, options);
+
+ this._router = this.options.router || new L.Routing.OSRM();
+ this._plan = this.options.plan || L.Routing.plan(undefined, { geocoder: this.options.geocoder });
+ if (this.options.geocoder) {
+ this._plan.options.geocoder = this.options.geocoder;
+ }
+ if (this.options.waypoints) {
+ this._plan.setWaypoints(this.options.waypoints);
+ }
+
+ L.Routing.Itinerary.prototype.initialize.call(this, options);
+
+ this.on('routeselected', this._routeSelected, this);
+ this._plan.on('waypointschanged', this._route, this);
+
+ this._route();
+ },
+
+ onAdd: function(map) {
+ var container = L.Routing.Itinerary.prototype.onAdd.call(this, map);
+
+ this._map = map;
+ this._map.addLayer(this._plan);
+
+ if (this.options.geocoder) {
+ container.insertBefore(this._plan.createGeocoders(), container.firstChild);
+ }
+
+ return container;
+ },
+
+ onRemove: function(map) {
+ if (this._line) {
+ map.removeLayer(this._line);
+ }
+ map.removeLayer(this._plan);
+ return L.Routing.Itinerary.prototype.onRemove.call(this, map);
+ },
+
+ getWaypoints: function() {
+ return this._plan.getWaypoints();
+ },
+
+ setWaypoints: function(waypoints) {
+ this._plan.setWaypoints(waypoints);
+ },
+
+ spliceWaypoints: function() {
+ var removed = this._plan.spliceWaypoints.apply(this._plan, arguments);
+ return removed;
+ },
+
+ getPlan: function() {
+ return this._plan;
+ },
+
+ _routeSelected: function(e) {
+ var route = e.route;
+ this._clearLine();
+
+ this._line = L.Routing.line(route, this.options.lineOptions);
+ this._line.addTo(this._map);
+ this._hookEvents(this._line);
+
+ if (this.options.fitSelectedRoutes) {
+ this._map.fitBounds(this._line.getBounds());
+ }
+ },
+
+ _hookEvents: function(l) {
+ l.on('linetouched', function(e) {
+ this._plan.dragNewWaypoint(e);
+ }, this);
+ },
+
+ _route: function() {
+ this._clearLine();
+ this._clearAlts();
+ if (this._plan.isReady()) {
+ this._router.route(this._plan.getWaypoints(), function(err, routes) {
+ if (err) {
+ console.log(err);
+ return;
+ }
+ this.setAlternatives(routes);
+ }, this);
+ }
+ },
+
+ _clearLine: function() {
+ if (this._line) {
+ this._map.removeLayer(this._line);
+ delete this._line;
+ }
+ }
+ });
+
+ L.Routing.control = function(options) {
+ return new L.Routing.Control(options);
+ };
+})();
+ return L.Routing;
+}));
+// Packaging/modules magic dance end. This code is inserted after all other
+// code when the dist is built.
diff --git a/public/js/route.js b/public/js/route.js
index c086bb1..e881f11 100644
--- a/public/js/route.js
+++ b/public/js/route.js
@@ -78,7 +78,7 @@ function loadRouteJSON(data)
response = JSON.parse(data);
for (var k in response)
{
- addToRoute(response[k]);
+ addToRoute(response[k], false);
}
return true;
@@ -91,7 +91,7 @@ function loadRouteJSON(data)
*/
function loadRoute(id)
{
- $('#myRoutesWindow').hide(200);
+ openBox('Route')
$.ajax({
url: "/json/route/id/" + id,
@@ -124,13 +124,15 @@ function deleteRoute(id)
* @param {Integer} id
* @returns {Boolean}
*/
-function addToRoute(id)
+function addToRoute(id, build)
{
$('#wayPoints').append('
× ' + points[id].options.title + '
');
closePopup();
openBox('Route', true);
- buildRoute();
+ if (build !== false)
+ buildRoute();
+
return true;
}