From 2ffcec965a4c531d4d0bc2566013827c97edf63d Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 12 Nov 2018 17:23:38 -0500 Subject: [PATCH 1/8] Don't snap notes to OSM elements (closes #5191) --- modules/behavior/draw.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/behavior/draw.js b/modules/behavior/draw.js index 21d17d2f5..54517c62d 100644 --- a/modules/behavior/draw.js +++ b/modules/behavior/draw.js @@ -41,7 +41,9 @@ export function behaviorDraw(context) { // related code // - `mode/drag_node.js` `datum()` function datum() { - if (d3_event.altKey) return {}; + var mode = context.mode(); + var isNote = mode && (mode.id.indexOf('note') !== -1); + if (d3_event.altKey || isNote) return {}; var element; if (d3_event.type === 'keydown') { From 4fe05602cf098f8039e4bdca591abc17721de397 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 12 Nov 2018 21:17:34 -0800 Subject: [PATCH 2/8] Adds Communication, Mobile Phone, Television, and Radio Mast presets Adds Communication Tower preset Corrects the paths of the Observation Tower and Surveillance Camera presets --- data/presets.yaml | 37 ++++++++++--- data/presets/presets.json | 9 +++- .../presets/man_made/mast/communication.json | 37 +++++++++++++ .../mast/communication/mobile_phone.json | 36 +++++++++++++ .../man_made/mast/communication/radio.json | 33 ++++++++++++ .../mast/communication/television.json | 33 ++++++++++++ .../camera.json} | 0 .../presets/man_made/tower/communication.json | 38 +++++++++++++ .../man_made/{ => tower}/observation.json | 9 ++++ data/taginfo.json | 54 ++++++++++++++----- dist/locales/en.json | 36 ++++++++++--- 11 files changed, 293 insertions(+), 29 deletions(-) create mode 100644 data/presets/presets/man_made/mast/communication.json create mode 100644 data/presets/presets/man_made/mast/communication/mobile_phone.json create mode 100644 data/presets/presets/man_made/mast/communication/radio.json create mode 100644 data/presets/presets/man_made/mast/communication/television.json rename data/presets/presets/man_made/{surveillance_camera.json => surveillance/camera.json} (100%) create mode 100644 data/presets/presets/man_made/tower/communication.json rename data/presets/presets/man_made/{ => tower}/observation.json (60%) diff --git a/data/presets.yaml b/data/presets.yaml index 61a9116f9..7f3855597 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -4710,16 +4710,31 @@ en: name: Mast # 'terms: antenna,broadcast tower,cell phone tower,cell tower,communication mast,communication tower,guyed tower,mobile phone tower,radio mast,radio tower,television tower,transmission mast,transmission tower,tv tower' terms: '' + man_made/mast/communication: + # 'man_made=mast, tower:type=communication' + name: Communication Mast + # 'terms: antenna,broadcast tower,cell phone tower,cell tower,communication mast,communication tower,guyed tower,mobile phone tower,radio mast,radio tower,television tower,transmission mast,transmission tower,tv tower' + terms: '' + man_made/mast/communication/mobile_phone: + # 'man_made=mast, tower:type=communication, communication:mobile_phone=yes' + name: Mobile Phone Mast + # 'terms: antenna,cell mast,cell phone mast,cell phone tower,cell tower,communication mast,communication tower,guyed tower,mobile phone tower,television tower,transmission mast,transmission tower' + terms: '' + man_made/mast/communication/radio: + # 'man_made=mast, tower:type=communication, communication:radio=yes' + name: Radio Broadcast Mast + # 'terms: antenna,broadcast tower,communication mast,communication tower,guyed tower,radio mast,radio tower,transmission mast,transmission tower' + terms: '' + man_made/mast/communication/television: + # 'man_made=mast, tower:type=communication, communication:television=yes' + name: Television Broadcast Mast + # 'terms: antenna,broadcast tower,communication mast,communication tower,guyed tower,television tower,transmission mast,transmission tower,tv tower' + terms: '' man_made/monitoring_station: # man_made=monitoring_station name: Monitoring Station # 'terms: weather,earthquake,seismology,air,gps' terms: '' - man_made/observation: - # 'man_made=tower, tower:type=observation' - name: Observation Tower - # 'terms: lookout tower,fire tower' - terms: '' man_made/observatory: # man_made=observatory name: Observatory @@ -4759,7 +4774,7 @@ en: name: Surveillance # 'terms: anpr,alpr,camera,car plate recognition,cctv,guard,license plate recognition,monitoring,number plate recognition,security,video,webcam' terms: '' - man_made/surveillance_camera: + man_made/surveillance/camera: # 'man_made=surveillance, surveillance:type=camera' name: Surveillance Camera # 'terms: anpr,alpr,camera,car plate recognition,cctv,guard,license plate recognition,monitoring,number plate recognition,security,video,webcam' @@ -4773,6 +4788,16 @@ en: # man_made=tower name: Tower terms: '' + man_made/tower/communication: + # 'man_made=tower, tower:type=communication' + name: Communication Tower + # 'terms: antenna,broadcast tower,cell phone tower,cell tower,communication mast,communication tower,guyed tower,mobile phone tower,radio mast,radio tower,television tower,transmission mast,transmission tower,tv tower' + terms: '' + man_made/tower/observation: + # 'man_made=tower, tower:type=observation' + name: Observation Tower + # 'terms: lookout tower,fire tower' + terms: '' man_made/wastewater_plant: # man_made=wastewater_plant name: Wastewater Plant diff --git a/data/presets/presets.json b/data/presets/presets.json index f7ebbce24..316cfac8e 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -594,8 +594,11 @@ "man_made/groyne": {"geometry": ["line", "area"], "tags": {"man_made": "groyne"}, "name": "Groyne"}, "man_made/lighthouse": {"icon": "maki-lighthouse", "fields": ["building_area"], "geometry": ["point", "area"], "tags": {"man_made": "lighthouse"}, "name": "Lighthouse"}, "man_made/mast": {"icon": "maki-communications-tower", "fields": ["tower/type", "tower/construction", "height", "communication_multi"], "geometry": ["point"], "terms": ["antenna", "broadcast tower", "cell phone tower", "cell tower", "communication mast", "communication tower", "guyed tower", "mobile phone tower", "radio mast", "radio tower", "television tower", "transmission mast", "transmission tower", "tv tower"], "tags": {"man_made": "mast"}, "name": "Mast"}, + "man_made/mast/communication": {"icon": "maki-communications-tower", "fields": ["tower/type", "tower/construction", "height", "communication_multi"], "geometry": ["point"], "terms": ["antenna", "broadcast tower", "cell phone tower", "cell tower", "communication mast", "communication tower", "guyed tower", "mobile phone tower", "radio mast", "radio tower", "television tower", "transmission mast", "transmission tower", "tv tower"], "tags": {"man_made": "mast", "tower:type": "communication"}, "reference": {"key": "tower:type", "value": "communication"}, "name": "Communication Mast"}, + "man_made/mast/communication/mobile_phone": {"icon": "maki-communications-tower", "fields": ["tower/type", "tower/construction", "height", "communication_multi"], "geometry": ["point"], "terms": ["antenna", "cell mast", "cell phone mast", "cell phone tower", "cell tower", "communication mast", "communication tower", "guyed tower", "mobile phone tower", "television tower", "transmission mast", "transmission tower"], "tags": {"man_made": "mast", "tower:type": "communication", "communication:mobile_phone": "yes"}, "reference": {"key": "communication:mobile_phone", "value": "yes"}, "name": "Mobile Phone Mast"}, + "man_made/mast/communication/radio": {"icon": "maki-communications-tower", "fields": ["tower/type", "tower/construction", "height", "communication_multi"], "geometry": ["point"], "terms": ["antenna", "broadcast tower", "communication mast", "communication tower", "guyed tower", "radio mast", "radio tower", "transmission mast", "transmission tower"], "tags": {"man_made": "mast", "tower:type": "communication", "communication:radio": "yes"}, "reference": {"key": "communication:radio", "value": "yes"}, "name": "Radio Broadcast Mast"}, + "man_made/mast/communication/television": {"icon": "maki-communications-tower", "fields": ["tower/type", "tower/construction", "height", "communication_multi"], "geometry": ["point"], "terms": ["antenna", "broadcast tower", "communication mast", "communication tower", "guyed tower", "television tower", "transmission mast", "transmission tower", "tv tower"], "tags": {"man_made": "mast", "tower:type": "communication", "communication:television": "yes"}, "reference": {"key": "communication:television", "value": "yes"}, "name": "Television Broadcast Mast"}, "man_made/monitoring_station": {"icon": "temaki-antenna", "geometry": ["point", "area"], "fields": ["monitoring_multi", "operator"], "terms": ["weather", "earthquake", "seismology", "air", "gps"], "tags": {"man_made": "monitoring_station"}, "name": "Monitoring Station"}, - "man_made/observation": {"icon": "temaki-tower", "geometry": ["point", "area"], "terms": ["lookout tower", "fire tower"], "tags": {"man_made": "tower", "tower:type": "observation"}, "name": "Observation Tower"}, "man_made/observatory": {"geometry": ["point", "area"], "terms": ["astronomical", "meteorological"], "tags": {"man_made": "observatory"}, "name": "Observatory"}, "man_made/petroleum_well": {"icon": "temaki-storage_tank", "geometry": ["point"], "terms": ["drilling rig", "oil derrick", "oil drill", "oil horse", "oil rig", "oil pump", "petroleum well", "pumpjack"], "tags": {"man_made": "petroleum_well"}, "name": "Oil Well"}, "man_made/pier": {"icon": "iD-highway-footway", "fields": ["name", "surface", "lit", "width", "access"], "geometry": ["line", "area"], "terms": ["dock", "jetty"], "tags": {"man_made": "pier"}, "name": "Pier"}, @@ -603,10 +606,12 @@ "man_made/pumping_station": {"icon": "maki-water", "geometry": ["point", "area"], "tags": {"man_made": "pumping_station"}, "name": "Pumping Station"}, "man_made/silo": {"icon": "temaki-silo", "fields": ["crop", "building_area"], "geometry": ["point", "area"], "terms": ["grain", "corn", "wheat"], "tags": {"man_made": "silo"}, "name": "Silo"}, "man_made/storage_tank": {"icon": "temaki-storage_tank", "fields": ["content", "building_area"], "geometry": ["point", "area"], "terms": ["water", "oil", "gas", "petrol"], "tags": {"man_made": "storage_tank"}, "name": "Storage Tank"}, - "man_made/surveillance_camera": {"icon": "maki-attraction", "geometry": ["point", "vertex"], "fields": ["surveillance", "surveillance/type", "camera/type", "camera/mount", "camera/direction", "surveillance/zone", "contact/webcam"], "terms": ["anpr", "alpr", "camera", "car plate recognition", "cctv", "guard", "license plate recognition", "monitoring", "number plate recognition", "security", "video", "webcam"], "tags": {"man_made": "surveillance", "surveillance:type": "camera"}, "name": "Surveillance Camera"}, "man_made/surveillance": {"icon": "maki-attraction", "geometry": ["point", "vertex"], "fields": ["surveillance", "surveillance/type", "surveillance/zone", "direction"], "terms": ["anpr", "alpr", "camera", "car plate recognition", "cctv", "guard", "license plate recognition", "monitoring", "number plate recognition", "security", "video", "webcam"], "tags": {"man_made": "surveillance"}, "name": "Surveillance"}, + "man_made/surveillance/camera": {"icon": "maki-attraction", "geometry": ["point", "vertex"], "fields": ["surveillance", "surveillance/type", "camera/type", "camera/mount", "camera/direction", "surveillance/zone", "contact/webcam"], "terms": ["anpr", "alpr", "camera", "car plate recognition", "cctv", "guard", "license plate recognition", "monitoring", "number plate recognition", "security", "video", "webcam"], "tags": {"man_made": "surveillance", "surveillance:type": "camera"}, "name": "Surveillance Camera"}, "man_made/survey_point": {"icon": "maki-monument", "fields": ["ref"], "geometry": ["point", "vertex"], "terms": ["trig point", "triangulation pillar", "trigonometrical station"], "tags": {"man_made": "survey_point"}, "name": "Survey Point"}, "man_made/tower": {"icon": "temaki-tower", "fields": ["tower/type", "tower/construction", "height"], "geometry": ["point", "area"], "tags": {"man_made": "tower"}, "name": "Tower"}, + "man_made/tower/communication": {"icon": "temaki-tower", "fields": ["tower/type", "tower/construction", "height", "communication_multi"], "geometry": ["point", "area"], "terms": ["antenna", "broadcast tower", "cell phone tower", "cell tower", "communication mast", "communication tower", "guyed tower", "mobile phone tower", "radio mast", "radio tower", "television tower", "transmission mast", "transmission tower", "tv tower"], "tags": {"man_made": "tower", "tower:type": "communication"}, "reference": {"key": "tower:type", "value": "communication"}, "name": "Communication Tower"}, + "man_made/tower/observation": {"icon": "temaki-tower", "fields": ["tower/type", "tower/construction", "height"], "geometry": ["point", "area"], "terms": ["lookout tower", "fire tower"], "tags": {"man_made": "tower", "tower:type": "observation"}, "reference": {"key": "tower:type", "value": "observation"}, "name": "Observation Tower"}, "man_made/wastewater_plant": {"icon": "maki-water", "fields": ["name", "operator", "address"], "geometry": ["point", "area"], "terms": ["sewage*", "water treatment plant", "reclamation plant"], "tags": {"man_made": "wastewater_plant"}, "name": "Wastewater Plant"}, "man_made/water_tower": {"icon": "maki-water", "fields": ["operator"], "geometry": ["point", "area"], "tags": {"man_made": "water_tower"}, "name": "Water Tower"}, "man_made/water_well": {"icon": "maki-water", "fields": ["operator"], "geometry": ["point", "area"], "tags": {"man_made": "water_well"}, "name": "Water Well"}, diff --git a/data/presets/presets/man_made/mast/communication.json b/data/presets/presets/man_made/mast/communication.json new file mode 100644 index 000000000..a67d1fcf1 --- /dev/null +++ b/data/presets/presets/man_made/mast/communication.json @@ -0,0 +1,37 @@ +{ + "icon": "maki-communications-tower", + "fields": [ + "tower/type", + "tower/construction", + "height", + "communication_multi" + ], + "geometry": [ + "point" + ], + "terms": [ + "antenna", + "broadcast tower", + "cell phone tower", + "cell tower", + "communication mast", + "communication tower", + "guyed tower", + "mobile phone tower", + "radio mast", + "radio tower", + "television tower", + "transmission mast", + "transmission tower", + "tv tower" + ], + "tags": { + "man_made": "mast", + "tower:type": "communication" + }, + "reference": { + "key": "tower:type", + "value": "communication" + }, + "name": "Communication Mast" +} diff --git a/data/presets/presets/man_made/mast/communication/mobile_phone.json b/data/presets/presets/man_made/mast/communication/mobile_phone.json new file mode 100644 index 000000000..33a30fa8a --- /dev/null +++ b/data/presets/presets/man_made/mast/communication/mobile_phone.json @@ -0,0 +1,36 @@ +{ + "icon": "maki-communications-tower", + "fields": [ + "tower/type", + "tower/construction", + "height", + "communication_multi" + ], + "geometry": [ + "point" + ], + "terms": [ + "antenna", + "cell mast", + "cell phone mast", + "cell phone tower", + "cell tower", + "communication mast", + "communication tower", + "guyed tower", + "mobile phone tower", + "television tower", + "transmission mast", + "transmission tower" + ], + "tags": { + "man_made": "mast", + "tower:type": "communication", + "communication:mobile_phone": "yes" + }, + "reference": { + "key": "communication:mobile_phone", + "value": "yes" + }, + "name": "Mobile Phone Mast" +} diff --git a/data/presets/presets/man_made/mast/communication/radio.json b/data/presets/presets/man_made/mast/communication/radio.json new file mode 100644 index 000000000..eaa08ad9d --- /dev/null +++ b/data/presets/presets/man_made/mast/communication/radio.json @@ -0,0 +1,33 @@ +{ + "icon": "maki-communications-tower", + "fields": [ + "tower/type", + "tower/construction", + "height", + "communication_multi" + ], + "geometry": [ + "point" + ], + "terms": [ + "antenna", + "broadcast tower", + "communication mast", + "communication tower", + "guyed tower", + "radio mast", + "radio tower", + "transmission mast", + "transmission tower" + ], + "tags": { + "man_made": "mast", + "tower:type": "communication", + "communication:radio": "yes" + }, + "reference": { + "key": "communication:radio", + "value": "yes" + }, + "name": "Radio Broadcast Mast" +} diff --git a/data/presets/presets/man_made/mast/communication/television.json b/data/presets/presets/man_made/mast/communication/television.json new file mode 100644 index 000000000..8943a47fb --- /dev/null +++ b/data/presets/presets/man_made/mast/communication/television.json @@ -0,0 +1,33 @@ +{ + "icon": "maki-communications-tower", + "fields": [ + "tower/type", + "tower/construction", + "height", + "communication_multi" + ], + "geometry": [ + "point" + ], + "terms": [ + "antenna", + "broadcast tower", + "communication mast", + "communication tower", + "guyed tower", + "television tower", + "transmission mast", + "transmission tower", + "tv tower" + ], + "tags": { + "man_made": "mast", + "tower:type": "communication", + "communication:television": "yes" + }, + "reference": { + "key": "communication:television", + "value": "yes" + }, + "name": "Television Broadcast Mast" +} diff --git a/data/presets/presets/man_made/surveillance_camera.json b/data/presets/presets/man_made/surveillance/camera.json similarity index 100% rename from data/presets/presets/man_made/surveillance_camera.json rename to data/presets/presets/man_made/surveillance/camera.json diff --git a/data/presets/presets/man_made/tower/communication.json b/data/presets/presets/man_made/tower/communication.json new file mode 100644 index 000000000..ecdaa2363 --- /dev/null +++ b/data/presets/presets/man_made/tower/communication.json @@ -0,0 +1,38 @@ +{ + "icon": "temaki-tower", + "fields": [ + "tower/type", + "tower/construction", + "height", + "communication_multi" + ], + "geometry": [ + "point", + "area" + ], + "terms": [ + "antenna", + "broadcast tower", + "cell phone tower", + "cell tower", + "communication mast", + "communication tower", + "guyed tower", + "mobile phone tower", + "radio mast", + "radio tower", + "television tower", + "transmission mast", + "transmission tower", + "tv tower" + ], + "tags": { + "man_made": "tower", + "tower:type": "communication" + }, + "reference": { + "key": "tower:type", + "value": "communication" + }, + "name": "Communication Tower" +} diff --git a/data/presets/presets/man_made/observation.json b/data/presets/presets/man_made/tower/observation.json similarity index 60% rename from data/presets/presets/man_made/observation.json rename to data/presets/presets/man_made/tower/observation.json index 960ff6f3f..8b2e61b7d 100644 --- a/data/presets/presets/man_made/observation.json +++ b/data/presets/presets/man_made/tower/observation.json @@ -1,5 +1,10 @@ { "icon": "temaki-tower", + "fields": [ + "tower/type", + "tower/construction", + "height" + ], "geometry": [ "point", "area" @@ -12,5 +17,9 @@ "man_made": "tower", "tower:type": "observation" }, + "reference": { + "key": "tower:type", + "value": "observation" + }, "name": "Observation Tower" } diff --git a/data/taginfo.json b/data/taginfo.json index 2a8d6e273..96dc57fc7 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -3977,6 +3977,34 @@ "object_types": ["node"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/communications-tower-15.svg?sanitize=true" }, + { + "key": "tower:type", + "value": "communication", + "description": "Communication Mast, Communication Tower", + "object_types": ["node"], + "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/communications-tower-15.svg?sanitize=true" + }, + { + "key": "communication:mobile_phone", + "value": "yes", + "description": "Mobile Phone Mast", + "object_types": ["node"], + "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/communications-tower-15.svg?sanitize=true" + }, + { + "key": "communication:radio", + "value": "yes", + "description": "Radio Broadcast Mast", + "object_types": ["node"], + "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/communications-tower-15.svg?sanitize=true" + }, + { + "key": "communication:television", + "value": "yes", + "description": "Television Broadcast Mast", + "object_types": ["node"], + "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/communications-tower-15.svg?sanitize=true" + }, { "key": "man_made", "value": "monitoring_station", @@ -3984,13 +4012,6 @@ "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/bhousel/temaki/master/icons/antenna.svg?sanitize=true" }, - { - "key": "tower:type", - "value": "observation", - "description": "Observation Tower", - "object_types": ["node", "area"], - "icon_url": "https://raw.githubusercontent.com/bhousel/temaki/master/icons/tower.svg?sanitize=true" - }, { "key": "man_made", "value": "observatory", @@ -4040,16 +4061,16 @@ "icon_url": "https://raw.githubusercontent.com/bhousel/temaki/master/icons/storage_tank.svg?sanitize=true" }, { - "key": "surveillance:type", - "value": "camera", - "description": "Surveillance Camera, Surveillance Type", + "key": "man_made", + "value": "surveillance", + "description": "Surveillance", "object_types": ["node"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/attraction-15.svg?sanitize=true" }, { - "key": "man_made", - "value": "surveillance", - "description": "Surveillance", + "key": "surveillance:type", + "value": "camera", + "description": "Surveillance Camera, Surveillance Type", "object_types": ["node"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/attraction-15.svg?sanitize=true" }, @@ -4067,6 +4088,13 @@ "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/bhousel/temaki/master/icons/tower.svg?sanitize=true" }, + { + "key": "tower:type", + "value": "observation", + "description": "Observation Tower", + "object_types": ["node", "area"], + "icon_url": "https://raw.githubusercontent.com/bhousel/temaki/master/icons/tower.svg?sanitize=true" + }, { "key": "man_made", "value": "wastewater_plant", diff --git a/dist/locales/en.json b/dist/locales/en.json index ee73a0156..ce67b9bd3 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -5552,14 +5552,26 @@ "name": "Mast", "terms": "antenna,broadcast tower,cell phone tower,cell tower,communication mast,communication tower,guyed tower,mobile phone tower,radio mast,radio tower,television tower,transmission mast,transmission tower,tv tower" }, + "man_made/mast/communication": { + "name": "Communication Mast", + "terms": "antenna,broadcast tower,cell phone tower,cell tower,communication mast,communication tower,guyed tower,mobile phone tower,radio mast,radio tower,television tower,transmission mast,transmission tower,tv tower" + }, + "man_made/mast/communication/mobile_phone": { + "name": "Mobile Phone Mast", + "terms": "antenna,cell mast,cell phone mast,cell phone tower,cell tower,communication mast,communication tower,guyed tower,mobile phone tower,television tower,transmission mast,transmission tower" + }, + "man_made/mast/communication/radio": { + "name": "Radio Broadcast Mast", + "terms": "antenna,broadcast tower,communication mast,communication tower,guyed tower,radio mast,radio tower,transmission mast,transmission tower" + }, + "man_made/mast/communication/television": { + "name": "Television Broadcast Mast", + "terms": "antenna,broadcast tower,communication mast,communication tower,guyed tower,television tower,transmission mast,transmission tower,tv tower" + }, "man_made/monitoring_station": { "name": "Monitoring Station", "terms": "weather,earthquake,seismology,air,gps" }, - "man_made/observation": { - "name": "Observation Tower", - "terms": "lookout tower,fire tower" - }, "man_made/observatory": { "name": "Observatory", "terms": "astronomical,meteorological" @@ -5588,14 +5600,14 @@ "name": "Storage Tank", "terms": "water,oil,gas,petrol" }, - "man_made/surveillance_camera": { - "name": "Surveillance Camera", - "terms": "anpr,alpr,camera,car plate recognition,cctv,guard,license plate recognition,monitoring,number plate recognition,security,video,webcam" - }, "man_made/surveillance": { "name": "Surveillance", "terms": "anpr,alpr,camera,car plate recognition,cctv,guard,license plate recognition,monitoring,number plate recognition,security,video,webcam" }, + "man_made/surveillance/camera": { + "name": "Surveillance Camera", + "terms": "anpr,alpr,camera,car plate recognition,cctv,guard,license plate recognition,monitoring,number plate recognition,security,video,webcam" + }, "man_made/survey_point": { "name": "Survey Point", "terms": "trig point,triangulation pillar,trigonometrical station" @@ -5604,6 +5616,14 @@ "name": "Tower", "terms": "" }, + "man_made/tower/communication": { + "name": "Communication Tower", + "terms": "antenna,broadcast tower,cell phone tower,cell tower,communication mast,communication tower,guyed tower,mobile phone tower,radio mast,radio tower,television tower,transmission mast,transmission tower,tv tower" + }, + "man_made/tower/observation": { + "name": "Observation Tower", + "terms": "lookout tower,fire tower" + }, "man_made/wastewater_plant": { "name": "Wastewater Plant", "terms": "sewage*,water treatment plant,reclamation plant" From acd1a6ab51a518d0e88ae775864e6e6d24ee3abf Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 12 Nov 2018 21:25:28 -0800 Subject: [PATCH 3/8] Updates terms for Mobile Phone Mast and Television Mast presets --- data/presets.yaml | 4 ++-- data/presets/presets.json | 4 ++-- .../presets/man_made/mast/communication/mobile_phone.json | 1 - .../presets/man_made/mast/communication/television.json | 2 ++ dist/locales/en.json | 4 ++-- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/data/presets.yaml b/data/presets.yaml index 7f3855597..0dd5a484f 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -4718,7 +4718,7 @@ en: man_made/mast/communication/mobile_phone: # 'man_made=mast, tower:type=communication, communication:mobile_phone=yes' name: Mobile Phone Mast - # 'terms: antenna,cell mast,cell phone mast,cell phone tower,cell tower,communication mast,communication tower,guyed tower,mobile phone tower,television tower,transmission mast,transmission tower' + # 'terms: antenna,cell mast,cell phone mast,cell phone tower,cell tower,communication mast,communication tower,guyed tower,mobile phone tower,transmission mast,transmission tower' terms: '' man_made/mast/communication/radio: # 'man_made=mast, tower:type=communication, communication:radio=yes' @@ -4728,7 +4728,7 @@ en: man_made/mast/communication/television: # 'man_made=mast, tower:type=communication, communication:television=yes' name: Television Broadcast Mast - # 'terms: antenna,broadcast tower,communication mast,communication tower,guyed tower,television tower,transmission mast,transmission tower,tv tower' + # 'terms: antenna,broadcast tower,communication mast,communication tower,guyed tower,television mast,television tower,transmission mast,transmission tower,tv mast,tv tower' terms: '' man_made/monitoring_station: # man_made=monitoring_station diff --git a/data/presets/presets.json b/data/presets/presets.json index 316cfac8e..5d15f16d2 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -595,9 +595,9 @@ "man_made/lighthouse": {"icon": "maki-lighthouse", "fields": ["building_area"], "geometry": ["point", "area"], "tags": {"man_made": "lighthouse"}, "name": "Lighthouse"}, "man_made/mast": {"icon": "maki-communications-tower", "fields": ["tower/type", "tower/construction", "height", "communication_multi"], "geometry": ["point"], "terms": ["antenna", "broadcast tower", "cell phone tower", "cell tower", "communication mast", "communication tower", "guyed tower", "mobile phone tower", "radio mast", "radio tower", "television tower", "transmission mast", "transmission tower", "tv tower"], "tags": {"man_made": "mast"}, "name": "Mast"}, "man_made/mast/communication": {"icon": "maki-communications-tower", "fields": ["tower/type", "tower/construction", "height", "communication_multi"], "geometry": ["point"], "terms": ["antenna", "broadcast tower", "cell phone tower", "cell tower", "communication mast", "communication tower", "guyed tower", "mobile phone tower", "radio mast", "radio tower", "television tower", "transmission mast", "transmission tower", "tv tower"], "tags": {"man_made": "mast", "tower:type": "communication"}, "reference": {"key": "tower:type", "value": "communication"}, "name": "Communication Mast"}, - "man_made/mast/communication/mobile_phone": {"icon": "maki-communications-tower", "fields": ["tower/type", "tower/construction", "height", "communication_multi"], "geometry": ["point"], "terms": ["antenna", "cell mast", "cell phone mast", "cell phone tower", "cell tower", "communication mast", "communication tower", "guyed tower", "mobile phone tower", "television tower", "transmission mast", "transmission tower"], "tags": {"man_made": "mast", "tower:type": "communication", "communication:mobile_phone": "yes"}, "reference": {"key": "communication:mobile_phone", "value": "yes"}, "name": "Mobile Phone Mast"}, + "man_made/mast/communication/mobile_phone": {"icon": "maki-communications-tower", "fields": ["tower/type", "tower/construction", "height", "communication_multi"], "geometry": ["point"], "terms": ["antenna", "cell mast", "cell phone mast", "cell phone tower", "cell tower", "communication mast", "communication tower", "guyed tower", "mobile phone tower", "transmission mast", "transmission tower"], "tags": {"man_made": "mast", "tower:type": "communication", "communication:mobile_phone": "yes"}, "reference": {"key": "communication:mobile_phone", "value": "yes"}, "name": "Mobile Phone Mast"}, "man_made/mast/communication/radio": {"icon": "maki-communications-tower", "fields": ["tower/type", "tower/construction", "height", "communication_multi"], "geometry": ["point"], "terms": ["antenna", "broadcast tower", "communication mast", "communication tower", "guyed tower", "radio mast", "radio tower", "transmission mast", "transmission tower"], "tags": {"man_made": "mast", "tower:type": "communication", "communication:radio": "yes"}, "reference": {"key": "communication:radio", "value": "yes"}, "name": "Radio Broadcast Mast"}, - "man_made/mast/communication/television": {"icon": "maki-communications-tower", "fields": ["tower/type", "tower/construction", "height", "communication_multi"], "geometry": ["point"], "terms": ["antenna", "broadcast tower", "communication mast", "communication tower", "guyed tower", "television tower", "transmission mast", "transmission tower", "tv tower"], "tags": {"man_made": "mast", "tower:type": "communication", "communication:television": "yes"}, "reference": {"key": "communication:television", "value": "yes"}, "name": "Television Broadcast Mast"}, + "man_made/mast/communication/television": {"icon": "maki-communications-tower", "fields": ["tower/type", "tower/construction", "height", "communication_multi"], "geometry": ["point"], "terms": ["antenna", "broadcast tower", "communication mast", "communication tower", "guyed tower", "television mast", "television tower", "transmission mast", "transmission tower", "tv mast", "tv tower"], "tags": {"man_made": "mast", "tower:type": "communication", "communication:television": "yes"}, "reference": {"key": "communication:television", "value": "yes"}, "name": "Television Broadcast Mast"}, "man_made/monitoring_station": {"icon": "temaki-antenna", "geometry": ["point", "area"], "fields": ["monitoring_multi", "operator"], "terms": ["weather", "earthquake", "seismology", "air", "gps"], "tags": {"man_made": "monitoring_station"}, "name": "Monitoring Station"}, "man_made/observatory": {"geometry": ["point", "area"], "terms": ["astronomical", "meteorological"], "tags": {"man_made": "observatory"}, "name": "Observatory"}, "man_made/petroleum_well": {"icon": "temaki-storage_tank", "geometry": ["point"], "terms": ["drilling rig", "oil derrick", "oil drill", "oil horse", "oil rig", "oil pump", "petroleum well", "pumpjack"], "tags": {"man_made": "petroleum_well"}, "name": "Oil Well"}, diff --git a/data/presets/presets/man_made/mast/communication/mobile_phone.json b/data/presets/presets/man_made/mast/communication/mobile_phone.json index 33a30fa8a..68591951a 100644 --- a/data/presets/presets/man_made/mast/communication/mobile_phone.json +++ b/data/presets/presets/man_made/mast/communication/mobile_phone.json @@ -19,7 +19,6 @@ "communication tower", "guyed tower", "mobile phone tower", - "television tower", "transmission mast", "transmission tower" ], diff --git a/data/presets/presets/man_made/mast/communication/television.json b/data/presets/presets/man_made/mast/communication/television.json index 8943a47fb..4484679a3 100644 --- a/data/presets/presets/man_made/mast/communication/television.json +++ b/data/presets/presets/man_made/mast/communication/television.json @@ -15,9 +15,11 @@ "communication mast", "communication tower", "guyed tower", + "television mast", "television tower", "transmission mast", "transmission tower", + "tv mast", "tv tower" ], "tags": { diff --git a/dist/locales/en.json b/dist/locales/en.json index ce67b9bd3..7e95240b7 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -5558,7 +5558,7 @@ }, "man_made/mast/communication/mobile_phone": { "name": "Mobile Phone Mast", - "terms": "antenna,cell mast,cell phone mast,cell phone tower,cell tower,communication mast,communication tower,guyed tower,mobile phone tower,television tower,transmission mast,transmission tower" + "terms": "antenna,cell mast,cell phone mast,cell phone tower,cell tower,communication mast,communication tower,guyed tower,mobile phone tower,transmission mast,transmission tower" }, "man_made/mast/communication/radio": { "name": "Radio Broadcast Mast", @@ -5566,7 +5566,7 @@ }, "man_made/mast/communication/television": { "name": "Television Broadcast Mast", - "terms": "antenna,broadcast tower,communication mast,communication tower,guyed tower,television tower,transmission mast,transmission tower,tv tower" + "terms": "antenna,broadcast tower,communication mast,communication tower,guyed tower,television mast,television tower,transmission mast,transmission tower,tv mast,tv tower" }, "man_made/monitoring_station": { "name": "Monitoring Station", From bb30cbf555b9e0df7323d35ed77b8d44e889ece7 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 13 Nov 2018 13:42:09 -0500 Subject: [PATCH 4/8] Move lib/d3.keybinding.js -> util/keybinding.js almost none of the original d3 "plugin" code remains --- modules/behavior/copy.js | 4 +- modules/behavior/draw.js | 5 +- modules/behavior/draw_way.js | 8 +- modules/behavior/hover.js | 7 +- modules/behavior/operation.js | 4 +- modules/behavior/paste.js | 5 +- modules/lib/index.js | 1 - modules/modes/drag_node.js | 8 +- modules/modes/move.js | 4 +- modules/modes/rotate.js | 12 +-- modules/modes/save.js | 6 +- modules/modes/select.js | 44 ++++----- modules/modes/select_data.js | 8 +- modules/modes/select_note.js | 12 +-- modules/ui/background.js | 6 +- modules/ui/conflicts.js | 5 +- modules/ui/feature_list.js | 5 +- modules/ui/full_screen.js | 5 +- modules/ui/help.js | 8 +- modules/ui/info.js | 5 +- modules/ui/init.js | 5 +- modules/ui/map_data.js | 5 +- modules/ui/map_in_map.js | 6 +- modules/ui/modal.js | 5 +- modules/ui/modes.js | 4 +- modules/ui/preset_list.js | 91 +++++++++---------- modules/ui/save.js | 5 +- modules/ui/shortcuts.js | 39 ++++---- modules/ui/undo_redo.js | 5 +- modules/ui/zoom.js | 5 +- modules/util/index.js | 1 + .../d3.keybinding.js => util/keybinding.js} | 66 +++++++------- test/index.html | 2 +- test/spec/lib/d3.combobox.js | 2 +- .../d3.keybinding.js => util/keybinding.js} | 4 +- 35 files changed, 176 insertions(+), 231 deletions(-) rename modules/{lib/d3.keybinding.js => util/keybinding.js} (88%) rename test/spec/{lib/d3.keybinding.js => util/keybinding.js} (97%) diff --git a/modules/behavior/copy.js b/modules/behavior/copy.js index 4aaca957f..247bc6ba2 100644 --- a/modules/behavior/copy.js +++ b/modules/behavior/copy.js @@ -7,12 +7,12 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; +import { utilKeybinding } from '../util'; import { uiCmd } from '../ui'; export function behaviorCopy(context) { - var keybinding = d3_keybinding('copy'); + var keybinding = utilKeybinding('copy'); function groupEntities(ids, graph) { diff --git a/modules/behavior/draw.js b/modules/behavior/draw.js index 54517c62d..8640b4ce0 100644 --- a/modules/behavior/draw.js +++ b/modules/behavior/draw.js @@ -7,12 +7,11 @@ import { touches as d3_touches } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; import { behaviorEdit } from './edit'; import { behaviorHover } from './hover'; import { behaviorTail } from './tail'; import { geoChooseEdge, geoVecLength } from '../geo'; -import { utilRebind } from '../util/rebind'; +import { utilKeybinding, utilRebind } from '../util'; var _usedTails = {}; @@ -25,7 +24,7 @@ export function behaviorDraw(context) { 'move', 'click', 'clickWay', 'clickNode', 'undo', 'cancel', 'finish' ); - var keybinding = d3_keybinding('draw'); + var keybinding = utilKeybinding('draw'); var hover = behaviorHover(context).altDisables(true) .on('hover', context.ui().sidebar.hover); diff --git a/modules/behavior/draw_way.js b/modules/behavior/draw_way.js index 148a6fd89..e99da7b32 100644 --- a/modules/behavior/draw_way.js +++ b/modules/behavior/draw_way.js @@ -5,8 +5,6 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { actionAddMidpoint, actionMoveNode, @@ -17,6 +15,7 @@ import { behaviorDraw } from './draw'; import { geoChooseEdge, geoHasSelfIntersections } from '../geo'; import { modeBrowse, modeSelect } from '../modes'; import { osmNode } from '../osm'; +import { utilKeybinding } from '../util'; export function behaviorDrawWay(context, wayId, index, mode, startGraph) { @@ -41,9 +40,8 @@ export function behaviorDrawWay(context, wayId, index, mode, startGraph) { _tempEdits++; - function keydown() { - if (d3_event.keyCode === d3_keybinding.modifierCodes.alt) { + if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) { if (context.surface().classed('nope')) { context.surface() .classed('nope-suppressed', true); @@ -56,7 +54,7 @@ export function behaviorDrawWay(context, wayId, index, mode, startGraph) { function keyup() { - if (d3_event.keyCode === d3_keybinding.modifierCodes.alt) { + if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) { if (context.surface().classed('nope-suppressed')) { context.surface() .classed('nope', true); diff --git a/modules/behavior/hover.js b/modules/behavior/hover.js index 99d12a9dc..fc0ef8a7a 100644 --- a/modules/behavior/hover.js +++ b/modules/behavior/hover.js @@ -5,9 +5,8 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; import { osmEntity, osmNote } from '../osm'; -import { utilRebind } from '../util/rebind'; +import { utilKeybinding, utilRebind } from '../util'; /* @@ -29,7 +28,7 @@ export function behaviorHover(context) { function keydown() { - if (_altDisables && d3_event.keyCode === d3_keybinding.modifierCodes.alt) { + if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) { _selection.selectAll('.hover') .classed('hover-suppressed', true) .classed('hover', false); @@ -43,7 +42,7 @@ export function behaviorHover(context) { function keyup() { - if (_altDisables && d3_event.keyCode === d3_keybinding.modifierCodes.alt) { + if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) { _selection.selectAll('.hover-suppressed') .classed('hover-suppressed', false) .classed('hover', true); diff --git a/modules/behavior/operation.js b/modules/behavior/operation.js index 0fc96e1f0..5e3e03897 100644 --- a/modules/behavior/operation.js +++ b/modules/behavior/operation.js @@ -3,8 +3,8 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; import { uiFlash } from '../ui'; +import { utilKeybinding } from '../util'; /* Creates a keybinding behavior for an operation */ @@ -14,7 +14,7 @@ export function behaviorOperation() { var behavior = function () { if (_operation && _operation.available()) { - keybinding = d3_keybinding('behavior.key.' + _operation.id); + keybinding = utilKeybinding('behavior.key.' + _operation.id); keybinding.on(_operation.keys, function() { d3_event.preventDefault(); var disabled = _operation.disabled(); diff --git a/modules/behavior/paste.js b/modules/behavior/paste.js index 6a2f44326..a663309b8 100644 --- a/modules/behavior/paste.js +++ b/modules/behavior/paste.js @@ -6,8 +6,6 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { actionCopyEntities, actionMove @@ -21,10 +19,11 @@ import { import { modeMove } from '../modes'; import { uiCmd } from '../ui'; +import { utilKeybinding } from '../util'; export function behaviorPaste(context) { - var keybinding = d3_keybinding('paste'); + var keybinding = utilKeybinding('paste'); function doPaste() { diff --git a/modules/lib/index.js b/modules/lib/index.js index bee98416c..1cb15f749 100644 --- a/modules/lib/index.js +++ b/modules/lib/index.js @@ -1,2 +1 @@ export { d3combobox } from './d3.combobox'; -export { d3keybinding } from './d3.keybinding'; diff --git a/modules/modes/drag_node.js b/modules/modes/drag_node.js index a395d98fb..9753f8e1e 100644 --- a/modules/modes/drag_node.js +++ b/modules/modes/drag_node.js @@ -6,8 +6,6 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { t } from '../util/locale'; import { @@ -34,6 +32,8 @@ import { import { modeBrowse, modeSelect } from './index'; import { osmJoinWays, osmNode } from '../osm'; import { uiFlash } from '../ui'; +import { utilKeybinding } from '../util'; + export function modeDragNode(context) { @@ -102,7 +102,7 @@ export function modeDragNode(context) { function keydown() { - if (d3_event.keyCode === d3_keybinding.modifierCodes.alt) { + if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) { if (context.surface().classed('nope')) { context.surface() .classed('nope-suppressed', true); @@ -115,7 +115,7 @@ export function modeDragNode(context) { function keyup() { - if (d3_event.keyCode === d3_keybinding.modifierCodes.alt) { + if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) { if (context.surface().classed('nope-suppressed')) { context.surface() .classed('nope', true); diff --git a/modules/modes/move.js b/modules/modes/move.js index 8cfbbe6b7..1a9704e4b 100644 --- a/modules/modes/move.js +++ b/modules/modes/move.js @@ -3,13 +3,13 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; import { t } from '../util/locale'; import { actionMove } from '../actions'; import { behaviorEdit } from '../behavior'; import { geoViewportEdge, geoVecSubtract } from '../geo'; import { modeBrowse, modeSelect } from './index'; +import { utilKeybinding } from '../util'; import { operationCircularize, @@ -27,7 +27,7 @@ export function modeMove(context, entityIDs, baseGraph) { button: 'browse' }; - var keybinding = d3_keybinding('move'); + var keybinding = utilKeybinding('move'); var behaviors = [ behaviorEdit(context), operationCircularize(entityIDs, context).behavior, diff --git a/modules/modes/rotate.js b/modules/modes/rotate.js index 12cac0fdd..1b0f30248 100644 --- a/modules/modes/rotate.js +++ b/modules/modes/rotate.js @@ -8,17 +8,11 @@ import { polygonCentroid as d3_polygonCentroid } from 'd3-polygon'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { t } from '../util/locale'; import { actionRotate } from '../actions'; import { behaviorEdit } from '../behavior'; import { geoVecInterp } from '../geo'; - -import { - modeBrowse, - modeSelect -} from './index'; +import { modeBrowse, modeSelect } from './index'; import { operationCircularize, @@ -29,7 +23,7 @@ import { operationReflectShort } from '../operations'; -import { utilGetAllNodes } from '../util'; +import { utilGetAllNodes, utilKeybinding } from '../util'; export function modeRotate(context, entityIDs) { @@ -38,7 +32,7 @@ export function modeRotate(context, entityIDs) { button: 'browse' }; - var keybinding = d3_keybinding('rotate'); + var keybinding = utilKeybinding('rotate'); var behaviors = [ behaviorEdit(context), operationCircularize(entityIDs, context).behavior, diff --git a/modules/modes/save.js b/modules/modes/save.js index 5e67bd163..5b1ef162f 100644 --- a/modules/modes/save.js +++ b/modules/modes/save.js @@ -11,7 +11,6 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; import { t } from '../util/locale'; import { @@ -40,7 +39,8 @@ import { import { utilDisplayName, - utilDisplayType + utilDisplayType, + utilKeybinding } from '../util'; @@ -49,7 +49,7 @@ var _isSaving = false; export function modeSave(context) { var mode = { id: 'save' }; - var keybinding = d3_keybinding('modeSave'); + var keybinding = utilKeybinding('modeSave'); var loading = uiLoading(context) .message(t('save.uploading')) diff --git a/modules/modes/select.js b/modules/modes/select.js index 1d19eccc3..8e47d6aed 100644 --- a/modules/modes/select.js +++ b/modules/modes/select.js @@ -9,7 +9,6 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; import { t } from '../util/locale'; import { actionAddMidpoint } from '../actions'; @@ -23,30 +22,21 @@ import { behaviorSelect } from '../behavior'; -import { - geoExtent, - geoChooseEdge, - geoPointInPolygon -} from '../geo'; - -import { - osmNode, - osmWay -} from '../osm'; - +import { geoExtent, geoChooseEdge, geoPointInPolygon } from '../geo'; import { modeBrowse } from './browse'; import { modeDragNode } from './drag_node'; import { modeDragNote } from './drag_note'; +import { osmNode, osmWay } from '../osm'; import * as Operations from '../operations/index'; import { uiEditMenu, uiSelectionList } from '../ui'; import { uiCmd } from '../ui/cmd'; -import { utilEntityOrMemberSelector, utilEntitySelector } from '../util'; +import { utilEntityOrMemberSelector, utilEntitySelector, utilKeybinding } from '../util'; // deprecation warning - Radial Menu to be removed in iD v3 import { uiRadialMenu } from '../ui'; -var relatedParent; +var _relatedParent; export function modeSelect(context, selectedIDs) { @@ -55,7 +45,7 @@ export function modeSelect(context, selectedIDs) { button: 'browse' }; - var keybinding = d3_keybinding('select'); + var keybinding = utilKeybinding('select'); var timeout = null; var behaviors = [ behaviorCopy(context), @@ -132,7 +122,7 @@ export function modeSelect(context, selectedIDs) { function singularParent() { var parents = commonParents(); if (!parents || parents.length === 0) { - relatedParent = null; + _relatedParent = null; return null; } @@ -140,12 +130,12 @@ export function modeSelect(context, selectedIDs) { // parents, and we want to remember which parent line we started on. if (parents.length === 1) { - relatedParent = parents[0]; // remember this parent for later - return relatedParent; + _relatedParent = parents[0]; // remember this parent for later + return _relatedParent; } - if (parents.indexOf(relatedParent) !== -1) { - return relatedParent; // prefer the previously seen parent + if (parents.indexOf(_relatedParent) !== -1) { + return _relatedParent; // prefer the previously seen parent } return parents[0]; @@ -292,8 +282,8 @@ export function modeSelect(context, selectedIDs) { .classed('related', false); singularParent(); - if (relatedParent) { - surface.selectAll(utilEntitySelector([relatedParent])) + if (_relatedParent) { + surface.selectAll(utilEntitySelector([_relatedParent])) .classed('related', true); } @@ -412,19 +402,19 @@ export function modeSelect(context, selectedIDs) { var parents = _uniq(commonParents()); if (!parents || parents.length < 2) return; - var index = parents.indexOf(relatedParent); + var index = parents.indexOf(_relatedParent); if (index < 0 || index > parents.length - 2) { - relatedParent = parents[0]; + _relatedParent = parents[0]; } else { - relatedParent = parents[index + 1]; + _relatedParent = parents[index + 1]; } var surface = context.surface(); surface.selectAll('.related') .classed('related', false); - if (relatedParent) { - surface.selectAll(utilEntitySelector([relatedParent])) + if (_relatedParent) { + surface.selectAll(utilEntitySelector([_relatedParent])) .classed('related', true); } } diff --git a/modules/modes/select_data.js b/modules/modes/select_data.js index 89cde781e..e7b9f8bf8 100644 --- a/modules/modes/select_data.js +++ b/modules/modes/select_data.js @@ -6,8 +6,6 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { behaviorBreathe, behaviorHover, @@ -16,9 +14,9 @@ import { } from '../behavior'; import { geoExtent } from '../geo'; -import { modeDragNode, modeDragNote } from '../modes'; -import { modeBrowse } from './browse'; +import { modeBrowse, modeDragNode, modeDragNote } from '../modes'; import { uiDataEditor } from '../ui'; +import { utilKeybinding } from '../util'; export function modeSelectData(context, selectedDatum) { @@ -27,7 +25,7 @@ export function modeSelectData(context, selectedDatum) { button: 'browse' }; - var keybinding = d3_keybinding('select-data'); + var keybinding = utilKeybinding('select-data'); var dataEditor = uiDataEditor(context); var behaviors = [ diff --git a/modules/modes/select_note.js b/modules/modes/select_note.js index 67ae6b1c5..43606540d 100644 --- a/modules/modes/select_note.js +++ b/modules/modes/select_note.js @@ -3,8 +3,6 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { behaviorBreathe, behaviorHover, @@ -12,14 +10,10 @@ import { behaviorSelect } from '../behavior'; -import { - modeDragNode, - modeDragNote -} from '../modes'; - +import { modeBrowse, modeDragNode, modeDragNote } from '../modes'; import { services } from '../services'; -import { modeBrowse } from './browse'; import { uiNoteEditor } from '../ui'; +import { utilKeybinding } from '../util'; export function modeSelectNote(context, selectedNoteID) { @@ -29,7 +23,7 @@ export function modeSelectNote(context, selectedNoteID) { }; var osm = services.osm; - var keybinding = d3_keybinding('select-note'); + var keybinding = utilKeybinding('select-note'); var noteEditor = uiNoteEditor(context) .on('change', function() { context.map().pan([0,0]); // trigger a redraw diff --git a/modules/ui/background.js b/modules/ui/background.js index 6f91098b8..c95fc59c2 100644 --- a/modules/ui/background.js +++ b/modules/ui/background.js @@ -10,8 +10,6 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { t, textDirection } from '../util/locale'; import { svgIcon } from '../svg'; import { uiBackgroundDisplayOptions } from './background_display_options'; @@ -23,7 +21,7 @@ import { uiMapData } from './map_data'; import { uiMapInMap } from './map_in_map'; import { uiSettingsCustomBackground } from './settings/custom_background'; import { uiTooltipHtml } from './tooltipHtml'; -import { utilCallWhenIdle } from '../util'; +import { utilCallWhenIdle, utilKeybinding } from '../util'; import { tooltip } from '../util/tooltip'; @@ -412,7 +410,7 @@ export function uiBackground(context) { update(); - var keybinding = d3_keybinding('background') + var keybinding = utilKeybinding('background') .on(key, togglePane) .on(uiCmd('⌘' + key), quickSwitch) .on([t('map_data.key'), t('help.key')], hidePane); diff --git a/modules/ui/conflicts.js b/modules/ui/conflicts.js index 1c23289de..fed273f45 100644 --- a/modules/ui/conflicts.js +++ b/modules/ui/conflicts.js @@ -5,8 +5,6 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { t } from '../util/locale'; import { JXON } from '../util/jxon'; import { geoExtent } from '../geo'; @@ -16,6 +14,7 @@ import { utilDetect } from '../util/detect'; import { utilEntityOrMemberSelector, + utilKeybinding, utilRebind, utilWrap } from '../util'; @@ -23,7 +22,7 @@ import { export function uiConflicts(context) { var dispatch = d3_dispatch('cancel', 'save'); - var keybinding = d3_keybinding('conflicts'); + var keybinding = utilKeybinding('conflicts'); var _origChanges; var _conflictList; diff --git a/modules/ui/feature_list.js b/modules/ui/feature_list.js index e3974f926..c086e952b 100644 --- a/modules/ui/feature_list.js +++ b/modules/ui/feature_list.js @@ -3,8 +3,6 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import * as sexagesimal from '@mapbox/sexagesimal'; import { t } from '../util/locale'; import { dmsCoordinatePair } from '../util/units'; @@ -20,12 +18,13 @@ import { utilDisplayName, utilDisplayType, utilEntityOrMemberSelector, + utilKeybinding, utilNoAuto } from '../util'; export function uiFeatureList(context) { - var keybinding = d3_keybinding('uiFeatureList'); + var keybinding = utilKeybinding('uiFeatureList'); var _geocodeResults; diff --git a/modules/ui/full_screen.js b/modules/ui/full_screen.js index 55ed0df9d..32bb4cdb4 100644 --- a/modules/ui/full_screen.js +++ b/modules/ui/full_screen.js @@ -3,15 +3,14 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { uiCmd } from './cmd'; +import { utilKeybinding } from '../util'; import { utilDetect } from '../util/detect'; export function uiFullScreen(context) { var element = context.container().node(), - keybinding = d3_keybinding('full-screen'); + keybinding = utilKeybinding('full-screen'); // button; diff --git a/modules/ui/help.js b/modules/ui/help.js index 1d350be2d..a9035e140 100644 --- a/modules/ui/help.js +++ b/modules/ui/help.js @@ -3,10 +3,7 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import marked from 'marked'; -import { t, textDirection } from '../util/locale'; import { svgIcon } from '../svg'; import { uiCmd } from './cmd'; import { uiBackground } from './background'; @@ -14,6 +11,9 @@ import { uiIntro } from './intro'; import { uiMapData } from './map_data'; import { uiShortcuts } from './shortcuts'; import { uiTooltipHtml } from './tooltipHtml'; +import { utilKeybinding } from '../util'; + +import { t, textDirection } from '../util/locale'; import { tooltip } from '../util/tooltip'; import { icon } from './intro/helper'; @@ -476,7 +476,7 @@ export function uiHelp(context) { clickHelp(docs[0], 0); - var keybinding = d3_keybinding('help') + var keybinding = utilKeybinding('help') .on(key, togglePane) .on([t('background.key'), t('map_data.key')], hidePane); diff --git a/modules/ui/info.js b/modules/ui/info.js index e6130bcdb..20553c996 100644 --- a/modules/ui/info.js +++ b/modules/ui/info.js @@ -3,12 +3,11 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { t } from '../util/locale'; import { svgIcon } from '../svg'; import { uiCmd } from './cmd'; import { uiInfoPanels } from './panels'; +import { utilKeybinding } from '../util'; export function uiInfo(context) { @@ -118,7 +117,7 @@ export function uiInfo(context) { redraw(); - var keybinding = d3_keybinding('info') + var keybinding = utilKeybinding('info') .on(uiCmd('⌘' + t('info_panels.key')), toggle); ids.forEach(function(k) { diff --git a/modules/ui/init.js b/modules/ui/init.js index 497201b0a..93c8c9c5b 100644 --- a/modules/ui/init.js +++ b/modules/ui/init.js @@ -3,8 +3,6 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { t, textDirection } from '../util/locale'; import { tooltip } from '../util/tooltip'; @@ -12,6 +10,7 @@ import { behaviorHash } from '../behavior'; import { modeBrowse } from '../modes'; import { svgDefs, svgIcon } from '../svg'; import { utilGetDimensions } from '../util/dimensions'; +import { utilKeybinding } from '../util'; import { uiAccount } from './account'; import { uiAttribution } from './attribution'; @@ -298,7 +297,7 @@ export function uiInit(context) { var panPixels = 80; - var keybinding = d3_keybinding('main') + var keybinding = utilKeybinding('main') .on('⌫', function() { d3_event.preventDefault(); }) .on(t('sidebar.key'), ui.sidebar.toggle) .on('←', pan([panPixels, 0])) diff --git a/modules/ui/map_data.js b/modules/ui/map_data.js index 9f4f96e53..aeae728d7 100644 --- a/modules/ui/map_data.js +++ b/modules/ui/map_data.js @@ -3,8 +3,6 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { svgIcon } from '../svg'; import { t, textDirection } from '../util/locale'; import { tooltip } from '../util/tooltip'; @@ -15,6 +13,7 @@ import { uiDisclosure } from './disclosure'; import { uiHelp } from './help'; import { uiSettingsCustomData } from './settings/custom_data'; import { uiTooltipHtml } from './tooltipHtml'; +import { utilKeybinding } from '../util'; export function uiMapData(context) { @@ -646,7 +645,7 @@ export function uiMapData(context) { update(); setFill(_fillSelected); - var keybinding = d3_keybinding('features') + var keybinding = utilKeybinding('features') .on(key, togglePane) .on(t('area_fill.wireframe.key'), toggleWireframe) .on([t('background.key'), t('help.key')], hidePane); diff --git a/modules/ui/map_in_map.js b/modules/ui/map_in_map.js index 139f41f6c..0cf229705 100644 --- a/modules/ui/map_in_map.js +++ b/modules/ui/map_in_map.js @@ -10,8 +10,6 @@ import { zoomIdentity as d3_zoomIdentity } from 'd3-zoom'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { t } from '../util/locale'; import { geoRawMercator, @@ -23,7 +21,7 @@ import { import { rendererTileLayer } from '../renderer'; import { svgDebug, svgData } from '../svg'; -import { utilSetTransform } from '../util'; +import { utilKeybinding, utilSetTransform } from '../util'; import { utilGetDimensions } from '../util/dimensions'; @@ -336,7 +334,7 @@ export function uiMapInMap(context) { redraw(); - var keybinding = d3_keybinding('map-in-map') + var keybinding = utilKeybinding('map-in-map') .on(t('background.minimap.key'), toggle); d3_select(document) diff --git a/modules/ui/modal.js b/modules/ui/modal.js index 4a1aafc19..91de76e8a 100644 --- a/modules/ui/modal.js +++ b/modules/ui/modal.js @@ -3,13 +3,12 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { svgIcon } from '../svg'; +import { utilKeybinding } from '../util'; export function uiModal(selection, blocking) { - var keybinding = d3_keybinding('modal'); + var keybinding = utilKeybinding('modal'); var previous = selection.select('div.modal'); var animate = previous.empty(); diff --git a/modules/ui/modes.js b/modules/ui/modes.js index 4aa4f369a..503544d95 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -1,7 +1,6 @@ import _debounce from 'lodash-es/debounce'; import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; import { modeAddArea, @@ -13,6 +12,7 @@ import { import { svgIcon } from '../svg'; import { tooltip } from '../util/tooltip'; +import { utilKeybinding } from '../util'; import { uiTooltipHtml } from './tooltipHtml'; @@ -56,7 +56,7 @@ export function uiModes(context) { .classed('mode-' + exited.id, false); }); - var keybinding = d3_keybinding('mode-buttons'); + var keybinding = utilKeybinding('mode-buttons'); modes.forEach(function(mode) { keybinding.on(mode.key, function() { diff --git a/modules/ui/preset_list.js b/modules/ui/preset_list.js index 1242e2fe2..b593602fe 100644 --- a/modules/ui/preset_list.js +++ b/modules/ui/preset_list.js @@ -5,8 +5,6 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { t, textDirection } from '../util/locale'; import { actionChangePreset } from '../actions/index'; import { operationDelete } from '../operations/index'; @@ -14,19 +12,19 @@ import { modeBrowse } from '../modes/index'; import { svgIcon } from '../svg/index'; import { uiPresetIcon } from './preset_icon'; import { uiTagReference } from './tag_reference'; -import { utilNoAuto, utilRebind } from '../util'; +import { utilKeybinding, utilNoAuto, utilRebind } from '../util'; export function uiPresetList(context) { - var dispatch = d3_dispatch('choose'), - id, - currentPreset, - autofocus = false; + var dispatch = d3_dispatch('choose'); + var _entityID; + var _currentPreset; + var _autofocus = false; function presetList(selection) { - var entity = context.entity(id), - geometry = context.geometry(id); + var entity = context.entity(_entityID); + var geometry = context.geometry(_entityID); // Treat entities on addr:interpolation lines as points, not vertices (#3241) if (geometry === 'vertex' && entity.isOnAddressLine(context.graph())) { @@ -45,11 +43,11 @@ export function uiPresetList(context) { .append('h3') .text(t('inspector.choose')); - if (context.entity(id).isUsed(context.graph())) { + if (context.entity(_entityID).isUsed(context.graph())) { messagewrap .append('button') .attr('class', 'preset-choose') - .on('click', function() { dispatch.call('choose', this, currentPreset); }) + .on('click', function() { dispatch.call('choose', this, _currentPreset); }) .call(svgIcon((textDirection === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward')); } else { messagewrap @@ -64,16 +62,16 @@ export function uiPresetList(context) { function initialKeydown() { // hack to let delete shortcut work when search is autofocused if (search.property('value').length === 0 && - (d3_event.keyCode === d3_keybinding.keyCodes['⌫'] || - d3_event.keyCode === d3_keybinding.keyCodes['⌦'])) { + (d3_event.keyCode === utilKeybinding.keyCodes['⌫'] || + d3_event.keyCode === utilKeybinding.keyCodes['⌦'])) { d3_event.preventDefault(); d3_event.stopPropagation(); - operationDelete([id], context)(); + operationDelete([_entityID], context)(); // hack to let undo work when search is autofocused } else if (search.property('value').length === 0 && (d3_event.ctrlKey || d3_event.metaKey) && - d3_event.keyCode === d3_keybinding.keyCodes.z) { + d3_event.keyCode === utilKeybinding.keyCodes.z) { d3_event.preventDefault(); d3_event.stopPropagation(); context.undo(); @@ -86,7 +84,7 @@ export function uiPresetList(context) { function keydown() { // down arrow - if (d3_event.keyCode === d3_keybinding.keyCodes['↓'] && + if (d3_event.keyCode === utilKeybinding.keyCodes['↓'] && // if insertion point is at the end of the string search.node().selectionStart === search.property('value').length) { d3_event.preventDefault(); @@ -137,7 +135,7 @@ export function uiPresetList(context) { searchWrap .call(svgIcon('#iD-icon-search', 'pre-text')); - if (autofocus) { + if (_autofocus) { search.node().focus(); } @@ -168,7 +166,7 @@ export function uiPresetList(context) { items.enter() .append('div') .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); }) - .classed('current', function(item) { return item.preset === currentPreset; }) + .classed('current', function(item) { return item.preset === _currentPreset; }) .each(function(item) { d3_select(this).call(item); }) .style('opacity', 0) .transition() @@ -181,7 +179,7 @@ export function uiPresetList(context) { var parentItem = d3_select(item.node().parentElement.closest('.preset-list-item')); // arrow down, move focus to the next, lower item - if (d3_event.keyCode === d3_keybinding.keyCodes['↓']) { + if (d3_event.keyCode === utilKeybinding.keyCodes['↓']) { d3_event.preventDefault(); d3_event.stopPropagation(); // the next item in the list at the same level @@ -205,7 +203,7 @@ export function uiPresetList(context) { } // arrow up, move focus to the previous, higher item - } else if (d3_event.keyCode === d3_keybinding.keyCodes['↑']) { + } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑']) { d3_event.preventDefault(); d3_event.stopPropagation(); // the previous item in the list at the same level @@ -215,8 +213,7 @@ export function uiPresetList(context) { if (previousItem.empty()) { // if there is a parent item if (!parentItem.empty()) { - // the item is the first subitem of a sublist, - // select the parent item + // the item is the first subitem of a sublist select the parent item previousItem = parentItem; } // if the previous item is expanded @@ -224,29 +221,27 @@ export function uiPresetList(context) { // select the last subitem of the sublist of the previous item previousItem = previousItem.select('.subgrid .preset-list-item:last-child'); } + if (!previousItem.empty()) { // focus on the previous item previousItem.select('.preset-list-button').node().focus(); - } - else { + } else { // the focus is at the top of the list, move focus back to the search field var search = d3_select(this.closest('.preset-list-pane')).select('.preset-search-input'); search.node().focus(); } - } + // arrow left, move focus to the parent item if there is one - else if (d3_event.keyCode === d3_keybinding.keyCodes[(textDirection === 'rtl') ? '→' : '←']) { + } else if (d3_event.keyCode === utilKeybinding.keyCodes[(textDirection === 'rtl') ? '→' : '←']) { d3_event.preventDefault(); d3_event.stopPropagation(); - - // if there is a parent item + // if there is a parent item, focus on the parent item if (!parentItem.empty()) { - // focus on the parent item parentItem.select('.preset-list-button').node().focus(); } - } + // arrow right, choose this item - else if (d3_event.keyCode === d3_keybinding.keyCodes[(textDirection === 'rtl') ? '←' : '→']) { + } else if (d3_event.keyCode === utilKeybinding.keyCodes[(textDirection === 'rtl') ? '←' : '→']) { d3_event.preventDefault(); d3_event.stopPropagation(); item.datum().choose(); @@ -277,12 +272,12 @@ export function uiPresetList(context) { .attr('class', 'preset-list-button') .classed('expanded', false) .call(uiPresetIcon() - .geometry(context.geometry(id)) + .geometry(context.geometry(_entityID)) .preset(preset)) .on('click', click) .on('keydown', function() { // right arrow, expand the focused item - if (d3_event.keyCode === d3_keybinding.keyCodes[(textDirection === 'rtl') ? '←' : '→']) { + if (d3_event.keyCode === utilKeybinding.keyCodes[(textDirection === 'rtl') ? '←' : '→']) { d3_event.preventDefault(); d3_event.stopPropagation(); // if the item isn't expanded @@ -291,7 +286,7 @@ export function uiPresetList(context) { click.call(this); } // left arrow, collapse the focused item - } else if (d3_event.keyCode === d3_keybinding.keyCodes[(textDirection === 'rtl') ? '→' : '←']) { + } else if (d3_event.keyCode === utilKeybinding.keyCodes[(textDirection === 'rtl') ? '→' : '←']) { d3_event.preventDefault(); d3_event.stopPropagation(); // if the item is expanded @@ -348,7 +343,6 @@ export function uiPresetList(context) { }; item.preset = preset; - return item; } @@ -361,7 +355,7 @@ export function uiPresetList(context) { wrap.append('button') .attr('class', 'preset-list-button') .call(uiPresetIcon() - .geometry(context.geometry(id)) + .geometry(context.geometry(_entityID)) .preset(preset)) .on('click', item.choose) .on('keydown', itemKeydown) @@ -375,9 +369,8 @@ export function uiPresetList(context) { item.choose = function() { context.presets().choose(preset); - context.perform( - actionChangePreset(id, currentPreset, preset), + actionChangePreset(_entityID, _currentPreset, preset), t('operations.change_tags.annotation') ); @@ -390,30 +383,30 @@ export function uiPresetList(context) { }; item.preset = preset; - item.reference = uiTagReference(preset.reference(context.geometry(id)), context); + item.reference = uiTagReference(preset.reference(context.geometry(_entityID)), context); return item; } - presetList.autofocus = function(_) { - if (!arguments.length) return autofocus; - autofocus = _; + presetList.autofocus = function(val) { + if (!arguments.length) return _autofocus; + _autofocus = val; return presetList; }; - presetList.entityID = function(_) { - if (!arguments.length) return id; - id = _; - presetList.preset(context.presets().match(context.entity(id), context.graph())); + presetList.entityID = function(val) { + if (!arguments.length) return _entityID; + _entityID = val; + presetList.preset(context.presets().match(context.entity(_entityID), context.graph())); return presetList; }; - presetList.preset = function(_) { - if (!arguments.length) return currentPreset; - currentPreset = _; + presetList.preset = function(val) { + if (!arguments.length) return _currentPreset; + _currentPreset = val; return presetList; }; diff --git a/modules/ui/save.js b/modules/ui/save.js index 09d6e5d08..6b4f0d90d 100644 --- a/modules/ui/save.js +++ b/modules/ui/save.js @@ -5,13 +5,12 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { t } from '../util/locale'; import { modeSave } from '../modes'; import { svgIcon } from '../svg'; import { uiCmd } from './cmd'; import { uiTooltipHtml } from './tooltipHtml'; +import { utilKeybinding } from '../util'; import { tooltip } from '../util/tooltip'; @@ -101,7 +100,7 @@ export function uiSave(context) { updateCount(); - var keybinding = d3_keybinding('uiSave') + var keybinding = utilKeybinding('uiSave') .on(key, save, true); d3_select(document) diff --git a/modules/ui/shortcuts.js b/modules/ui/shortcuts.js index 882e54738..05c124829 100644 --- a/modules/ui/shortcuts.js +++ b/modules/ui/shortcuts.js @@ -3,33 +3,32 @@ import { selectAll as d3_selectAll } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { t } from '../util/locale'; import { dataShortcuts } from '../../data'; import { svgIcon } from '../svg'; import { uiCmd } from './cmd'; import { uiModal } from './modal'; +import { utilKeybinding } from '../util'; import { utilDetect } from '../util/detect'; export function uiShortcuts() { var detected = utilDetect(); - var activeTab = 0; - var modalSelection; - var savedSelection; + var _activeTab = 0; + var _modalSelection; + var _selection = d3_select(null); - var keybinding = d3_keybinding('shortcuts') + var keybinding = utilKeybinding('shortcuts') .on(t('shortcuts.toggle.key'), function () { if (d3_selectAll('.modal-shortcuts').size()) { // already showing - if (modalSelection) { - modalSelection.close(); - modalSelection = null; + if (_modalSelection) { + _modalSelection.close(); + _modalSelection = null; } } else { - modalSelection = uiModal(savedSelection); - shortcutsModal(modalSelection); + _modalSelection = uiModal(_selection); + shortcutsModal(_modalSelection); } }); @@ -38,11 +37,11 @@ export function uiShortcuts() { - function shortcutsModal(modalSelection) { - modalSelection.select('.modal') + function shortcutsModal(_modalSelection) { + _modalSelection.select('.modal') .classed('modal-shortcuts', true); - var shortcutsModal = modalSelection.select('.content'); + var shortcutsModal = _modalSelection.select('.content'); shortcutsModal .append('div') @@ -84,7 +83,7 @@ export function uiShortcuts() { .append('div') .attr('class', 'tab') .on('click', function (d, i) { - activeTab = i; + _activeTab = i; render(selection); }); @@ -98,7 +97,7 @@ export function uiShortcuts() { // Update wrapper.selectAll('.tab') .classed('active', function (d, i) { - return i === activeTab; + return i === _activeTab; }); @@ -246,16 +245,16 @@ export function uiShortcuts() { // Update wrapper.selectAll('.shortcut-tab') .style('display', function (d, i) { - return i === activeTab ? 'flex' : 'none'; + return i === _activeTab ? 'flex' : 'none'; }); } return function(selection, show) { - savedSelection = selection; + _selection = selection; if (show) { - modalSelection = uiModal(selection); - shortcutsModal(modalSelection); + _modalSelection = uiModal(selection); + shortcutsModal(_modalSelection); } }; } diff --git a/modules/ui/undo_redo.js b/modules/ui/undo_redo.js index e57a93140..256219678 100644 --- a/modules/ui/undo_redo.js +++ b/modules/ui/undo_redo.js @@ -5,12 +5,11 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { t, textDirection } from '../util/locale'; import { svgIcon } from '../svg'; import { uiCmd } from './cmd'; import { uiTooltipHtml } from './tooltipHtml'; +import { utilKeybinding } from '../util'; import { tooltip } from '../util/tooltip'; @@ -65,7 +64,7 @@ export function uiUndoRedo(context) { .call(svgIcon('#iD-icon-' + iconName)); }); - var keybinding = d3_keybinding('undo') + var keybinding = utilKeybinding('undo') .on(commands[0].cmd, function() { d3_event.preventDefault(); commands[0].action(); }) .on(commands[1].cmd, function() { d3_event.preventDefault(); commands[1].action(); }); diff --git a/modules/ui/zoom.js b/modules/ui/zoom.js index 48d28d783..27e6286a2 100644 --- a/modules/ui/zoom.js +++ b/modules/ui/zoom.js @@ -3,12 +3,11 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { t, textDirection } from '../util/locale'; import { svgIcon } from '../svg'; import { uiCmd } from './cmd'; import { uiTooltipHtml } from './tooltipHtml'; +import { utilKeybinding } from '../util'; import { tooltip } from '../util/tooltip'; @@ -73,7 +72,7 @@ export function uiZoom(context) { .call(svgIcon('#iD-icon-' + d.icon, 'light')); }); - var keybinding = d3_keybinding('zoom'); + var keybinding = utilKeybinding('zoom'); ['plus', 'ffplus', '=', 'ffequals'].forEach(function(key) { keybinding.on([key], zoomIn); diff --git a/modules/util/index.js b/modules/util/index.js index a0269240f..bbca0d00a 100644 --- a/modules/util/index.js +++ b/modules/util/index.js @@ -16,6 +16,7 @@ export { utilGetSetValue } from './get_set_value'; export { utilHashcode } from './util'; export { utilHighlightEntity } from './util'; export { utilIdleWorker } from './idle_worker'; +export { utilKeybinding } from './keybinding'; export { utilNoAuto } from './util'; export { utilPrefixCSSProperty } from './util'; export { utilPrefixDOMProperty } from './util'; diff --git a/modules/lib/d3.keybinding.js b/modules/util/keybinding.js similarity index 88% rename from modules/lib/d3.keybinding.js rename to modules/util/keybinding.js index 8fb2f7372..88019df27 100644 --- a/modules/lib/d3.keybinding.js +++ b/modules/util/keybinding.js @@ -4,31 +4,22 @@ import { } from 'd3-selection'; -/* - * This code is licensed under the MIT license. - * - * Copyright © 2013, iD authors. - * - * Portions copyright © 2011, Keith Cirkel - * See https://github.com/keithamus/jwerty - * - */ -export function d3keybinding(namespace) { - var bindings = []; +export function utilKeybinding(namespace) { + var _keybindings = []; function testBindings(isCapturing) { - var didMatch = false, - i, binding; + var didMatch = false; + var i, binding; // Most key shortcuts will accept either lower or uppercase ('h' or 'H'), // so we don't strictly match on the shift key, but we prioritize - // shifted bindings first, and fallback to unshifted only if no match. + // shifted keybindings first, and fallback to unshifted only if no match. // (This lets us differentiate between '←'/'⇧←' or '⌘Z'/'⌘⇧Z') - // priority match shifted bindings first - for (i = 0; i < bindings.length; i++) { - binding = bindings[i]; + // priority match shifted keybindings first + for (i = 0; i < _keybindings.length; i++) { + binding = _keybindings[i]; if (!binding.event.modifiers.shiftKey) continue; // no shift if (!!binding.capture !== isCapturing) continue; if (matches(binding, true)) { @@ -37,10 +28,10 @@ export function d3keybinding(namespace) { } } - // then unshifted bindings + // then unshifted keybindings if (didMatch) return; - for (i = 0; i < bindings.length; i++) { - binding = bindings[i]; + for (i = 0; i < _keybindings.length; i++) { + binding = _keybindings[i]; if (binding.event.modifiers.shiftKey) continue; // shift if (!!binding.capture !== isCapturing) continue; if (matches(binding, false)) { @@ -115,7 +106,7 @@ export function d3keybinding(namespace) { keybinding.off = function(selection) { - bindings = []; + _keybindings = []; selection = selection || d3_select(document); selection.on('keydown.capture' + namespace, null); selection.on('keydown.bubble' + namespace, null); @@ -148,17 +139,17 @@ export function d3keybinding(namespace) { // Normalise matching errors if (code[j] === '++') code[j] = '+'; - if (code[j] in d3keybinding.modifierCodes) { - binding.event.modifiers[d3keybinding.modifierProperties[d3keybinding.modifierCodes[code[j]]]] = true; + if (code[j] in utilKeybinding.modifierCodes) { + binding.event.modifiers[utilKeybinding.modifierProperties[utilKeybinding.modifierCodes[code[j]]]] = true; } else { - binding.event.key = d3keybinding.keys[code[j]] || code[j]; - if (code[j] in d3keybinding.keyCodes) { - binding.event.keyCode = d3keybinding.keyCodes[code[j]]; + binding.event.key = utilKeybinding.keys[code[j]] || code[j]; + if (code[j] in utilKeybinding.keyCodes) { + binding.event.keyCode = utilKeybinding.keyCodes[code[j]]; } } } - bindings.push(binding); + _keybindings.push(binding); } return keybinding; @@ -168,7 +159,12 @@ export function d3keybinding(namespace) { } -d3keybinding.modifierCodes = { +/* + * See https://github.com/keithamus/jwerty + */ + + +utilKeybinding.modifierCodes = { // Shift key, ⇧ '⇧': 16, shift: 16, // CTRL key, on Mac: ⌃ @@ -179,14 +175,14 @@ d3keybinding.modifierCodes = { '⌘': 91, meta: 91, cmd: 91, 'super': 91, win: 91 }; -d3keybinding.modifierProperties = { +utilKeybinding.modifierProperties = { 16: 'shiftKey', 17: 'ctrlKey', 18: 'altKey', 91: 'metaKey' }; -d3keybinding.keys = { +utilKeybinding.keys = { // Backspace key, on Mac: ⌫ (Backspace) '⌫': 'Backspace', backspace: 'Backspace', // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥ @@ -287,7 +283,7 @@ d3keybinding.keys = { f25: 'F25' }; -d3keybinding.keyCodes = { +utilKeybinding.keyCodes = { // Backspace key, on Mac: ⌫ (Backspace) '⌫': 8, backspace: 8, // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥ @@ -362,26 +358,26 @@ d3keybinding.keyCodes = { // NUMPAD 0-9 var i = 95, n = 0; while (++i < 106) { - d3keybinding.keyCodes['num-' + n] = i; + utilKeybinding.keyCodes['num-' + n] = i; ++n; } // 0-9 i = 47; n = 0; while (++i < 58) { - d3keybinding.keyCodes[n] = i; + utilKeybinding.keyCodes[n] = i; ++n; } // F1-F25 i = 111; n = 1; while (++i < 136) { - d3keybinding.keyCodes['f' + n] = i; + utilKeybinding.keyCodes['f' + n] = i; ++n; } // a-z i = 64; while (++i < 91) { - d3keybinding.keyCodes[String.fromCharCode(i).toLowerCase()] = i; + utilKeybinding.keyCodes[String.fromCharCode(i).toLowerCase()] = i; } diff --git a/test/index.html b/test/index.html index abcf0ed83..c954aae87 100644 --- a/test/index.html +++ b/test/index.html @@ -81,7 +81,6 @@ - @@ -138,6 +137,7 @@ + diff --git a/test/spec/lib/d3.combobox.js b/test/spec/lib/d3.combobox.js index 9a803f004..af588c766 100644 --- a/test/spec/lib/d3.combobox.js +++ b/test/spec/lib/d3.combobox.js @@ -10,7 +10,7 @@ describe('d3.combobox', function() { ]; function simulateKeypress(key) { - var keyCode = iD.lib.d3keybinding.keyCodes[key]; + var keyCode = iD.utilKeybinding.keyCodes[key]; var value = input.property('value'); var start = input.property('selectionStart'); var finis = input.property('selectionEnd'); diff --git a/test/spec/lib/d3.keybinding.js b/test/spec/util/keybinding.js similarity index 97% rename from test/spec/lib/d3.keybinding.js rename to test/spec/util/keybinding.js index c4ce6a5da..672060dba 100644 --- a/test/spec/lib/d3.keybinding.js +++ b/test/spec/util/keybinding.js @@ -1,8 +1,8 @@ -describe('d3.keybinding', function() { +describe('utilKeybinding', function() { var keybinding, spy, input; beforeEach(function () { - keybinding = iD.lib.d3keybinding('keybinding-test'); + keybinding = iD.utilKeybinding('keybinding-test'); spy = sinon.spy(); input = d3.select('body') .append('input'); From 152022aec4ebed3285fd09c9265bcd2ef6de3d12 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 13 Nov 2018 20:57:21 -0500 Subject: [PATCH 5/8] Use context.keybinding for keybindings that don't change (closes #5487) --- modules/behavior/add_way.js | 18 +++---- modules/behavior/breathe.js | 8 +-- modules/behavior/copy.js | 35 ++++++-------- modules/behavior/drag.js | 35 ++++++-------- modules/behavior/draw.js | 14 +++--- modules/behavior/edit.js | 7 ++- modules/behavior/hash.js | 11 ++--- modules/behavior/hover.js | 14 +++--- modules/behavior/operation.js | 76 ++++++++++++++--------------- modules/behavior/paste.js | 34 ++++--------- modules/behavior/select.js | 4 +- modules/behavior/tail.js | 10 ++-- modules/core/context.js | 40 ++++++--------- modules/modes/move.js | 3 +- modules/modes/rotate.js | 3 +- modules/modes/save.js | 2 +- modules/modes/select.js | 5 +- modules/modes/select_data.js | 8 ++- modules/modes/select_note.js | 8 ++- modules/ui/background.js | 10 ++-- modules/ui/conflicts.js | 2 +- modules/ui/feature_list.js | 7 +-- modules/ui/full_screen.js | 21 +++----- modules/ui/help.js | 9 +--- modules/ui/info.js | 16 +++--- modules/ui/init.js | 7 +-- modules/ui/map_data.js | 9 +--- modules/ui/map_in_map.js | 7 +-- modules/ui/modal.js | 3 +- modules/ui/modes.js | 8 +-- modules/ui/panels/background.js | 4 +- modules/ui/save.js | 12 +---- modules/ui/shortcuts.js | 9 +--- modules/ui/undo_redo.js | 6 +-- modules/ui/zoom.js | 14 ++---- modules/util/keybinding.js | 86 ++++++++++++++++++++++----------- test/spec/util/keybinding.js | 6 +-- 37 files changed, 252 insertions(+), 319 deletions(-) diff --git a/modules/behavior/add_way.js b/modules/behavior/add_way.js index 8fb6b3ca1..decb20370 100644 --- a/modules/behavior/add_way.js +++ b/modules/behavior/add_way.js @@ -9,26 +9,26 @@ export function behaviorAddWay(context) { var dispatch = d3_dispatch('start', 'startFromWay', 'startFromNode'); var draw = behaviorDraw(context); - var addWay = function(surface) { + function behavior(surface) { draw.on('click', function() { dispatch.apply('start', this, arguments); }) .on('clickWay', function() { dispatch.apply('startFromWay', this, arguments); }) .on('clickNode', function() { dispatch.apply('startFromNode', this, arguments); }) - .on('cancel', addWay.cancel) - .on('finish', addWay.cancel); + .on('cancel', behavior.cancel) + .on('finish', behavior.cancel); context.map() .dblclickEnable(false); surface.call(draw); - }; + } - addWay.off = function(surface) { + behavior.off = function(surface) { surface.call(draw.off); }; - addWay.cancel = function() { + behavior.cancel = function() { window.setTimeout(function() { context.map().dblclickEnable(true); }, 1000); @@ -37,11 +37,11 @@ export function behaviorAddWay(context) { }; - addWay.tail = function(text) { + behavior.tail = function(text) { draw.tail(text); - return addWay; + return behavior; }; - return utilRebind(addWay, dispatch, 'on'); + return utilRebind(behavior, dispatch, 'on'); } diff --git a/modules/behavior/breathe.js b/modules/behavior/breathe.js index 6b09fe476..09e0a2dc6 100644 --- a/modules/behavior/breathe.js +++ b/modules/behavior/breathe.js @@ -136,7 +136,7 @@ export function behaviorBreathe() { } - var breathe = function(surface) { + function behavior(surface) { _done = false; _timer = d3_timer(function() { // wait for elements to actually become selected @@ -148,10 +148,10 @@ export function behaviorBreathe() { _timer.stop(); return true; }, 20); - }; + } - breathe.off = function() { + behavior.off = function() { _done = true; if (_timer) { _timer.stop(); @@ -162,5 +162,5 @@ export function behaviorBreathe() { }; - return breathe; + return behavior; } diff --git a/modules/behavior/copy.js b/modules/behavior/copy.js index 247bc6ba2..d465aeedd 100644 --- a/modules/behavior/copy.js +++ b/modules/behavior/copy.js @@ -2,18 +2,11 @@ import _extend from 'lodash-es/extend'; import _groupBy from 'lodash-es/groupBy'; import _map from 'lodash-es/map'; -import { - event as d3_event, - select as d3_select -} from 'd3-selection'; - -import { utilKeybinding } from '../util'; +import { event as d3_event } from 'd3-selection'; import { uiCmd } from '../ui'; export function behaviorCopy(context) { - var keybinding = utilKeybinding('copy'); - function groupEntities(ids, graph) { var entities = ids.map(function (id) { return graph.entity(id); }); @@ -47,8 +40,15 @@ export function behaviorCopy(context) { } + function getSelectionText() { + return window.getSelection().toString(); + } + + function doCopy() { - if (!getSelectionText()) d3_event.preventDefault(); + if (!getSelectionText()) { + d3_event.preventDefault(); + } var graph = context.graph(); var selected = groupEntities(context.selectedIDs(), graph); @@ -82,20 +82,15 @@ export function behaviorCopy(context) { } - function copy() { - keybinding.on(uiCmd('⌘C'), doCopy); - d3_select(document).call(keybinding); - return copy; + function behavior() { + context.keybinding().on(uiCmd('⌘C'), doCopy); + return behavior; } - function getSelectionText() { - return window.getSelection().toString(); - } - - copy.off = function() { - d3_select(document).call(keybinding.off); + behavior.off = function() { + context.keybinding().off(uiCmd('⌘C')); }; - return copy; + return behavior; } diff --git a/modules/behavior/drag.js b/modules/behavior/drag.js index 9735b77ae..0221149ec 100644 --- a/modules/behavior/drag.js +++ b/modules/behavior/drag.js @@ -10,13 +10,8 @@ import { } from 'd3-selection'; import { osmNote } from '../osm'; - import { utilRebind } from '../util/rebind'; - -import { - utilPrefixCSSProperty, - utilPrefixDOMProperty -} from '../util'; +import { utilPrefixCSSProperty, utilPrefixDOMProperty } from '../util'; /* @@ -62,7 +57,7 @@ export function behaviorDrag() { function eventOf(thiz, argumentz) { return function(e1) { - e1.target = drag; + e1.target = behavior; d3_customEvent(e1, dispatch.apply, dispatch, [e1.type, thiz, argumentz]); }; } @@ -154,7 +149,7 @@ export function behaviorDrag() { } - function drag(selection) { + function behavior(selection) { var matchesSelector = utilPrefixDOMProperty('matchesSelector'); var delegate = dragstart; @@ -181,49 +176,49 @@ export function behaviorDrag() { } - drag.off = function(selection) { + behavior.off = function(selection) { selection .on('mousedown.drag' + _selector, null) .on('touchstart.drag' + _selector, null); }; - drag.selector = function(_) { + behavior.selector = function(_) { if (!arguments.length) return _selector; _selector = _; - return drag; + return behavior; }; - drag.origin = function (_) { + behavior.origin = function(_) { if (!arguments.length) return _origin; _origin = _; - return drag; + return behavior; }; - drag.cancel = function() { + behavior.cancel = function() { d3_select(window) .on('mousemove.drag', null) .on('mouseup.drag', null); - return drag; + return behavior; }; - drag.target = function() { + behavior.target = function() { if (!arguments.length) return _target; _target = arguments[0]; _event = eventOf(_target, Array.prototype.slice.call(arguments, 1)); - return drag; + return behavior; }; - drag.surface = function() { + behavior.surface = function() { if (!arguments.length) return _surface; _surface = arguments[0]; - return drag; + return behavior; }; - return utilRebind(drag, dispatch, 'on'); + return utilRebind(behavior, dispatch, 'on'); } diff --git a/modules/behavior/draw.js b/modules/behavior/draw.js index 8640b4ce0..3081d2153 100644 --- a/modules/behavior/draw.js +++ b/modules/behavior/draw.js @@ -190,7 +190,7 @@ export function behaviorDraw(context) { } - function draw(selection) { + function behavior(selection) { context.install(hover); context.install(edit); @@ -215,11 +215,11 @@ export function behaviorDraw(context) { d3_select(document) .call(keybinding); - return draw; + return behavior; } - draw.off = function(selection) { + behavior.off = function(selection) { context.ui().sidebar.hover.cancel(); context.uninstall(hover); context.uninstall(edit); @@ -240,15 +240,15 @@ export function behaviorDraw(context) { // note: keyup.space-block, click.draw-block should remain d3_select(document) - .call(keybinding.off); + .call(keybinding.unbind); }; - draw.tail = function(_) { + behavior.tail = function(_) { tail.text(_); - return draw; + return behavior; }; - return utilRebind(draw, dispatch, 'on'); + return utilRebind(behavior, dispatch, 'on'); } diff --git a/modules/behavior/edit.js b/modules/behavior/edit.js index d4ed0f6b9..09749b821 100644 --- a/modules/behavior/edit.js +++ b/modules/behavior/edit.js @@ -1,16 +1,15 @@ export function behaviorEdit(context) { - function edit() { + function behavior() { context.map() .minzoom(context.minEditableZoom()); } - edit.off = function() { + behavior.off = function() { context.map() .minzoom(0); }; - - return edit; + return behavior; } diff --git a/modules/behavior/hash.js b/modules/behavior/hash.js index 2aa76ff2b..72e20edf4 100644 --- a/modules/behavior/hash.js +++ b/modules/behavior/hash.js @@ -86,7 +86,7 @@ export function behaviorHash(context) { } - function hash() { + function behavior() { context.map() .on('move.hash', throttledUpdate); @@ -97,7 +97,6 @@ export function behaviorHash(context) { .on('hashchange.hash', hashchange); if (window.location.hash) { - var q = utilStringQs(window.location.hash.substring(1)); if (q.id) { @@ -119,19 +118,19 @@ export function behaviorHash(context) { } if (q.walkthrough === 'true') { - hash.startWalkthrough = true; + behavior.startWalkthrough = true; } hashchange(); if (q.map) { - hash.hadHash = true; + behavior.hadHash = true; } } } - hash.off = function() { + behavior.off = function() { throttledUpdate.cancel(); context.map() @@ -147,5 +146,5 @@ export function behaviorHash(context) { }; - return hash; + return behavior; } diff --git a/modules/behavior/hover.js b/modules/behavior/hover.js index fc0ef8a7a..b7bd503de 100644 --- a/modules/behavior/hover.js +++ b/modules/behavior/hover.js @@ -55,7 +55,7 @@ export function behaviorHover(context) { } - var hover = function(selection) { + function behavior(selection) { _selection = selection; _newId = null; @@ -150,10 +150,10 @@ export function behaviorHover(context) { dispatch.call('hover', this, null); } } - }; + } - hover.off = function(selection) { + behavior.off = function(selection) { selection.selectAll('.hover') .classed('hover', false); selection.selectAll('.hover-suppressed') @@ -172,12 +172,12 @@ export function behaviorHover(context) { }; - hover.altDisables = function(_) { + behavior.altDisables = function(val) { if (!arguments.length) return _altDisables; - _altDisables = _; - return hover; + _altDisables = val; + return behavior; }; - return utilRebind(hover, dispatch, 'on'); + return utilRebind(behavior, dispatch, 'on'); } diff --git a/modules/behavior/operation.js b/modules/behavior/operation.js index 5e3e03897..91d61bb4b 100644 --- a/modules/behavior/operation.js +++ b/modules/behavior/operation.js @@ -1,57 +1,51 @@ -import { - event as d3_event, - select as d3_select -} from 'd3-selection'; - +import { event as d3_event } from 'd3-selection'; import { uiFlash } from '../ui'; -import { utilKeybinding } from '../util'; /* Creates a keybinding behavior for an operation */ -export function behaviorOperation() { - var keybinding; +export function behaviorOperation(context) { var _operation; - var behavior = function () { + function keypress() { + d3_event.preventDefault(); + var disabled = _operation.disabled(); + var flash; + + if (disabled) { + flash = uiFlash() + .duration(4000) + .iconName('#iD-operation-' + _operation.id) + .iconClass('operation disabled') + .text(_operation.tooltip); + + flash(); + + } else { + flash = uiFlash() + .duration(2000) + .iconName('#iD-operation-' + _operation.id) + .iconClass('operation') + .text(_operation.annotation() || _operation.title); + + flash(); + _operation(); + } + } + + + function behavior() { if (_operation && _operation.available()) { - keybinding = utilKeybinding('behavior.key.' + _operation.id); - keybinding.on(_operation.keys, function() { - d3_event.preventDefault(); - var disabled = _operation.disabled(); - var flash; - - if (disabled) { - flash = uiFlash() - .duration(4000) - .iconName('#iD-operation-' + _operation.id) - .iconClass('operation disabled') - .text(_operation.tooltip); - - flash(); - - } else { - flash = uiFlash() - .duration(2000) - .iconName('#iD-operation-' + _operation.id) - .iconClass('operation') - .text(_operation.annotation() || _operation.title); - - flash(); - _operation(); - } - }); - - d3_select(document).call(keybinding); + context.keybinding() + .on(_operation.keys, keypress); } return behavior; - }; + } behavior.off = function() { - if (keybinding) { - d3_select(document).call(keybinding.off); - } + context.keybinding() + .off(_operation.keys); }; diff --git a/modules/behavior/paste.js b/modules/behavior/paste.js index a663309b8..596ed9c2c 100644 --- a/modules/behavior/paste.js +++ b/modules/behavior/paste.js @@ -1,30 +1,15 @@ import _invert from 'lodash-es/invert'; import _mapValues from 'lodash-es/mapValues'; -import { - event as d3_event, - select as d3_select -} from 'd3-selection'; - -import { - actionCopyEntities, - actionMove -} from '../actions'; - -import { - geoExtent, - geoPointInPolygon, - geoVecSubtract -} from '../geo'; +import { event as d3_event } from 'd3-selection'; +import { actionCopyEntities, actionMove } from '../actions'; +import { geoExtent, geoPointInPolygon, geoVecSubtract } from '../geo'; import { modeMove } from '../modes'; import { uiCmd } from '../ui'; -import { utilKeybinding } from '../util'; export function behaviorPaste(context) { - var keybinding = utilKeybinding('paste'); - function doPaste() { d3_event.preventDefault(); @@ -78,17 +63,16 @@ export function behaviorPaste(context) { } - function paste() { - keybinding.on(uiCmd('⌘V'), doPaste); - d3_select(document).call(keybinding); - return paste; + function behavior() { + context.keybinding().on(uiCmd('⌘V'), doPaste); + return behavior; } - paste.off = function() { - d3_select(document).call(keybinding.off); + behavior.off = function() { + context.keybinding().off(uiCmd('⌘V')); }; - return paste; + return behavior; } diff --git a/modules/behavior/select.js b/modules/behavior/select.js index 1dd051e65..bbb6df526 100644 --- a/modules/behavior/select.js +++ b/modules/behavior/select.js @@ -180,7 +180,7 @@ export function behaviorSelect(context) { } - var behavior = function(selection) { + function behavior(selection) { lastMouse = null; suppressMenu = true; p1 = null; @@ -208,7 +208,7 @@ export function behaviorSelect(context) { context.surface() .classed('behavior-multiselect', true); } - }; + } behavior.off = function(selection) { diff --git a/modules/behavior/tail.js b/modules/behavior/tail.js index 3b4919516..bd284c072 100644 --- a/modules/behavior/tail.js +++ b/modules/behavior/tail.js @@ -15,7 +15,7 @@ export function behaviorTail() { var _text; - function tail(selection) { + function behavior(selection) { if (!_text) return; d3_select(window) @@ -71,7 +71,7 @@ export function behaviorTail() { } - tail.off = function(selection) { + behavior.off = function(selection) { if (!_text) return; container @@ -88,12 +88,12 @@ export function behaviorTail() { }; - tail.text = function(val) { + behavior.text = function(val) { if (!arguments.length) return _text; _text = val; - return tail; + return behavior; }; - return tail; + return behavior; } diff --git a/modules/core/context.js b/modules/core/context.js index 7efd9e7b2..8d5c3095d 100644 --- a/modules/core/context.js +++ b/modules/core/context.js @@ -10,38 +10,18 @@ import { dispatch as d3_dispatch } from 'd3-dispatch'; import { json as d3_json } from 'd3-request'; import { select as d3_select } from 'd3-selection'; -import { - t, - currentLocale, - addTranslation, - setLocale -} from '../util/locale'; +import { t, currentLocale, addTranslation, setLocale } from '../util/locale'; import { coreHistory } from './history'; - -import { - dataLocales, - dataEn -} from '../../data'; - +import { dataLocales, dataEn } from '../../data'; import { geoRawMercator } from '../geo/raw_mercator'; import { modeSelect } from '../modes/select'; import { presetIndex } from '../presets'; - -import { - rendererBackground, - rendererFeatures, - rendererMap -} from '../renderer'; - +import { rendererBackground, rendererFeatures, rendererMap } from '../renderer'; import { services } from '../services'; import { uiInit } from '../ui/init'; import { utilDetect } from '../util/detect'; - -import { - utilCallWhenIdle, - utilRebind -} from '../util'; +import { utilCallWhenIdle, utilKeybinding, utilRebind } from '../util'; export var areaKeys = {}; @@ -104,9 +84,17 @@ export function coreContext() { }; - /* Straight accessors. Avoid using these if you can. */ - var ui, connection, history; + /* User interface and keybinding */ + var ui; context.ui = function() { return ui; }; + + var keybinding = utilKeybinding('context'); + context.keybinding = function() { return keybinding; }; + d3_select(document).call(keybinding); + + + /* Straight accessors. Avoid using these if you can. */ + var connection, history; context.connection = function() { return connection; }; context.history = function() { return history; }; diff --git a/modules/modes/move.js b/modules/modes/move.js index 1a9704e4b..d46e8fd5f 100644 --- a/modules/modes/move.js +++ b/modules/modes/move.js @@ -159,7 +159,8 @@ export function modeMove(context, entityIDs, baseGraph) { context.history() .on('undone.move', null); - keybinding.off(); + d3_select(document) + .call(keybinding.unbind); }; diff --git a/modules/modes/rotate.js b/modules/modes/rotate.js index 1b0f30248..d9d98d209 100644 --- a/modules/modes/rotate.js +++ b/modules/modes/rotate.js @@ -142,7 +142,8 @@ export function modeRotate(context, entityIDs) { context.history() .on('undone.rotate', null); - keybinding.off(); + d3_select(document) + .call(keybinding.unbind); }; diff --git a/modules/modes/save.js b/modules/modes/save.js index 5b1ef162f..52c09511c 100644 --- a/modules/modes/save.js +++ b/modules/modes/save.js @@ -487,7 +487,7 @@ export function modeSave(context) { function keybindingOff() { d3_select(document) - .call(keybinding.off); + .call(keybinding.unbind); } diff --git a/modules/modes/select.js b/modules/modes/select.js index 8e47d6aed..add5b6479 100644 --- a/modules/modes/select.js +++ b/modules/modes/select.js @@ -511,7 +511,10 @@ export function modeSelect(context, selectedIDs) { if (inspector) wrap.call(inspector.close); behaviors.forEach(context.uninstall); - keybinding.off(); + + d3_select(document) + .call(keybinding.unbind); + closeMenu(); editMenu = undefined; diff --git a/modules/modes/select_data.js b/modules/modes/select_data.js index e7b9f8bf8..fa1e413c7 100644 --- a/modules/modes/select_data.js +++ b/modules/modes/select_data.js @@ -63,7 +63,9 @@ export function modeSelectData(context, selectedDatum) { mode.enter = function() { behaviors.forEach(context.install); keybinding.on('⎋', esc, true); - d3_select(document).call(keybinding); + + d3_select(document) + .call(keybinding); selectData(); @@ -81,7 +83,9 @@ export function modeSelectData(context, selectedDatum) { mode.exit = function() { behaviors.forEach(context.uninstall); - keybinding.off(); + + d3_select(document) + .call(keybinding.unbind); context.surface() .selectAll('.layer-mapdata .selected') diff --git a/modules/modes/select_note.js b/modules/modes/select_note.js index 43606540d..f2dcc3835 100644 --- a/modules/modes/select_note.js +++ b/modules/modes/select_note.js @@ -95,7 +95,9 @@ export function modeSelectNote(context, selectedNoteID) { behaviors.forEach(context.install); keybinding.on('⎋', esc, true); - d3_select(document).call(keybinding); + + d3_select(document) + .call(keybinding); selectNote(); @@ -112,7 +114,9 @@ export function modeSelectNote(context, selectedNoteID) { mode.exit = function() { behaviors.forEach(context.uninstall); - keybinding.off(); + + d3_select(document) + .call(keybinding.unbind); context.surface() .selectAll('.layer-notes .selected') diff --git a/modules/ui/background.js b/modules/ui/background.js index c95fc59c2..4d98aedfd 100644 --- a/modules/ui/background.js +++ b/modules/ui/background.js @@ -21,7 +21,7 @@ import { uiMapData } from './map_data'; import { uiMapInMap } from './map_in_map'; import { uiSettingsCustomBackground } from './settings/custom_background'; import { uiTooltipHtml } from './tooltipHtml'; -import { utilCallWhenIdle, utilKeybinding } from '../util'; +import { utilCallWhenIdle } from '../util'; import { tooltip } from '../util/tooltip'; @@ -410,13 +410,9 @@ export function uiBackground(context) { update(); - var keybinding = utilKeybinding('background') + context.keybinding() .on(key, togglePane) - .on(uiCmd('⌘' + key), quickSwitch) - .on([t('map_data.key'), t('help.key')], hidePane); - - d3_select(document) - .call(keybinding); + .on(uiCmd('⌘' + key), quickSwitch); uiBackground.hidePane = hidePane; uiBackground.togglePane = togglePane; diff --git a/modules/ui/conflicts.js b/modules/ui/conflicts.js index fed273f45..9bb0e1252 100644 --- a/modules/ui/conflicts.js +++ b/modules/ui/conflicts.js @@ -34,7 +34,7 @@ export function uiConflicts(context) { function keybindingOff() { d3_select(document) - .call(keybinding.off); + .call(keybinding.unbind); } function tryAgain() { diff --git a/modules/ui/feature_list.js b/modules/ui/feature_list.js index c086e952b..5bef2e1ba 100644 --- a/modules/ui/feature_list.js +++ b/modules/ui/feature_list.js @@ -18,13 +18,11 @@ import { utilDisplayName, utilDisplayType, utilEntityOrMemberSelector, - utilKeybinding, utilNoAuto } from '../util'; export function uiFeatureList(context) { - var keybinding = utilKeybinding('uiFeatureList'); var _geocodeResults; @@ -66,12 +64,9 @@ export function uiFeatureList(context) { context.map() .on('drawn.feature-list', mapDrawn); - keybinding + context.keybinding() .on(uiCmd('⌘F'), focusSearch); - d3_select(document) - .call(keybinding); - function focusSearch() { var mode = context.mode() && context.mode().id; diff --git a/modules/ui/full_screen.js b/modules/ui/full_screen.js index 32bb4cdb4..9422afbf1 100644 --- a/modules/ui/full_screen.js +++ b/modules/ui/full_screen.js @@ -1,17 +1,12 @@ -import { - event as d3_event, - select as d3_select -} from 'd3-selection'; +import { event as d3_event } from 'd3-selection'; import { uiCmd } from './cmd'; -import { utilKeybinding } from '../util'; import { utilDetect } from '../util/detect'; export function uiFullScreen(context) { - var element = context.container().node(), - keybinding = utilKeybinding('full-screen'); - // button; + var element = context.container().node(); + // var button = d3_select(null); function getFullScreenFn() { @@ -66,8 +61,7 @@ export function uiFullScreen(context) { return function() { // selection) { - if (!isSupported()) - return; + if (!isSupported()) return; // button = selection.append('button') // .attr('title', t('full_screen')) @@ -79,10 +73,7 @@ export function uiFullScreen(context) { // .attr('class', 'icon full-screen'); var detected = utilDetect(); - var keys = detected.os === 'mac' ? [uiCmd('⌃⌘F'), 'f11'] : ['f11']; - keybinding.on(keys, fullScreen); - - d3_select(document) - .call(keybinding); + var keys = (detected.os === 'mac' ? [uiCmd('⌃⌘F'), 'f11'] : ['f11']); + context.keybinding().on(keys, fullScreen); }; } diff --git a/modules/ui/help.js b/modules/ui/help.js index a9035e140..195c34819 100644 --- a/modules/ui/help.js +++ b/modules/ui/help.js @@ -11,7 +11,6 @@ import { uiIntro } from './intro'; import { uiMapData } from './map_data'; import { uiShortcuts } from './shortcuts'; import { uiTooltipHtml } from './tooltipHtml'; -import { utilKeybinding } from '../util'; import { t, textDirection } from '../util/locale'; import { tooltip } from '../util/tooltip'; @@ -476,12 +475,8 @@ export function uiHelp(context) { clickHelp(docs[0], 0); - var keybinding = utilKeybinding('help') - .on(key, togglePane) - .on([t('background.key'), t('map_data.key')], hidePane); - - d3_select(document) - .call(keybinding); + context.keybinding() + .on(key, togglePane); uiHelp.hidePane = hidePane; uiHelp.togglePane = togglePane; diff --git a/modules/ui/info.js b/modules/ui/info.js index 20553c996..3e8bfae27 100644 --- a/modules/ui/info.js +++ b/modules/ui/info.js @@ -7,14 +7,13 @@ import { t } from '../util/locale'; import { svgIcon } from '../svg'; import { uiCmd } from './cmd'; import { uiInfoPanels } from './panels'; -import { utilKeybinding } from '../util'; export function uiInfo(context) { - var ids = Object.keys(uiInfoPanels), - wasActive = ['measurement'], - panels = {}, - active = {}; + var ids = Object.keys(uiInfoPanels); + var wasActive = ['measurement']; + var panels = {}; + var active = {}; // create panels ids.forEach(function(k) { @@ -117,18 +116,15 @@ export function uiInfo(context) { redraw(); - var keybinding = utilKeybinding('info') + context.keybinding() .on(uiCmd('⌘' + t('info_panels.key')), toggle); ids.forEach(function(k) { var key = t('info_panels.' + k + '.key', { default: null }); if (!key) return; - keybinding + context.keybinding() .on(uiCmd('⌘⇧' + key), function() { toggle(k); }); }); - - d3_select(document) - .call(keybinding); } return info; diff --git a/modules/ui/init.js b/modules/ui/init.js index 93c8c9c5b..fcc8ce493 100644 --- a/modules/ui/init.js +++ b/modules/ui/init.js @@ -10,7 +10,6 @@ import { behaviorHash } from '../behavior'; import { modeBrowse } from '../modes'; import { svgDefs, svgIcon } from '../svg'; import { utilGetDimensions } from '../util/dimensions'; -import { utilKeybinding } from '../util'; import { uiAccount } from './account'; import { uiAttribution } from './attribution'; @@ -297,7 +296,7 @@ export function uiInit(context) { var panPixels = 80; - var keybinding = utilKeybinding('main') + context.keybinding() .on('⌫', function() { d3_event.preventDefault(); }) .on(t('sidebar.key'), ui.sidebar.toggle) .on('←', pan([panPixels, 0])) @@ -309,9 +308,6 @@ export function uiInit(context) { .on(['⇧→', uiCmd('⌘→')], pan([-map.dimensions()[0], 0])) .on(['⇧↓', uiCmd('⌘↓')], pan([0, -map.dimensions()[1]])); - d3_select(document) - .call(keybinding); - context.enter(modeBrowse(context)); if (!_initCounter++) { @@ -372,6 +368,7 @@ export function uiInit(context) { ui.restart = function(arg) { + context.keybinding().clear(); context.locale(arg); context.loadLocale(function(err) { if (!err) { diff --git a/modules/ui/map_data.js b/modules/ui/map_data.js index aeae728d7..3fa8cfb8f 100644 --- a/modules/ui/map_data.js +++ b/modules/ui/map_data.js @@ -13,7 +13,6 @@ import { uiDisclosure } from './disclosure'; import { uiHelp } from './help'; import { uiSettingsCustomData } from './settings/custom_data'; import { uiTooltipHtml } from './tooltipHtml'; -import { utilKeybinding } from '../util'; export function uiMapData(context) { @@ -645,13 +644,9 @@ export function uiMapData(context) { update(); setFill(_fillSelected); - var keybinding = utilKeybinding('features') + context.keybinding() .on(key, togglePane) - .on(t('area_fill.wireframe.key'), toggleWireframe) - .on([t('background.key'), t('help.key')], hidePane); - - d3_select(document) - .call(keybinding); + .on(t('area_fill.wireframe.key'), toggleWireframe); uiMapData.hidePane = hidePane; uiMapData.togglePane = togglePane; diff --git a/modules/ui/map_in_map.js b/modules/ui/map_in_map.js index 0cf229705..55b9b4ed7 100644 --- a/modules/ui/map_in_map.js +++ b/modules/ui/map_in_map.js @@ -21,7 +21,7 @@ import { import { rendererTileLayer } from '../renderer'; import { svgDebug, svgData } from '../svg'; -import { utilKeybinding, utilSetTransform } from '../util'; +import { utilSetTransform } from '../util'; import { utilGetDimensions } from '../util/dimensions'; @@ -334,11 +334,8 @@ export function uiMapInMap(context) { redraw(); - var keybinding = utilKeybinding('map-in-map') + context.keybinding() .on(t('background.minimap.key'), toggle); - - d3_select(document) - .call(keybinding); } return map_in_map; diff --git a/modules/ui/modal.js b/modules/ui/modal.js index 91de76e8a..9ffc14ade 100644 --- a/modules/ui/modal.js +++ b/modules/ui/modal.js @@ -34,7 +34,8 @@ export function uiModal(selection, blocking) { .duration(200) .style('top','0px'); - keybinding.off(); + d3_select(document) + .call(keybinding.unbind); }; diff --git a/modules/ui/modes.js b/modules/ui/modes.js index 503544d95..d27f31dcf 100644 --- a/modules/ui/modes.js +++ b/modules/ui/modes.js @@ -12,7 +12,6 @@ import { import { svgIcon } from '../svg'; import { tooltip } from '../util/tooltip'; -import { utilKeybinding } from '../util'; import { uiTooltipHtml } from './tooltipHtml'; @@ -56,10 +55,8 @@ export function uiModes(context) { .classed('mode-' + exited.id, false); }); - var keybinding = utilKeybinding('mode-buttons'); - modes.forEach(function(mode) { - keybinding.on(mode.key, function() { + context.keybinding().on(mode.key, function() { if (mode.id === 'add-note' && !(notesEnabled() && notesEditable())) return; if (mode.id !== 'add-note' && !editable()) return; @@ -71,9 +68,6 @@ export function uiModes(context) { }); }); - d3_select(document) - .call(keybinding); - var debouncedUpdate = _debounce(update, 500, { leading: true, trailing: true }); diff --git a/modules/ui/panels/background.js b/modules/ui/panels/background.js index 5f869405a..38e81fd23 100644 --- a/modules/ui/panels/background.js +++ b/modules/ui/panels/background.js @@ -20,8 +20,8 @@ export function uiPanelBackground(context) { var debouncedRedraw = _debounce(redraw, 250); function redraw(selection) { - var source = background.baseLayerSource(), - isDG = (source.id.match(/^DigitalGlobe/i) !== null); + var source = background.baseLayerSource(); + var isDG = (source.id.match(/^DigitalGlobe/i) !== null); if (currSourceName !== source.name()) { currSourceName = source.name(); diff --git a/modules/ui/save.js b/modules/ui/save.js index 6b4f0d90d..686f548c6 100644 --- a/modules/ui/save.js +++ b/modules/ui/save.js @@ -1,16 +1,11 @@ import { interpolateRgb as d3_interpolateRgb } from 'd3-interpolate'; - -import { - event as d3_event, - select as d3_select -} from 'd3-selection'; +import { event as d3_event } from 'd3-selection'; import { t } from '../util/locale'; import { modeSave } from '../modes'; import { svgIcon } from '../svg'; import { uiCmd } from './cmd'; import { uiTooltipHtml } from './tooltipHtml'; -import { utilKeybinding } from '../util'; import { tooltip } from '../util/tooltip'; @@ -100,12 +95,9 @@ export function uiSave(context) { updateCount(); - var keybinding = utilKeybinding('uiSave') + context.keybinding() .on(key, save, true); - d3_select(document) - .call(keybinding); - context.history() .on('change.save', updateCount); diff --git a/modules/ui/shortcuts.js b/modules/ui/shortcuts.js index 05c124829..43ee2b0b5 100644 --- a/modules/ui/shortcuts.js +++ b/modules/ui/shortcuts.js @@ -8,18 +8,17 @@ import { dataShortcuts } from '../../data'; import { svgIcon } from '../svg'; import { uiCmd } from './cmd'; import { uiModal } from './modal'; -import { utilKeybinding } from '../util'; import { utilDetect } from '../util/detect'; -export function uiShortcuts() { +export function uiShortcuts(context) { var detected = utilDetect(); var _activeTab = 0; var _modalSelection; var _selection = d3_select(null); - var keybinding = utilKeybinding('shortcuts') + context.keybinding() .on(t('shortcuts.toggle.key'), function () { if (d3_selectAll('.modal-shortcuts').size()) { // already showing if (_modalSelection) { @@ -32,10 +31,6 @@ export function uiShortcuts() { } }); - d3_select(document) - .call(keybinding); - - function shortcutsModal(_modalSelection) { _modalSelection.select('.modal') diff --git a/modules/ui/undo_redo.js b/modules/ui/undo_redo.js index 256219678..eef37b5d2 100644 --- a/modules/ui/undo_redo.js +++ b/modules/ui/undo_redo.js @@ -9,7 +9,6 @@ import { t, textDirection } from '../util/locale'; import { svgIcon } from '../svg'; import { uiCmd } from './cmd'; import { uiTooltipHtml } from './tooltipHtml'; -import { utilKeybinding } from '../util'; import { tooltip } from '../util/tooltip'; @@ -64,13 +63,10 @@ export function uiUndoRedo(context) { .call(svgIcon('#iD-icon-' + iconName)); }); - var keybinding = utilKeybinding('undo') + context.keybinding() .on(commands[0].cmd, function() { d3_event.preventDefault(); commands[0].action(); }) .on(commands[1].cmd, function() { d3_event.preventDefault(); commands[1].action(); }); - d3_select(document) - .call(keybinding); - var debouncedUpdate = _debounce(update, 500, { leading: true, trailing: true }); diff --git a/modules/ui/zoom.js b/modules/ui/zoom.js index 27e6286a2..baf4b539b 100644 --- a/modules/ui/zoom.js +++ b/modules/ui/zoom.js @@ -7,7 +7,6 @@ import { t, textDirection } from '../util/locale'; import { svgIcon } from '../svg'; import { uiCmd } from './cmd'; import { uiTooltipHtml } from './tooltipHtml'; -import { utilKeybinding } from '../util'; import { tooltip } from '../util/tooltip'; @@ -72,19 +71,14 @@ export function uiZoom(context) { .call(svgIcon('#iD-icon-' + d.icon, 'light')); }); - var keybinding = utilKeybinding('zoom'); - ['plus', 'ffplus', '=', 'ffequals'].forEach(function(key) { - keybinding.on([key], zoomIn); - keybinding.on([uiCmd('⌘' + key)], zoomInFurther); + context.keybinding().on([key], zoomIn); + context.keybinding().on([uiCmd('⌘' + key)], zoomInFurther); }); ['_', '-', 'ffminus', 'dash'].forEach(function(key) { - keybinding.on([key], zoomOut); - keybinding.on([uiCmd('⌘' + key)], zoomOutFurther); + context.keybinding().on([key], zoomOut); + context.keybinding().on([uiCmd('⌘' + key)], zoomOutFurther); }); - - d3_select(document) - .call(keybinding); }; } diff --git a/modules/util/keybinding.js b/modules/util/keybinding.js index 88019df27..ec643b09c 100644 --- a/modules/util/keybinding.js +++ b/modules/util/keybinding.js @@ -1,3 +1,5 @@ +import _isFunction from 'lodash-es/isFunction'; + import { event as d3_event, select as d3_select @@ -5,11 +7,12 @@ import { export function utilKeybinding(namespace) { - var _keybindings = []; + var _keybindings = {}; function testBindings(isCapturing) { var didMatch = false; + var bindings = Object.keys(_keybindings).map(function(id) { return _keybindings[id]; }); var i, binding; // Most key shortcuts will accept either lower or uppercase ('h' or 'H'), @@ -18,8 +21,8 @@ export function utilKeybinding(namespace) { // (This lets us differentiate between '←'/'⇧←' or '⌘Z'/'⌘⇧Z') // priority match shifted keybindings first - for (i = 0; i < _keybindings.length; i++) { - binding = _keybindings[i]; + for (i = 0; i < bindings.length; i++) { + binding = bindings[i]; if (!binding.event.modifiers.shiftKey) continue; // no shift if (!!binding.capture !== isCapturing) continue; if (matches(binding, true)) { @@ -30,8 +33,8 @@ export function utilKeybinding(namespace) { // then unshifted keybindings if (didMatch) return; - for (i = 0; i < _keybindings.length; i++) { - binding = _keybindings[i]; + for (i = 0; i < bindings.length; i++) { + binding = bindings[i]; if (binding.event.modifiers.shiftKey) continue; // shift if (!!binding.capture !== isCapturing) continue; if (matches(binding, false)) { @@ -99,26 +102,53 @@ export function utilKeybinding(namespace) { function keybinding(selection) { selection = selection || d3_select(document); - selection.on('keydown.capture' + namespace, capture, true); - selection.on('keydown.bubble' + namespace, bubble, false); + selection.on('keydown.capture.' + namespace, capture, true); + selection.on('keydown.bubble.' + namespace, bubble, false); return keybinding; } - - keybinding.off = function(selection) { + // was: keybinding.off() + keybinding.unbind = function(selection) { _keybindings = []; selection = selection || d3_select(document); - selection.on('keydown.capture' + namespace, null); - selection.on('keydown.bubble' + namespace, null); + selection.on('keydown.capture.' + namespace, null); + selection.on('keydown.bubble.' + namespace, null); return keybinding; }; - keybinding.on = function(codes, callback, capture) { + keybinding.clear = function() { + _keybindings = {}; + return keybinding; + }; + + + // Remove one or more keycode bindings. + keybinding.off = function(codes, capture) { var arr = [].concat(codes); + for (var i = 0; i < arr.length; i++) { - var code = arr[i]; + var id = arr[i] + (capture ? '-capture' : '-bubble'); + delete _keybindings[id]; + } + return keybinding; + }; + + + // Add one or more keycode bindings. + keybinding.on = function(codes, callback, capture) { + if (!_isFunction(callback)) { + return keybinding.off(codes, capture); + } + + var arr = [].concat(codes); + + for (var i = 0; i < arr.length; i++) { + var id = arr[i] + (capture ? '-capture' : '-bubble'); var binding = { + id: id, + capture: capture, + callback: callback, event: { key: undefined, // preferred keyCode: 0, // fallback @@ -128,33 +158,36 @@ export function utilKeybinding(namespace) { altKey: false, metaKey: false } - }, - capture: capture, - callback: callback + } }; - code = code.toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g); + if (_keybindings[id]) { + console.warn('warning: duplicate keybinding for "' + id + '"'); // eslint-disable-line no-console + } - for (var j = 0; j < code.length; j++) { + _keybindings[id] = binding; + + var matches = arr[i].toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g); + for (var j = 0; j < matches.length; j++) { // Normalise matching errors - if (code[j] === '++') code[j] = '+'; + if (matches[j] === '++') matches[j] = '+'; - if (code[j] in utilKeybinding.modifierCodes) { - binding.event.modifiers[utilKeybinding.modifierProperties[utilKeybinding.modifierCodes[code[j]]]] = true; + if (matches[j] in utilKeybinding.modifierCodes) { + var prop = utilKeybinding.modifierProperties[utilKeybinding.modifierCodes[matches[j]]]; + binding.event.modifiers[prop] = true; } else { - binding.event.key = utilKeybinding.keys[code[j]] || code[j]; - if (code[j] in utilKeybinding.keyCodes) { - binding.event.keyCode = utilKeybinding.keyCodes[code[j]]; + binding.event.key = utilKeybinding.keys[matches[j]] || matches[j]; + if (matches[j] in utilKeybinding.keyCodes) { + binding.event.keyCode = utilKeybinding.keyCodes[matches[j]]; } } } - - _keybindings.push(binding); } return keybinding; }; + return keybinding; } @@ -163,7 +196,6 @@ export function utilKeybinding(namespace) { * See https://github.com/keithamus/jwerty */ - utilKeybinding.modifierCodes = { // Shift key, ⇧ '⇧': 16, shift: 16, diff --git a/test/spec/util/keybinding.js b/test/spec/util/keybinding.js index 672060dba..0b12c4040 100644 --- a/test/spec/util/keybinding.js +++ b/test/spec/util/keybinding.js @@ -9,7 +9,7 @@ describe('utilKeybinding', function() { }); afterEach(function () { - keybinding.off(d3.select(document)); + d3.select(document).call(keybinding.unbind); input.remove(); }); @@ -86,13 +86,13 @@ describe('utilKeybinding', function() { expect(spy).to.have.been.calledOnce; }); - it('resets bindings when keybinding.off is called', function () { + it('resets bindings when keybinding.unbind is called', function () { d3.select(document).call(keybinding.on('A', spy)); happen.keydown(document, {keyCode: 65}); expect(spy).to.have.been.calledOnce; spy = sinon.spy(); - d3.select(document).call(keybinding.off); + d3.select(document).call(keybinding.unbind); d3.select(document).call(keybinding.on('A', spy)); happen.keydown(document, {keyCode: 65}); expect(spy).to.have.been.calledOnce; From 5f2ba5a1002c7ca2649979bac9c0b5416af8fba0 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 13 Nov 2018 22:55:56 -0500 Subject: [PATCH 6/8] Update rollup dependency (closes #5478) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8ffcc6a78..a42d69b2d 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "osm-community-index": "0.4.7", "phantomjs-prebuilt": "~2.1.11", "request": "^2.88.0", - "rollup": "~0.66.0", + "rollup": "~0.67.1", "rollup-plugin-commonjs": "^9.0.0", "rollup-plugin-includepaths": "~0.2.3", "rollup-plugin-json": "^3.0.0", From 317a3be93b0f2fdd6f7ffac5baa8f82ad241a595 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 14 Nov 2018 14:22:10 -0500 Subject: [PATCH 7/8] Add `tiler.getGeoJSON`, useful for debugging tile settings --- modules/util/tiler.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/modules/util/tiler.js b/modules/util/tiler.js index b695da3da..ead3a75b7 100644 --- a/modules/util/tiler.js +++ b/modules/util/tiler.js @@ -111,6 +111,31 @@ export function utilTiler() { }; + /** + * getGeoJSON() returns a FeatureCollection for debugging tiles + */ + tiler.getGeoJSON = function(projection) { + var features = tiler.getTiles(projection).map(function(tile) { + return { + type: 'Feature', + properties: { + id: tile.id, + name: tile.id + }, + geometry: { + type: 'Polygon', + coordinates: [ tile.extent.polygon() ] + } + }; + }); + + return { + type: 'FeatureCollection', + features: features + }; + }; + + tiler.tileSize = function(val) { if (!arguments.length) return _tileSize; _tileSize = val; From 1731ce46511d8c842692ab77202f856c6bab412e Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 14 Nov 2018 14:48:42 -0500 Subject: [PATCH 8/8] Partition viewport by tiles, not by pixels (closes #4297) The previous approach split the viewport up by pixels, but each time the view moved, the pixels would change, so it was not a stable selection of the streetview data, and the markers would fight for position as the user moved around. This approach uses utilTiler to partition the view into stable tiles. --- modules/services/mapillary.js | 71 ++++++++--------------------- modules/services/openstreetcam.js | 50 ++++++++------------ modules/services/streetside.js | 57 ++++++++--------------- modules/svg/mapillary_images.js | 4 +- test/spec/services/mapillary.js | 14 +++--- test/spec/services/openstreetcam.js | 7 +-- test/spec/services/streetside.js | 7 +-- 7 files changed, 76 insertions(+), 134 deletions(-) diff --git a/modules/services/mapillary.js b/modules/services/mapillary.js index e7afb42d4..5e13da7b2 100644 --- a/modules/services/mapillary.js +++ b/modules/services/mapillary.js @@ -1,12 +1,9 @@ /* global Mapillary:false */ import _find from 'lodash-es/find'; -import _flatten from 'lodash-es/flatten'; import _forEach from 'lodash-es/forEach'; -import _map from 'lodash-es/map'; import _some from 'lodash-es/some'; import _union from 'lodash-es/union'; -import { range as d3_range } from 'd3-array'; import { dispatch as d3_dispatch } from 'd3-dispatch'; import { request as d3_request } from 'd3-request'; import { @@ -213,57 +210,29 @@ function parsePagination(links) { } -// partition viewport into `psize` x `psize` regions -function partitionViewport(psize, projection) { - var dimensions = projection.clipExtent()[1]; - psize = psize || 16; - var cols = d3_range(0, dimensions[0], psize); - var rows = d3_range(0, dimensions[1], psize); - var partitions = []; +// partition viewport into higher zoom tiles +function partitionViewport(projection) { + var z = geoScaleToZoom(projection.scale()); + var z2 = (Math.ceil(z * 2) / 2) + 2.5; // round to next 0.5 and add 2.5 + var tiler = utilTiler().zoomExtent([z2, z2]); - rows.forEach(function(y) { - cols.forEach(function(x) { - var min = [x, y + psize]; - var max = [x + psize, y]; - partitions.push( - geoExtent(projection.invert(min), projection.invert(max))); - }); - }); - - return partitions; + return tiler.getTiles(projection) + .map(function(tile) { return tile.extent; }); } // no more than `limit` results per partition. -function searchLimited(psize, limit, projection, rtree) { - limit = limit || 3; +function searchLimited(limit, projection, rtree) { + limit = limit || 5; - var partitions = partitionViewport(psize, projection); - var results; + return partitionViewport(projection) + .reduce(function(result, extent) { + var found = rtree.search(extent.bbox()) + .slice(0, limit) + .map(function(d) { return d.data; }); - // console.time('previous'); - results = _flatten(_map(partitions, function(extent) { - return rtree.search(extent.bbox()) - .slice(0, limit) - .map(function(d) { return d.data; }); - })); - // console.timeEnd('previous'); - - // console.time('new'); - // results = partitions.reduce(function(result, extent) { - // var found = rtree.search(extent.bbox()) - // .map(function(d) { return d.data; }) - // .sort(function(a, b) { - // return a.loc[1] - b.loc[1]; - // // return a.key.localeCompare(b.key); - // }) - // .slice(0, limit); - - // return (found.length ? result.concat(found) : result); - // }, []); - // console.timeEnd('new'); - - return results; + return (found.length ? result.concat(found) : result); + }, []); } @@ -309,14 +278,14 @@ export default { images: function(projection) { - var psize = 16, limit = 3; - return searchLimited(psize, limit, projection, _mlyCache.images.rtree); + var limit = 5; + return searchLimited(limit, projection, _mlyCache.images.rtree); }, signs: function(projection) { - var psize = 32, limit = 3; - return searchLimited(psize, limit, projection, _mlyCache.map_features.rtree); + var limit = 5; + return searchLimited(limit, projection, _mlyCache.map_features.rtree); }, diff --git a/modules/services/openstreetcam.js b/modules/services/openstreetcam.js index 07ef328d4..4f1d180bc 100644 --- a/modules/services/openstreetcam.js +++ b/modules/services/openstreetcam.js @@ -1,10 +1,7 @@ import _find from 'lodash-es/find'; -import _flatten from 'lodash-es/flatten'; import _forEach from 'lodash-es/forEach'; -import _map from 'lodash-es/map'; import _union from 'lodash-es/union'; -import { range as d3_range } from 'd3-array'; import { dispatch as d3_dispatch } from 'd3-dispatch'; import { request as d3_request } from 'd3-request'; @@ -164,40 +161,29 @@ function loadNextTilePage(which, currZoom, url, tile) { } -// partition viewport into `psize` x `psize` regions -function partitionViewport(psize, projection) { - var dimensions = projection.clipExtent()[1]; - psize = psize || 16; - var cols = d3_range(0, dimensions[0], psize); - var rows = d3_range(0, dimensions[1], psize); - var partitions = []; +// partition viewport into higher zoom tiles +function partitionViewport(projection) { + var z = geoScaleToZoom(projection.scale()); + var z2 = (Math.ceil(z * 2) / 2) + 2.5; // round to next 0.5 and add 2.5 + var tiler = utilTiler().zoomExtent([z2, z2]); - rows.forEach(function(y) { - cols.forEach(function(x) { - var min = [x, y + psize]; - var max = [x + psize, y]; - partitions.push( - geoExtent(projection.invert(min), projection.invert(max))); - }); - }); - - return partitions; + return tiler.getTiles(projection) + .map(function(tile) { return tile.extent; }); } // no more than `limit` results per partition. -function searchLimited(psize, limit, projection, rtree) { - limit = limit || 3; +function searchLimited(limit, projection, rtree) { + limit = limit || 5; - var partitions = partitionViewport(psize, projection); - var results; + return partitionViewport(projection) + .reduce(function(result, extent) { + var found = rtree.search(extent.bbox()) + .slice(0, limit) + .map(function(d) { return d.data; }); - results = _flatten(_map(partitions, function(extent) { - return rtree.search(extent.bbox()) - .slice(0, limit) - .map(function(d) { return d.data; }); - })); - return results; + return (found.length ? result.concat(found) : result); + }, []); } @@ -237,8 +223,8 @@ export default { images: function(projection) { - var psize = 16, limit = 3; - return searchLimited(psize, limit, projection, _oscCache.images.rtree); + var limit = 5; + return searchLimited(limit, projection, _oscCache.images.rtree); }, diff --git a/modules/services/streetside.js b/modules/services/streetside.js index d748d9251..a7bdf4eb4 100644 --- a/modules/services/streetside.js +++ b/modules/services/streetside.js @@ -1,12 +1,9 @@ import _extend from 'lodash-es/extend'; import _find from 'lodash-es/find'; -import _flatten from 'lodash-es/flatten'; import _forEach from 'lodash-es/forEach'; -import _map from 'lodash-es/map'; import _union from 'lodash-es/union'; import { dispatch as d3_dispatch } from 'd3-dispatch'; -import { range as d3_range } from 'd3-array'; import { timer as d3_timer } from 'd3-timer'; import { @@ -25,6 +22,7 @@ import { geoMetersToLon, geoPointInPolygon, geoRotate, + geoScaleToZoom, geoVecLength } from '../geo'; @@ -233,45 +231,30 @@ function getBubbles(url, tile, callback) { }); } -/** - * partitionViewport() partition viewport into `psize` x `psize` regions. - */ -function partitionViewport(psize, projection) { - var dimensions = projection.clipExtent()[1]; - psize = psize || 16; - var cols = d3_range(0, dimensions[0], psize); - var rows = d3_range(0, dimensions[1], psize); - var partitions = []; +// partition viewport into higher zoom tiles +function partitionViewport(projection) { + var z = geoScaleToZoom(projection.scale()); + var z2 = (Math.ceil(z * 2) / 2) + 2.5; // round to next 0.5 and add 2.5 + var tiler = utilTiler().zoomExtent([z2, z2]); - rows.forEach(function (y) { - cols.forEach(function (x) { - var min = [x, y + psize]; - var max = [x + psize, y]; - partitions.push(geoExtent(projection.invert(min), projection.invert(max))); - }); - }); - - return partitions; + return tiler.getTiles(projection) + .map(function(tile) { return tile.extent; }); } -/** - * searchLimited(). - */ -function searchLimited(psize, limit, projection, rtree) { - limit = limit || 3; +// no more than `limit` results per partition. +function searchLimited(limit, projection, rtree) { + limit = limit || 5; - var partitions = partitionViewport(psize, projection); - var results; + return partitionViewport(projection) + .reduce(function(result, extent) { + var found = rtree.search(extent.bbox()) + .slice(0, limit) + .map(function(d) { return d.data; }); - results = _flatten(_map(partitions, function (extent) { - return rtree.search(extent.bbox()) - .slice(0, limit) - .map(function (d) { return d.data; }); - })); - - return results; + return (found.length ? result.concat(found) : result); + }, []); } @@ -485,8 +468,8 @@ export default { * bubbles() */ bubbles: function (projection) { - var psize = 32, limit = 3; - return searchLimited(psize, limit, projection, _ssCache.bubbles.rtree); + var limit = 5; + return searchLimited(limit, projection, _ssCache.bubbles.rtree); }, diff --git a/modules/svg/mapillary_images.js b/modules/svg/mapillary_images.js index 641882790..55dfa7dd3 100644 --- a/modules/svg/mapillary_images.js +++ b/modules/svg/mapillary_images.js @@ -217,8 +217,8 @@ export function svgMapillaryImages(projection, context, dispatch) { function drawImages(selection) { - var enabled = svgMapillaryImages.enabled, - service = getService(); + var enabled = svgMapillaryImages.enabled; + var service = getService(); layer = selection.selectAll('.layer-mapillary-images') .data(service ? [0] : []); diff --git a/test/spec/services/mapillary.js b/test/spec/services/mapillary.js index f01107fbd..6e9667a0b 100644 --- a/test/spec/services/mapillary.js +++ b/test/spec/services/mapillary.js @@ -248,18 +248,19 @@ describe('iD.serviceMapillary', function() { ]); }); - it('limits results no more than 3 stacked images in one spot', function() { + it('limits results no more than 5 stacked images in one spot', function() { var features = [ { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10,0], ca: 90 } }, { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10,0], ca: 90 } }, { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '2', loc: [10,0], ca: 90 } }, { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '3', loc: [10,0], ca: 90 } }, - { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], ca: 90 } } + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], ca: 90 } }, + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '5', loc: [10,0], ca: 90 } } ]; mapillary.cache().images.rtree.load(features); var res = mapillary.images(context.projection); - expect(res).to.have.length.of.at.most(3); + expect(res).to.have.length.of.at.most(5); }); }); @@ -284,7 +285,7 @@ describe('iD.serviceMapillary', function() { ]); }); - it('limits results no more than 3 stacked signs in one spot', function() { + it('limits results no more than 5 stacked signs in one spot', function() { var detections = [{ detection_key: '78vqha63gs1upg15s823qckcmn', image_key: 'bwYs-uXLDvm_meo_EC5Nzw' @@ -294,12 +295,13 @@ describe('iD.serviceMapillary', function() { { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10,0], detections: detections } }, { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '2', loc: [10,0], detections: detections } }, { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '3', loc: [10,0], detections: detections } }, - { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], detections: detections } } + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], detections: detections } }, + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '5', loc: [10,0], detections: detections } } ]; mapillary.cache().map_features.rtree.load(features); var res = mapillary.signs(context.projection); - expect(res).to.have.length.of.at.most(3); + expect(res).to.have.length.of.at.most(5); }); }); diff --git a/test/spec/services/openstreetcam.js b/test/spec/services/openstreetcam.js index 01658facf..67bbadf0d 100644 --- a/test/spec/services/openstreetcam.js +++ b/test/spec/services/openstreetcam.js @@ -244,18 +244,19 @@ describe('iD.serviceOpenstreetcam', function() { ]); }); - it('limits results no more than 3 stacked images in one spot', function() { + it('limits results no more than 5 stacked images in one spot', function() { var features = [ { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 0 } }, { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 1 } }, { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '2', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 2 } }, { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '3', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 3 } }, - { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 4 } } + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 4 } }, + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '5', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 5 } } ]; openstreetcam.cache().images.rtree.load(features); var res = openstreetcam.images(context.projection); - expect(res).to.have.length.of.at.most(3); + expect(res).to.have.length.of.at.most(5); }); }); diff --git a/test/spec/services/streetside.js b/test/spec/services/streetside.js index ab6ca67a2..f0ebebe79 100644 --- a/test/spec/services/streetside.js +++ b/test/spec/services/streetside.js @@ -132,18 +132,19 @@ describe('iD.serviceStreetside', function() { ]); }); - it('limits results no more than 3 stacked bubbles in one spot', function() { + it('limits results no more than 5 stacked bubbles in one spot', function() { var features = [ { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 1, loc: [10, 0], ca: 90, pr: undefined, ne: 2, pano: true, sequence_id: 1 } }, { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 2, loc: [10, 0], ca: 90, pr: 1, ne: 3, pano: true, sequence_id: 1 } }, { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 3, loc: [10, 0], ca: 90, pr: 2, ne: 4, pano: true, sequence_id: 1 } }, { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 4, loc: [10, 0], ca: 90, pr: 3, ne: 5, pano: true, sequence_id: 1 } }, - { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 5, loc: [10, 0], ca: 90, pr: 4, ne: undefined, pano: true, sequence_id: 1 } } + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 5, loc: [10, 0], ca: 90, pr: 4, ne: 6, pano: true, sequence_id: 1 } }, + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 6, loc: [10, 0], ca: 90, pr: 5, ne: undefined, pano: true, sequence_id: 1 } } ]; streetside.cache().bubbles.rtree.load(features); var res = streetside.bubbles(context.projection); - expect(res).to.have.length.of.at.most(3); + expect(res).to.have.length.of.at.most(5); }); });