From 36d8a207ab8d0d27b0a93c19f95c19dd44361ed8 Mon Sep 17 00:00:00 2001 From: Henrik Fehlauer Date: Tue, 26 May 2020 18:00:00 +0000 Subject: [PATCH 01/20] Convert all shortcuts to act on keydown already Contrary to mouse clicks, when pressing keys on a keyboard the standard behavior is to perform the associated action immediately, not only when releasing the key again. This should also improve the perceived performance slightly. Note that the 'D' shortcut had formerly been handled by Leaflet, which we now have to do on our own. While at it, move the character codes over to the options variable, as found in other parts of the codebase already. Also removing the listener from the container does not seem needed anymore nowadays. --- js/plugin/POIMarkers.js | 12 +++++------- js/plugin/Routing.js | 26 +++++++++++++++++++------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/js/plugin/POIMarkers.js b/js/plugin/POIMarkers.js index 51c156c..25efd33 100644 --- a/js/plugin/POIMarkers.js +++ b/js/plugin/POIMarkers.js @@ -42,11 +42,9 @@ BR.PoiMarkers = L.Control.extend({ self.draw(false); }); - var container = new L.DomUtil.create('div'); - // keys not working when map container does not have focus, use document instead - L.DomEvent.removeListener(container, 'keyup', this._keyupListener); - L.DomEvent.addListener(document, 'keyup', this._keyupListener, this); + L.DomEvent.addListener(document, 'keydown', this._keydownListener, this); + var container = new L.DomUtil.create('div'); return container; }, @@ -62,14 +60,14 @@ BR.PoiMarkers = L.Control.extend({ } }, - _keyupListener: function(e) { + _keydownListener: function(e) { // Suppress shortcut handling when a text input field is focussed if (document.activeElement.type == 'text' || document.activeElement.type == 'textarea') { return; } - if (e.keyCode === this.options.shortcut.draw.disable) { + if (e.keyCode === this.options.shortcut.draw.disable && !e.repeat) { this.draw(false); - } else if (e.keyCode === this.options.shortcut.draw.enable) { + } else if (e.keyCode === this.options.shortcut.draw.enable && !e.repeat) { this.draw(true); } }, diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 9249dca..668818c 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -23,6 +23,12 @@ BR.Routing = L.Routing.extend({ textFunction: function(distance) { return distance / 1000; } + }, + shortcut: { + draw: { + enable: 68, // char code for 'd' + disable: 27 // char code for 'ESC' + } } }, @@ -163,8 +169,7 @@ BR.Routing = L.Routing.extend({ this._draw ); - // keys not working when map container does not have focus, use document instead - L.DomEvent.removeListener(this._container, 'keyup', this._keyupListener); + L.DomEvent.addListener(document, 'keydown', this._keydownListener, this); L.DomEvent.addListener(document, 'keyup', this._keyupListener, this); // enable drawing mode @@ -336,16 +341,23 @@ BR.Routing = L.Routing.extend({ return segments; }, - _keyupListener: function(e) { + _keydownListener: function(e) { // Suppress shortcut handling when a text input field is focussed if (document.activeElement.type == 'text' || document.activeElement.type == 'textarea') { return; } - // add 'esc' to disable drawing - if (e.keyCode === 27) { + if (e.keyCode === this.options.shortcut.draw.disable && !e.repeat) { this._draw.disable(); - } else { - L.Routing.prototype._keyupListener.call(this, e); + } else if (e.keyCode === this.options.shortcut.draw.enable && !e.repeat) { + this._draw.enable(); + } + }, + + _keyupListener: function(e) { + // Prevent Leaflet from triggering drawing a second time on keyup, + // since this is already done in _keydownListener + if (e.keyCode === this.options.shortcut.draw.enable) { + return; } }, From fbd3d3d383ef7e7d0db266f19c95bb3baf37b20d Mon Sep 17 00:00:00 2001 From: Henrik Fehlauer Date: Wed, 27 May 2020 18:00:00 +0000 Subject: [PATCH 02/20] Add missing shortcut tooltips to control buttons Those shortcuts were already working before, but not really discoverable for users. To achieve a consistent UI and keep the wording in line with existing conventions and future shortcuts (i.e. no whole sentences, shortcut in parenthesis), the mute tooltip is changed again to occupy two lines (to limit the width of the tooltip), to be less wordy (so it is faster to read), to use the "mute" term (to help memorizing the M shortcut, at least in English), and to align with the parenthesis style. --- locales/en.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/locales/en.json b/locales/en.json index 902ff22..9c900f0 100644 --- a/locales/en.json +++ b/locales/en.json @@ -118,7 +118,7 @@ "edit": "Click to edit", "help": "□ = move / resize, = delete,
click circle to quit editing" }, - "opacity-slider": "Set transparency of route track and markers. Press and hold the 'M' key to temporarily hide the planned route.", + "opacity-slider": "Set transparency of route track and markers\n(Hold M key to mute temporarily)", "preview": "Preview", "privacy": "Privacy", "reverse-route": "Reverse route", @@ -127,8 +127,8 @@ "route-quality-incline": "Incline coding", "strava-biking": "Show Strava biking segments", "strava-running": "Show Strava running segments", - "zoomInTitle": "Zoom in", - "zoomOutTitle": "Zoom out" + "zoomInTitle": "Zoom in (+ key)", + "zoomOutTitle": "Zoom out (- key)" }, "modal": { "close": "Close" From 08c8cfa8be8ab3be01c8b79c1ee07137feb31629 Mon Sep 17 00:00:00 2001 From: Henrik Fehlauer Date: Thu, 28 May 2020 18:00:00 +0000 Subject: [PATCH 03/20] Prevent more accidental shortcut triggers Do not allow browser keyboard shortcuts to trigger route functions, e.g. pressing Ctrl+P triggers printing in most browsers, but should not at the same time switch BRouter-Web to POI mode. This can be prevented by filtering for modifiers. In the same fashion, when modal dialogs or dropdowns are open it should not be possible to activate map functions in the background now. Finally, inhibit shortcuts in number input fields too, as found in the editable number input fields in the customize profile options. Previously only regular text input fields were protected. To make those checks easier to use, they are deduplicated and moved to Util.js. --- js/Util.js | 30 ++++++++++++++++++++++++++++++ js/control/OpacitySlider.js | 14 ++------------ js/plugin/POIMarkers.js | 7 +++---- js/plugin/Routing.js | 7 +++---- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/js/Util.js b/js/Util.js index 643ae0d..c3f2a3b 100644 --- a/js/Util.js +++ b/js/Util.js @@ -67,5 +67,35 @@ BR.Util = { } $el.remove(); return env; + }, + + keyboardShortcutsAllowed: function(keyEvent) { + // Skip auto-repeating key events + if (keyEvent.repeat) { + return false; + } + + // Suppress shortcut handling when a text or number input field is focussed + if ( + document.activeElement.type == 'number' || + document.activeElement.type == 'text' || + document.activeElement.type == 'textarea' + ) { + return false; + } + + // Only allow shortcuts without modifiers for now, to prevent triggering map functions + // when browser shortcuts are triggered (e.g. Ctrl+P for print should not add a POI) + if (keyEvent.ctrlKey || keyEvent.metaKey || keyEvent.altKey) { + return false; + } + + // Do not allow shortcuts triggering actions behind Bootstrap's + // modal dialogs or when dropdown menus are opened + if ($('.modal.show').length || $('.dropdown.show').length) { + return false; + } + + return true; } }; diff --git a/js/control/OpacitySlider.js b/js/control/OpacitySlider.js index 14f0439..e675b2c 100644 --- a/js/control/OpacitySlider.js +++ b/js/control/OpacitySlider.js @@ -52,23 +52,13 @@ BR.OpacitySlider = L.Class.extend({ }, _keydownListener: function(e) { - // Suppress shortcut handling when a text input field is focussed - if (document.activeElement.type == 'text' || document.activeElement.type == 'textarea') { - return; - } - - if (e.keyCode === this.options.muteKeyCode && !e.repeat) { + if (BR.Util.keyboardShortcutsAllowed(e) && e.keyCode === this.options.muteKeyCode) { this.options.callback(0); } }, _keyupListener: function(e) { - // Suppress shortcut handling when a text input field is focussed - if (document.activeElement.type == 'text' || document.activeElement.type == 'textarea') { - return; - } - - if (e.keyCode === this.options.muteKeyCode && !e.repeat) { + if (BR.Util.keyboardShortcutsAllowed(e) && e.keyCode === this.options.muteKeyCode) { this.options.callback(this.input.val() / 100); } }, diff --git a/js/plugin/POIMarkers.js b/js/plugin/POIMarkers.js index 25efd33..dce84ae 100644 --- a/js/plugin/POIMarkers.js +++ b/js/plugin/POIMarkers.js @@ -61,13 +61,12 @@ BR.PoiMarkers = L.Control.extend({ }, _keydownListener: function(e) { - // Suppress shortcut handling when a text input field is focussed - if (document.activeElement.type == 'text' || document.activeElement.type == 'textarea') { + if (!BR.Util.keyboardShortcutsAllowed(e)) { return; } - if (e.keyCode === this.options.shortcut.draw.disable && !e.repeat) { + if (e.keyCode === this.options.shortcut.draw.disable) { this.draw(false); - } else if (e.keyCode === this.options.shortcut.draw.enable && !e.repeat) { + } else if (e.keyCode === this.options.shortcut.draw.enable) { this.draw(true); } }, diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 668818c..e9e1b2b 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -342,13 +342,12 @@ BR.Routing = L.Routing.extend({ }, _keydownListener: function(e) { - // Suppress shortcut handling when a text input field is focussed - if (document.activeElement.type == 'text' || document.activeElement.type == 'textarea') { + if (!BR.Util.keyboardShortcutsAllowed(e)) { return; } - if (e.keyCode === this.options.shortcut.draw.disable && !e.repeat) { + if (e.keyCode === this.options.shortcut.draw.disable) { this._draw.disable(); - } else if (e.keyCode === this.options.shortcut.draw.enable && !e.repeat) { + } else if (e.keyCode === this.options.shortcut.draw.enable) { this._draw.enable(); } }, From d6c648d3ebe9f7f1dcc4aa7488736399ab5082c9 Mon Sep 17 00:00:00 2001 From: Henrik Fehlauer Date: Fri, 29 May 2020 18:00:00 +0000 Subject: [PATCH 04/20] Fix mute shortcut not working for color coded routes The mute shortcut would only work for regular routes before. Note that no-go areas, POIs and tracks continue to be unaffected by muting, so e.g. peeking at a track below the route is still possible. --- js/plugin/RoutingPathQuality.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/js/plugin/RoutingPathQuality.js b/js/plugin/RoutingPathQuality.js index 64b4872..1ed5e11 100644 --- a/js/plugin/RoutingPathQuality.js +++ b/js/plugin/RoutingPathQuality.js @@ -1,4 +1,10 @@ BR.RoutingPathQuality = L.Control.extend({ + options: { + shortcut: { + muteKeyCode: 77 // char code for 'm' + } + }, + initialize: function(map, layersControl, options) { L.setOptions(this, options); @@ -78,6 +84,7 @@ BR.RoutingPathQuality = L.Control.extend({ this.selectedProvider = this._initialProvider; this._active = false; + this._muted = false; }, onAdd: function(map) { @@ -133,6 +140,11 @@ BR.RoutingPathQuality = L.Control.extend({ }); } + if (this.options.shortcut.muteKeyCode) { + L.DomEvent.addListener(document, 'keydown', this._keydownListener, this); + L.DomEvent.addListener(document, 'keyup', this._keyupListener, this); + } + this.routingPathButton = new L.easyButton({ states: states }).addTo(map); @@ -177,6 +189,20 @@ BR.RoutingPathQuality = L.Control.extend({ this._routingSegments.addLayer(layers[i]); } } + }, + + _keydownListener: function(e) { + if (BR.Util.keyboardShortcutsAllowed(e) && this._active && e.keyCode === this.options.shortcut.muteKeyCode) { + this._muted = true; + this._deactivate(this.routingPathButton); + } + }, + + _keyupListener: function(e) { + if (BR.Util.keyboardShortcutsAllowed(e) && this._muted && e.keyCode === this.options.shortcut.muteKeyCode) { + this._muted = false; + this._activate(this.routingPathButton); + } } }); From 8e78c858c15d80c30498f3ffbac7b15faae81ad3 Mon Sep 17 00:00:00 2001 From: Henrik Fehlauer Date: Sat, 30 May 2020 18:00:00 +0000 Subject: [PATCH 05/20] Add shortcut to toggle color coding button Press 'C' repeatedly to switch between the various color coding options. When the last one is reached, we show the regular route again. --- js/plugin/RoutingPathQuality.js | 11 +++++++++-- locales/en.json | 6 +++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/js/plugin/RoutingPathQuality.js b/js/plugin/RoutingPathQuality.js index 1ed5e11..76dd6bc 100644 --- a/js/plugin/RoutingPathQuality.js +++ b/js/plugin/RoutingPathQuality.js @@ -1,6 +1,7 @@ BR.RoutingPathQuality = L.Control.extend({ options: { shortcut: { + toggle: 67, // char code for 'c' muteKeyCode: 77 // char code for 'm' } }, @@ -140,7 +141,7 @@ BR.RoutingPathQuality = L.Control.extend({ }); } - if (this.options.shortcut.muteKeyCode) { + if (this.options.shortcut.muteKeyCode || this.options.shortcut.toggle) { L.DomEvent.addListener(document, 'keydown', this._keydownListener, this); L.DomEvent.addListener(document, 'keyup', this._keyupListener, this); } @@ -192,10 +193,16 @@ BR.RoutingPathQuality = L.Control.extend({ }, _keydownListener: function(e) { - if (BR.Util.keyboardShortcutsAllowed(e) && this._active && e.keyCode === this.options.shortcut.muteKeyCode) { + if (!BR.Util.keyboardShortcutsAllowed(e)) { + return; + } + if (this._active && e.keyCode === this.options.shortcut.muteKeyCode) { this._muted = true; this._deactivate(this.routingPathButton); } + if (!this._muted && e.keyCode === this.options.shortcut.toggle) { + this.routingPathButton.button.click(); + } }, _keyupListener: function(e) { diff --git a/locales/en.json b/locales/en.json index 9c900f0..e5b1a67 100644 --- a/locales/en.json +++ b/locales/en.json @@ -122,9 +122,9 @@ "preview": "Preview", "privacy": "Privacy", "reverse-route": "Reverse route", - "route-quality-altitude": "Altitude coding", - "route-quality-cost": "Cost coding", - "route-quality-incline": "Incline coding", + "route-quality-altitude": "Altitude coding (C key to toggle)", + "route-quality-cost": "Cost coding (C key to toggle)", + "route-quality-incline": "Incline coding (C key to toggle)", "strava-biking": "Show Strava biking segments", "strava-running": "Show Strava running segments", "zoomInTitle": "Zoom in (+ key)", From fd97019fdc0e75843c3eb9da2d4238b7c87eeb4d Mon Sep 17 00:00:00 2001 From: Henrik Fehlauer Date: Sun, 31 May 2020 18:00:00 +0000 Subject: [PATCH 06/20] Add shortcut to trigger geolocation Press 'L' to initiate the "Show me where I am" function. --- js/Map.js | 18 +++++++++++++++++- locales/en.json | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/js/Map.js b/js/Map.js index 7defdc1..2e26771 100644 --- a/js/Map.js +++ b/js/Map.js @@ -2,6 +2,12 @@ BR.Map = { initMap: function() { var map, layersControl; + L.setOptions(this, { + shortcut: { + locate: 76 // char code for 'l' + } + }); + BR.keys = BR.keys || {}; var maxZoom = 19; @@ -96,7 +102,7 @@ BR.Map = { var secureContext = 'isSecureContext' in window ? isSecureContext : location.protocol === 'https:'; if (secureContext) { - L.control + var locationControl = L.control .locate({ strings: { title: i18next.t('map.locate-me') @@ -105,6 +111,16 @@ BR.Map = { iconLoading: 'fa fa-spinner fa-pulse' }) .addTo(map); + L.DomEvent.addListener( + document, + 'keydown', + function(e) { + if (BR.Util.keyboardShortcutsAllowed(e) && e.keyCode === this.options.shortcut.locate) { + locationControl.start(); + } + }, + this + ); } L.control.scale().addTo(map); diff --git a/locales/en.json b/locales/en.json index e5b1a67..7208818 100644 --- a/locales/en.json +++ b/locales/en.json @@ -110,7 +110,7 @@ "topo": "OpenTopoMap" }, "loading": "Loading…", - "locate-me": "Show me where I am", + "locate-me": "Show me where I am (L key)", "nogo": { "cancel": "Cancel drawing no-go area", "click-drag": "Click and drag to draw circle", From f23e3751b5783188fb00503eb14ffc162eaf341f Mon Sep 17 00:00:00 2001 From: Henrik Fehlauer Date: Mon, 1 Jun 2020 18:00:00 +0000 Subject: [PATCH 07/20] Allow translation of the search field placeholder text It was not translated before, since the string was coming from an external dependency. --- js/plugin/Search.js | 9 +++++++++ locales/en.json | 1 + 2 files changed, 10 insertions(+) diff --git a/js/plugin/Search.js b/js/plugin/Search.js index b0f20e0..7e0cabb 100644 --- a/js/plugin/Search.js +++ b/js/plugin/Search.js @@ -9,6 +9,15 @@ BR.Search = L.Control.Geocoder.extend({ position: 'topleft' }, + initialize: function(options) { + L.Control.Geocoder.prototype.initialize.call(this, options); + L.setOptions(this, { + // i18next.t will only return 'undefined' if it is called in a static context + // (e.g. when added directly to "options:" above), so we have to call it here + placeholder: i18next.t('map.geocoder-placeholder') + }); + }, + markGeocode: function(result) { this._map.fitBounds(result.geocode.bbox, { maxZoom: 17 diff --git a/locales/en.json b/locales/en.json index 7208818..1399b44 100644 --- a/locales/en.json +++ b/locales/en.json @@ -91,6 +91,7 @@ "draw-route-start": "Draw route (D key)", "draw-route-stop": "Stop drawing route (ESC key)", "enter-poi-name": "Enter Point of Interest name", + "geocoder-placeholder": "Search…", "hikebike-hillshading": "Hillshading", "hiking": "Hiking", "layer": { From 37ab05310b6a3381811adf83a1893ba57e19b281 Mon Sep 17 00:00:00 2001 From: Henrik Fehlauer Date: Tue, 2 Jun 2020 18:00:00 +0000 Subject: [PATCH 08/20] Add shortcut for geocoder search field Press 'F', type a query and press 'Enter' to find places effortlessly. Note that the button already triggers on mousedown events, even though by convention it should react on mouseup (i.e. a regular click) only. However, that's an issue in the external dependency, and can be worked around for now. --- js/index.js | 1 + js/plugin/Search.js | 14 +++++++++++++- locales/en.json | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/js/index.js b/js/index.js index 59fcc22..14432d2 100644 --- a/js/index.js +++ b/js/index.js @@ -46,6 +46,7 @@ search = new BR.Search(); map.addControl(search); + $('#map .leaflet-control-geocoder > button')[0].title = i18next.t('map.geocoder'); router = L.bRouter(); //brouterCgi dummyRouter diff --git a/js/plugin/Search.js b/js/plugin/Search.js index 7e0cabb..4985caa 100644 --- a/js/plugin/Search.js +++ b/js/plugin/Search.js @@ -6,7 +6,10 @@ BR.Search = L.Control.Geocoder.extend({ }), sizeInMeters: 800 }), - position: 'topleft' + position: 'topleft', + shortcut: { + search: 70 // char code for 'f' + } }, initialize: function(options) { @@ -16,6 +19,8 @@ BR.Search = L.Control.Geocoder.extend({ // (e.g. when added directly to "options:" above), so we have to call it here placeholder: i18next.t('map.geocoder-placeholder') }); + + L.DomEvent.addListener(document, 'keydown', this._keydownListener, this); }, markGeocode: function(result) { @@ -38,5 +43,12 @@ BR.Search = L.Control.Geocoder.extend({ if (this._geocodeMarker) { this._map.removeLayer(this._geocodeMarker); } + }, + + _keydownListener: function(e) { + if (BR.Util.keyboardShortcutsAllowed(e) && e.keyCode === this.options.shortcut.search) { + $('#map .leaflet-control-geocoder')[0].dispatchEvent(new MouseEvent('mousedown')); + e.preventDefault(); + } } }); diff --git a/locales/en.json b/locales/en.json index 1399b44..eb9c6ef 100644 --- a/locales/en.json +++ b/locales/en.json @@ -91,6 +91,7 @@ "draw-route-start": "Draw route (D key)", "draw-route-stop": "Stop drawing route (ESC key)", "enter-poi-name": "Enter Point of Interest name", + "geocoder": "Search (F key)", "geocoder-placeholder": "Search…", "hikebike-hillshading": "Hillshading", "hiking": "Hiking", From 2848e4dd36fa7a881705f4edb67a40b1c0f84a50 Mon Sep 17 00:00:00 2001 From: Henrik Fehlauer Date: Wed, 3 Jun 2020 18:00:00 +0000 Subject: [PATCH 09/20] Add shortcut to reverse route Press 'R' to swap start and end of the route. --- js/plugin/Routing.js | 5 ++++- locales/en.json | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index e9e1b2b..3675887 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -28,7 +28,8 @@ BR.Routing = L.Routing.extend({ draw: { enable: 68, // char code for 'd' disable: 27 // char code for 'ESC' - } + }, + reverse: 82 // char code for 'r' } }, @@ -349,6 +350,8 @@ BR.Routing = L.Routing.extend({ this._draw.disable(); } else if (e.keyCode === this.options.shortcut.draw.enable) { this._draw.enable(); + } else if (e.keyCode === this.options.shortcut.reverse) { + this.reverse(); } }, diff --git a/locales/en.json b/locales/en.json index eb9c6ef..3814eeb 100644 --- a/locales/en.json +++ b/locales/en.json @@ -123,7 +123,7 @@ "opacity-slider": "Set transparency of route track and markers\n(Hold M key to mute temporarily)", "preview": "Preview", "privacy": "Privacy", - "reverse-route": "Reverse route", + "reverse-route": "Reverse route (R key)", "route-quality-altitude": "Altitude coding (C key to toggle)", "route-quality-cost": "Cost coding (C key to toggle)", "route-quality-incline": "Incline coding (C key to toggle)", From a852acbfce14ec1be8b101ddb16445913223f068 Mon Sep 17 00:00:00 2001 From: Henrik Fehlauer Date: Thu, 4 Jun 2020 18:00:00 +0000 Subject: [PATCH 10/20] Add shortcut to create no-go areas Press 'N' to initiate drawing a no-go area. 'Escape' will cancel, similar to how drawing a route works. --- js/plugin/NogoAreas.js | 51 +++++++++++++++++++++++++++++++++--------- locales/en.json | 4 ++-- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/js/plugin/NogoAreas.js b/js/plugin/NogoAreas.js index 012de7d..6cff5ec 100644 --- a/js/plugin/NogoAreas.js +++ b/js/plugin/NogoAreas.js @@ -1,4 +1,13 @@ BR.NogoAreas = L.Control.extend({ + options: { + shortcut: { + draw: { + enable: 78, // char code for 'n' + disable: 27 // char code for 'ESC' + } + } + }, + statics: { MSG_BUTTON: 'Draw no-go area (circle)', MSG_BUTTON_CANCEL: 'Cancel drawing no-go area', @@ -48,28 +57,32 @@ BR.NogoAreas = L.Control.extend({ featuresLayer: this.drawnItems })); + this.startDrawing = function(control) { + // initial radius of 0 to detect click, see DeletableCircleEditor.onDrawingMouseUp + var opts = L.extend({ radius: 0 }, self.style); + editTools.startCircle(null, opts); + + control.state('cancel-no-go-create'); + }; + + this.stopDrawing = function(control) { + editTools.stopDrawing(); + control.state('no-go-create'); + }; + this.button = L.easyButton({ states: [ { stateName: BR.NogoAreas.STATE_CREATE, icon: 'fa-ban', title: BR.NogoAreas.MSG_BUTTON, - onClick: function(control) { - // initial radius of 0 to detect click, see DeletableCircleEditor.onDrawingMouseUp - var opts = L.extend({ radius: 0 }, self.style); - editTools.startCircle(null, opts); - - control.state('cancel-no-go-create'); - } + onClick: this.startDrawing }, { stateName: BR.NogoAreas.STATE_CANCEL, icon: 'fa-ban active', title: BR.NogoAreas.MSG_BUTTON_CANCEL, - onClick: function(control) { - editTools.stopDrawing(); - control.state('no-go-create'); - } + onClick: this.stopDrawing } ] }); @@ -78,6 +91,8 @@ BR.NogoAreas = L.Control.extend({ // events firing in Chrome mobile while L.Map.Tap enabled for circle drawing L.DomEvent.addListener(this.button.button, 'pointerdown', L.DomEvent.stop); + L.DomEvent.addListener(document, 'keydown', this._keydownListener, this); + this.editTools.on( 'editable:drawing:end', function(e) { @@ -124,6 +139,20 @@ BR.NogoAreas = L.Control.extend({ return L.DomUtil.create('div'); }, + _keydownListener: function(e) { + if (!BR.Util.keyboardShortcutsAllowed(e)) { + return; + } + if (e.keyCode === this.options.shortcut.draw.disable && this.button.state() === BR.NogoAreas.STATE_CANCEL) { + this.stopDrawing(this.button); + } else if ( + e.keyCode === this.options.shortcut.draw.enable && + this.button.state() === BR.NogoAreas.STATE_CREATE + ) { + this.startDrawing(this.button); + } + }, + displayUploadError: function(message) { $('#nogoError').text(message ? message : ''); $('#nogoError').css('display', message ? 'block' : 'none'); diff --git a/locales/en.json b/locales/en.json index 3814eeb..6b3db7b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -114,9 +114,9 @@ "loading": "Loading…", "locate-me": "Show me where I am (L key)", "nogo": { - "cancel": "Cancel drawing no-go area", + "cancel": "Cancel drawing no-go area (ESC key)", "click-drag": "Click and drag to draw circle", - "draw": "Draw no-go area (circle)", + "draw": "Draw circular no-go area (N key)", "edit": "Click to edit", "help": "□ = move / resize, = delete,
click circle to quit editing" }, From 48f8fe3524f656e28094c2ce06d0152dc687ea81 Mon Sep 17 00:00:00 2001 From: Henrik Fehlauer Date: Fri, 5 Jun 2020 18:00:00 +0000 Subject: [PATCH 11/20] Add shortcut to delete last point Press 'Z' to undo adding the last point. --- js/index.js | 2 +- js/plugin/Routing.js | 9 ++++++++- locales/en.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/js/index.js b/js/index.js index 14432d2..4cc28eb 100644 --- a/js/index.js +++ b/js/index.js @@ -84,7 +84,7 @@ var deletePointButton = L.easyButton( '', function() { - routing.removeWaypoint(routing.getLast(), function(err, data) {}); + routing.deleteLastPoint(); }, i18next.t('map.delete-last-point') ); diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 3675887..38005f4 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -29,7 +29,8 @@ BR.Routing = L.Routing.extend({ enable: 68, // char code for 'd' disable: 27 // char code for 'ESC' }, - reverse: 82 // char code for 'r' + reverse: 82, // char code for 'r' + deleteLastPoint: 90 // char code for 'z' } }, @@ -352,6 +353,8 @@ BR.Routing = L.Routing.extend({ this._draw.enable(); } else if (e.keyCode === this.options.shortcut.reverse) { this.reverse(); + } else if (e.keyCode === this.options.shortcut.deleteLastPoint) { + this.deleteLastPoint(); } }, @@ -374,6 +377,10 @@ BR.Routing = L.Routing.extend({ this.setWaypoints(waypoints); }, + deleteLastPoint: function() { + this.removeWaypoint(this.getLast(), function(err, data) {}); + }, + _removeDistanceMarkers: function() { if (this._map && this._distanceMarkers) { this._map.removeLayer(this._distanceMarkers); diff --git a/locales/en.json b/locales/en.json index 6b3db7b..c1aceca 100644 --- a/locales/en.json +++ b/locales/en.json @@ -82,7 +82,7 @@ "clear-route": "Clear route data", "copyright": "Copyright", "cycling": "Cycling", - "delete-last-point": "Delete last point", + "delete-last-point": "Delete last point (Z key)", "delete-nogo-areas": "Delete all no-go areas", "delete-pois": "Delete all points of interest", "delete-route": "Delete route", From 29530919b5f17dfda148e6d3e62646c8dc2c4d6c Mon Sep 17 00:00:00 2001 From: Henrik Fehlauer Date: Sat, 6 Jun 2020 18:00:00 +0000 Subject: [PATCH 12/20] Stop deleting points once there are no more points left Prevents "Uncaught TypeError: Cannot read property 'off' of null" in L.Routing.js --- js/plugin/Routing.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/plugin/Routing.js b/js/plugin/Routing.js index 38005f4..f0ee5ab 100644 --- a/js/plugin/Routing.js +++ b/js/plugin/Routing.js @@ -378,7 +378,9 @@ BR.Routing = L.Routing.extend({ }, deleteLastPoint: function() { - this.removeWaypoint(this.getLast(), function(err, data) {}); + if ((lastPoint = this.getLast())) { + this.removeWaypoint(lastPoint, function(err, data) {}); + } }, _removeDistanceMarkers: function() { From e4e1c8a19e793e34ec97fd06b96edbb972ef955b Mon Sep 17 00:00:00 2001 From: Henrik Fehlauer Date: Sun, 7 Jun 2020 18:00:00 +0000 Subject: [PATCH 13/20] Add shortcut for clear route dialog Use 'Backspace' to show the dialog allowing to clear the route. The 'Del' key could then be used in the future to possibly allow to delete individual selected items like POIs or no-go areas. --- js/index.js | 88 +++++++++++++++++++++++++++++-------------------- locales/en.json | 1 + 2 files changed, 53 insertions(+), 36 deletions(-) diff --git a/js/index.js b/js/index.js index 4cc28eb..a0ce08c 100644 --- a/js/index.js +++ b/js/index.js @@ -92,45 +92,61 @@ deleteRouteButton = L.easyButton( 'fa-trash-o', function() { - bootbox.prompt({ - size: 'small', - title: i18next.t('map.clear-route'), - inputType: 'checkbox', - inputOptions: [ - { - text: i18next.t('map.delete-route'), - value: 'route' - }, - { - text: i18next.t('map.delete-nogo-areas'), - value: 'nogo' - }, - { - text: i18next.t('map.delete-pois'), - value: 'pois' - } - ], - value: ['route'], - callback: function(result) { - if (result !== null) { - if (result.indexOf('route') !== -1) { - routing.clear(); - } - if (result.indexOf('nogo') !== -1) { - nogos.clear(); - } - if (result.indexOf('pois') !== -1) { - pois.clear(); - } - onUpdate(); - urlHash.onMapMove(); - } - } - }); + clearRoute(); }, - i18next.t('map.clear-route') + i18next.t('map.clear-route-tooltip') ); + L.DomEvent.addListener( + document, + 'keydown', + function(e) { + if (BR.Util.keyboardShortcutsAllowed(e) && e.keyCode === 8 && !$('.modal.show').length) { + // char code for 'backspace' + clearRoute(); + } + }, + this + ); + + function clearRoute() { + bootbox.prompt({ + size: 'small', + title: i18next.t('map.clear-route'), + inputType: 'checkbox', + inputOptions: [ + { + text: i18next.t('map.delete-route'), + value: 'route' + }, + { + text: i18next.t('map.delete-nogo-areas'), + value: 'nogo' + }, + { + text: i18next.t('map.delete-pois'), + value: 'pois' + } + ], + value: ['route'], + callback: function(result) { + if (result !== null) { + if (result.indexOf('route') !== -1) { + routing.clear(); + } + if (result.indexOf('nogo') !== -1) { + nogos.clear(); + } + if (result.indexOf('pois') !== -1) { + pois.clear(); + } + onUpdate(); + urlHash.onMapMove(); + } + } + }); + } + function updateRoute(evt) { router.setOptions(evt.options); diff --git a/locales/en.json b/locales/en.json index c1aceca..cd27425 100644 --- a/locales/en.json +++ b/locales/en.json @@ -80,6 +80,7 @@ "attribution-osm-long": "OpenStreetMap contributors", "attribution-osm-short": "OpenStreetMap", "clear-route": "Clear route data", + "clear-route-tooltip": "Clear route data (Backspace key)", "copyright": "Copyright", "cycling": "Cycling", "delete-last-point": "Delete last point (Z key)", From ba0bb39fd5075ee28f575d7063251bcb5de8d258 Mon Sep 17 00:00:00 2001 From: Henrik Fehlauer Date: Mon, 8 Jun 2020 18:00:00 +0000 Subject: [PATCH 14/20] Add shortcut to toggle Strava layer Press 'S' to show the Strava buttons (if the API key has been set) as well as the Strava segments layer (once it has been fetched). Note that fetching and updating data from Strava still needs to be triggered manually by clicking the respective biking or running button. --- js/plugin/stravaSegments.js | 21 +++++++++++++++++++++ locales/en.json | 4 ++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/js/plugin/stravaSegments.js b/js/plugin/stravaSegments.js index 91d3534..a4de7ec 100644 --- a/js/plugin/stravaSegments.js +++ b/js/plugin/stravaSegments.js @@ -16,6 +16,12 @@ BR.stravaSegments = function(map, layersControl) { ); }; + L.setOptions(this, { + shortcut: { + toggleLayer: 83 // char code for 's' + } + }); + // hide strava buttons when layer is inactive var toggleStravaControl = function() { var stravaBar = stravaControl.runningButton.button.parentElement; @@ -24,5 +30,20 @@ BR.stravaSegments = function(map, layersControl) { toggleStravaControl(); stravaControl.stravaLayer.on('add remove', toggleStravaControl); + L.DomEvent.addListener( + document, + 'keydown', + function(e) { + if (BR.Util.keyboardShortcutsAllowed(e) && e.keyCode === this.options.shortcut.toggleLayer) { + if (map.hasLayer(stravaControl.stravaLayer)) { + map.removeLayer(stravaControl.stravaLayer); + } else { + map.addLayer(stravaControl.stravaLayer); + } + } + }, + this + ); + return stravaControl; }; diff --git a/locales/en.json b/locales/en.json index cd27425..6766c76 100644 --- a/locales/en.json +++ b/locales/en.json @@ -128,8 +128,8 @@ "route-quality-altitude": "Altitude coding (C key to toggle)", "route-quality-cost": "Cost coding (C key to toggle)", "route-quality-incline": "Incline coding (C key to toggle)", - "strava-biking": "Show Strava biking segments", - "strava-running": "Show Strava running segments", + "strava-biking": "Show Strava biking segments\n(S key to toggle layer, click to reload for current area)", + "strava-running": "Show Strava running segments\n(S key to toggle layer, click to reload for current area)", "zoomInTitle": "Zoom in (+ key)", "zoomOutTitle": "Zoom out (- key)" }, From 6da520ed4d23befc8372209f822fa4b66a7640f4 Mon Sep 17 00:00:00 2001 From: Henrik Fehlauer Date: Tue, 9 Jun 2020 18:00:00 +0000 Subject: [PATCH 15/20] Add shortcut to toggle profile switcher Press 'G' to open the profile switcher, select a profile with the arrow keys, apply with 'Enter' and close the switcher with 'Escape'. While 'T' and 'P' will be / were already taken, 'G' is at least easily reachable with the left hand for users using a mouse with their right hand at the same time. Since Bootstrap keeps updating the tooltip when changing options in the dropdown (which is useful in case the text is longer than the width of the control), the shortcut text needs to be applied dynamically too. --- index.html | 2 +- js/control/RoutingOptions.js | 20 ++++++++++++++++++++ locales/en.json | 3 ++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 2f871e3..30133be 100644 --- a/index.html +++ b/index.html @@ -29,7 +29,7 @@ >