diff --git a/app/script/components/deviceCard.js b/app/script/components/deviceCard.js index 416ddf2a6730..61746c816ec5 100644 --- a/app/script/components/deviceCard.js +++ b/app/script/components/deviceCard.js @@ -68,9 +68,9 @@ z.components.DeviceCard = class DeviceCard { _update_location() { if (this.device && this.device.location) { - z.location.get_location(this.device.location.lat, this.device.location.lon).then(retrieved_location => { + z.location.getLocation(this.device.location.lat, this.device.location.lon).then(retrieved_location => { if (retrieved_location) { - this._update_activation_location(`${retrieved_location.place}, ${retrieved_location.country_code}`); + this._update_activation_location(`${retrieved_location.place}, ${retrieved_location.countryCode}`); } }); } diff --git a/app/script/entity/message/Location.js b/app/script/entity/message/Location.js index b544ce4da92d..946f832a6893 100644 --- a/app/script/entity/message/Location.js +++ b/app/script/entity/message/Location.js @@ -33,7 +33,7 @@ z.entity.Location = class Location extends z.entity.Asset { this.zoom = ''; this.link_src = ko.pureComputed(() => { - return z.location.get_maps_url(this.latitude, this.longitude, this.name, this.zoom); + return z.location.getMapsUrl(this.latitude, this.longitude, this.name, this.zoom); }); } }; diff --git a/app/script/location/GeoLocation.js b/app/script/location/GeoLocation.js index 214831ed48f6..5bf941a6415f 100644 --- a/app/script/location/GeoLocation.js +++ b/app/script/location/GeoLocation.js @@ -22,31 +22,42 @@ window.z = window.z || {}; z.location = (() => { - const GOOGLE_GEOCODING_BASE_URL = 'https://maps.googleapis.com/maps/api/geocode/json'; const API_KEY = 'AIzaSyCKxxKw5JBZ5zEFtoirtgnw8omvH7gWzfo'; - const _parse_results = results => { - const res = {}; - const [result] = results; - res.address = result.formatted_address; - res.lat = result.geometry.location.lat; - res.lng = result.geometry.location.lng; - for (const component of result.address_components) { - const name = component.long_name || component.short_name; - for (const type of component.types) { - res[type] = name; - if (type === 'country') { - res.country_code = component.short_name || ''; + const GOOGLE_GEOCODE_BASE_URL = 'https://maps.googleapis.com/maps/api/geocode/json'; + + const _parseResults = ([result]) => { + const { + administrative_area_level_1: areaLevel1, + administrative_area_level_2: areaLevel2, + administrative_area_level_3: areaLevel3, + address_components: addressComponents, + formatted_address: formattedAddress, + geometry, + locality, + natural_feature: naturalFeature, + } = result; + + const parsedResults = { + address: formattedAddress, + lat: geometry.location.lat, + lng: geometry.location.lng, + place: locality || naturalFeature || areaLevel3 || areaLevel2 || areaLevel1, + }; + + addressComponents.forEach(({long_name: longName, short_name: shortName, types}) => { + const name = longName || shortName; + + types.forEach(type => { + parsedResults[type] = name; + const isCountry = type === 'country'; + if (isCountry) { + parsedResults.countryCode = shortName || ''; } - } - } - res.place = - res.locality || - res.natural_feature || - res.administrative_area_level_3 || - res.administrative_area_level_2 || - res.administrative_area_level_1; - delete (res.political != null); - return z.util.ObjectUtil.escape_properties(res); + }); + }); + + delete parsedResults.political; + return z.util.ObjectUtil.escape_properties(parsedResults); }; /** @@ -55,19 +66,17 @@ z.location = (() => { * @param {number} longitude - Longitude of location * @returns {Promise} Resolves with the location information */ - const get_location = (latitude, longitude) => { + const _getLocation = (latitude, longitude) => { return new Promise((resolve, reject) => { if (latitude == null || longitude == null) { reject(new Error('You need to specify latitude and longitude in order to retrieve the location')); } - $.ajax({ - url: `${GOOGLE_GEOCODING_BASE_URL}?latlng=${latitude},${longitude}&key=${API_KEY}`, - }) + + const requestUrl = `${GOOGLE_GEOCODE_BASE_URL}?latlng=${latitude},${longitude}&key=${API_KEY}`; + $.ajax({url: requestUrl}) .done(response => { - if (response.status === 'OK') { - return resolve(_parse_results(response.results)); - } - return resolve(); + const isStatusOk = response.status === 'OK'; + return isStatusOk ? resolve(_parseResults(response.results)) : resolve(); }) .fail((jqXHR, textStatus, errorThrown) => reject(new Error(errorThrown))); }); @@ -76,27 +85,24 @@ z.location = (() => { /** * Return link to Google Maps * - * @param {number} lat - Latitude of location - * @param {number} lng - Longitude of location + * @param {number} latitude - Latitude of location + * @param {number} longitude - Longitude of location * @param {string} name - Name of location * @param {string} zoom - Map zoom level * @returns {string} URL to location in Google Maps */ - const get_maps_url = (lat, lng, name, zoom) => { - let base_url; - base_url = 'https://google.com/maps/'; - if (name != null) { - base_url += `place/${name}/`; - } - base_url += `@${lat},${lng}`; - if (zoom != null) { - base_url += `,${zoom}z`; - } - return base_url; + const _getMapsUrl = (latitude, longitude, name, zoom) => { + const baseUrl = 'https://google.com/maps/'; + + const nameParam = name ? `place/${name}/` : ''; + const locationParam = `@${latitude},${longitude}`; + const zoomParam = zoom ? `,${zoom}z` : ''; + + return `${baseUrl}${nameParam}${locationParam}${zoomParam}`; }; return { - get_location, - get_maps_url, + getLocation: _getLocation, + getMapsUrl: _getMapsUrl, }; })(); diff --git a/app/script/view_model/content/PreferencesDeviceDetailsViewModel.js b/app/script/view_model/content/PreferencesDeviceDetailsViewModel.js index 98fcecbd1393..d455e19114ae 100644 --- a/app/script/view_model/content/PreferencesDeviceDetailsViewModel.js +++ b/app/script/view_model/content/PreferencesDeviceDetailsViewModel.js @@ -75,9 +75,9 @@ z.ViewModel.content.PreferencesDeviceDetailsViewModel = class PreferencesDeviceD } _update_device_location(location) { - z.location.get_location(location.lat, location.lon).then(retrieved_location => { + z.location.getLocation(location.lat, location.lon).then(retrieved_location => { if (retrieved_location) { - this._update_activation_location(`${retrieved_location.place}, ${retrieved_location.country_code}`); + this._update_activation_location(`${retrieved_location.place}, ${retrieved_location.countryCode}`); } }); } diff --git a/app/script/view_model/content/PreferencesDevicesViewModel.js b/app/script/view_model/content/PreferencesDevicesViewModel.js index e0a117c92e39..96c2d7ff2944 100644 --- a/app/script/view_model/content/PreferencesDevicesViewModel.js +++ b/app/script/view_model/content/PreferencesDevicesViewModel.js @@ -74,9 +74,9 @@ z.ViewModel.content.PreferencesDevicesViewModel = class PreferencesDevicesViewMo } _update_device_location(location) { - z.location.get_location(location.lat, location.lon).then(retrieved_location => { + z.location.getLocation(location.lat, location.lon).then(retrieved_location => { if (retrieved_location) { - this._update_activation_location(`${retrieved_location.place}, ${retrieved_location.country_code}`); + this._update_activation_location(`${retrieved_location.place}, ${retrieved_location.countryCode}`); } }); } diff --git a/test/unit_tests/location/GeoLocationSpec.js b/test/unit_tests/location/GeoLocationSpec.js index eb73b01cf6e9..3ff06d7e92a8 100644 --- a/test/unit_tests/location/GeoLocationSpec.js +++ b/test/unit_tests/location/GeoLocationSpec.js @@ -24,7 +24,7 @@ 'use strict'; describe('z.location', () => { - describe('get_location', () => { + describe('getLocation', () => { it('resolves a latitude & longitude via Google Maps API into a location name', done => { // prettier-ignore const locations = [{'address_components': [{'long_name': '2', 'short_name': '2', 'types': ['street_number']}, {'long_name': 'Alexanderstraße', 'short_name': 'Alexanderstraße', 'types': ['route']}, {'long_name': 'Mitte', 'short_name': 'Mitte', 'types': ['political', 'sublocality', 'sublocality_level_1']}, {'long_name': 'Berlin', 'short_name': 'Berlin', 'types': ['locality', 'political']}, {'long_name': 'Berlin', 'short_name': 'Berlin', 'types': ['administrative_area_level_1', 'political']}, {'long_name': 'Deutschland', 'short_name': 'DE', 'types': ['country', 'political']}, {'long_name': '10178', 'short_name': '10178', 'types': ['postal_code']}], 'formatted_address': 'Alexanderstraße 2, 10178 Berlin, Deutschland', 'geometry': {'location': {'lat': 52.523824, 'lng': 13.4145348}, 'location_type': 'ROOFTOP', 'viewport': {'northeast': {'lat': 52.52517298029149, 'lng': 13.4158837802915}, 'southwest': {'lat': 52.52247501970849, 'lng': 13.4131858197085}}}, 'place_id': 'ChIJ7xU9wx5OqEcRbjo-v63nALk', 'types': ['street_address']}, {'address_components': [{'long_name': 'Berlin (Alexanderplatz)', 'short_name': 'Berlin (Alexanderplatz)', 'types': ['bus_station', 'establishment', 'point_of_interest', 'transit_station']}, {'long_name': 'Mitte', 'short_name': 'Mitte', 'types': ['political', 'sublocality', 'sublocality_level_1']}, {'long_name': 'Berlin', 'short_name': 'Berlin', 'types': ['locality', 'political']}, {'long_name': 'Berlin', 'short_name': 'Berlin', 'types': ['administrative_area_level_1', 'political']}, {'long_name': 'Deutschland', 'short_name': 'DE', 'types': ['country', 'political']}, {'long_name': '10178', 'short_name': '10178', 'types': ['postal_code']}], 'formatted_address': 'Berlin (Alexanderplatz), 10178 Berlin, Deutschland', 'geometry': {'location': {'lat': 52.523198, 'lng': 13.414529}, 'location_type': 'APPROXIMATE', 'viewport': {'northeast': {'lat': 52.5245469802915, 'lng': 13.4158779802915}, 'southwest': {'lat': 52.5218490197085, 'lng': 13.4131800197085}}}, 'place_id': 'ChIJHTq-2R5OqEcRNJ0lG6qK3T4', 'types': ['bus_station', 'establishment', 'point_of_interest', 'transit_station']}, {'address_components': [{'long_name': 'Mitte', 'short_name': 'Mitte', 'types': ['political', 'sublocality', 'sublocality_level_2']}, {'long_name': 'Mitte', 'short_name': 'Mitte', 'types': ['political', 'sublocality', 'sublocality_level_1']}, {'long_name': 'Berlin', 'short_name': 'Berlin', 'types': ['locality', 'political']}, {'long_name': 'Berlin', 'short_name': 'Berlin', 'types': ['administrative_area_level_1', 'political']}, {'long_name': 'Deutschland', 'short_name': 'DE', 'types': ['country', 'political']}], 'formatted_address': 'Mitte, Berlin, Deutschland', 'geometry': {'bounds': {'northeast': {'lat': 52.5403962, 'lng': 13.4293586}, 'southwest': {'lat': 52.5040199, 'lng': 13.3658543}}, 'location': {'lat': 52.519444, 'lng': 13.406667}, 'location_type': 'APPROXIMATE', 'viewport': {'northeast': {'lat': 52.5403962, 'lng': 13.4293586}, 'southwest': {'lat': 52.5040199, 'lng': 13.3658543}}}, 'place_id': 'ChIJjw3Y6t9RqEcR8jUVWEcgISY', 'types': ['political', 'sublocality', 'sublocality_level_2']}, {'address_components': [{'long_name': 'Mitte', 'short_name': 'Mitte', 'types': ['political', 'sublocality', 'sublocality_level_1']}, {'long_name': 'Berlin', 'short_name': 'Berlin', 'types': ['locality', 'political']}, {'long_name': 'Berlin', 'short_name': 'Berlin', 'types': ['administrative_area_level_1', 'political']}, {'long_name': 'Deutschland', 'short_name': 'DE', 'types': ['country', 'political']}], 'formatted_address': 'Mitte, Berlin, Deutschland', 'geometry': {'bounds': {'northeast': {'lat': 52.5677268, 'lng': 13.4293586}, 'southwest': {'lat': 52.4987314, 'lng': 13.3015252}}, 'location': {'lat': 52.5306438, 'lng': 13.3830683}, 'location_type': 'APPROXIMATE', 'viewport': {'northeast': {'lat': 52.5677268, 'lng': 13.4293586}, 'southwest': {'lat': 52.4987314, 'lng': 13.3015252}}}, 'place_id': 'ChIJAUK8it1RqEcRwKtfW0YgIQU', 'types': ['political', 'sublocality', 'sublocality_level_1']}, {'address_components': [{'long_name': 'Berlin', 'short_name': 'Berlin', 'types': ['locality', 'political']}, {'long_name': 'Berlin', 'short_name': 'Berlin', 'types': ['administrative_area_level_1', 'political']}, {'long_name': 'Deutschland', 'short_name': 'DE', 'types': ['country', 'political']}], 'formatted_address': 'Berlin, Deutschland', 'geometry': {'bounds': {'northeast': {'lat': 52.6754542, 'lng': 13.7611176}, 'southwest': {'lat': 52.338234, 'lng': 13.088346}}, 'location': {'lat': 52.52000659999999, 'lng': 13.404954}, 'location_type': 'APPROXIMATE', 'viewport': {'northeast': {'lat': 52.6754542, 'lng': 13.7611175}, 'southwest': {'lat': 52.33962959999999, 'lng': 13.0911733}}}, 'place_id': 'ChIJAVkDPzdOqEcRcDteW0YgIQQ', 'types': ['locality', 'political']}, {'address_components': [{'long_name': '10178', 'short_name': '10178', 'types': ['postal_code']}, {'long_name': 'Berlin', 'short_name': 'Berlin', 'types': ['locality', 'political']}, {'long_name': 'Berlin', 'short_name': 'Berlin', 'types': ['administrative_area_level_1', 'political']}, {'long_name': 'Deutschland', 'short_name': 'DE', 'types': ['country', 'political']}], 'formatted_address': '10178 Berlin, Deutschland', 'geometry': {'bounds': {'northeast': {'lat': 52.528538, 'lng': 13.4296049}, 'southwest': {'lat': 52.5120099, 'lng': 13.3940579}}, 'location': {'lat': 52.5221879, 'lng': 13.4093313}, 'location_type': 'APPROXIMATE', 'viewport': {'northeast': {'lat': 52.528538, 'lng': 13.4296049}, 'southwest': {'lat': 52.5120099, 'lng': 13.3940579}}}, 'place_id': 'ChIJ85n72yFOqEcRIM89lUkgIRw', 'types': ['postal_code']}, {'address_components': [{'long_name': 'Berlin', 'short_name': 'Berlin', 'types': ['administrative_area_level_1', 'establishment', 'point_of_interest', 'political']}, {'long_name': 'Deutschland', 'short_name': 'DE', 'types': ['country', 'political']}], 'formatted_address': 'Berlin, Deutschland', 'geometry': {'bounds': {'northeast': {'lat': 52.6754542, 'lng': 13.7611176}, 'southwest': {'lat': 52.338234, 'lng': 13.088346}}, 'location': {'lat': 52.4938053, 'lng': 13.4552919}, 'location_type': 'APPROXIMATE', 'viewport': {'northeast': {'lat': 52.6754542, 'lng': 13.7611175}, 'southwest': {'lat': 52.33962959999999, 'lng': 13.0911733}}}, 'place_id': 'ChIJ8_KccStOqEcRhtFXjKWPuo0', 'types': ['administrative_area_level_1', 'establishment', 'point_of_interest', 'political']}, {'address_components': [{'long_name': 'Metropolregion Berlin/Brandenburg', 'short_name': 'Metropolregion Berlin/Brandenburg', 'types': ['political']}, {'long_name': 'Deutschland', 'short_name': 'DE', 'types': ['country', 'political']}], 'formatted_address': 'Metropolregion Berlin/Brandenburg, Deutschland', 'geometry': {'bounds': {'northeast': {'lat': 53.55898, 'lng': 14.7658261}, 'southwest': {'lat': 51.3590586, 'lng': 11.265727}}, 'location': {'lat': 52.268409, 'lng': 13.5287229}, 'location_type': 'APPROXIMATE', 'viewport': {'northeast': {'lat': 53.55898, 'lng': 14.765826}, 'southwest': {'lat': 51.3590586, 'lng': 11.265727}}}, 'place_id': 'ChIJZ4kamin3qEcRQ5VPQ7O8dWY', 'types': ['political']}, {'address_components': [{'long_name': 'Deutschland', 'short_name': 'DE', 'types': ['country', 'political']}], 'formatted_address': 'Deutschland', 'geometry': {'bounds': {'northeast': {'lat': 55.0815, 'lng': 15.0418962}, 'southwest': {'lat': 47.2701115, 'lng': 5.8663425}}, 'location': {'lat': 51.165691, 'lng': 10.451526}, 'location_type': 'APPROXIMATE', 'viewport': {'northeast': {'lat': 55.05812359999999, 'lng': 15.0418487}, 'southwest': {'lat': 47.2702482, 'lng': 5.8664874}}}, 'place_id': 'ChIJa76xwh5ymkcRW-WRjmtd6HU', 'types': ['country', 'political']}]; @@ -42,9 +42,9 @@ describe('z.location', () => { ]); z.location - .get_location(latitude, longitude) + .getLocation(latitude, longitude) .then(location => { - expect(location.country_code).toBe('DE'); + expect(location.countryCode).toBe('DE'); expect(location.place).toBe('Berlin'); done(); }) @@ -67,9 +67,9 @@ describe('z.location', () => { ]); z.location - .get_location(latitude, longitude) + .getLocation(latitude, longitude) .then(location => { - expect(location.country_code).toBe('<script>alert("malicious")</script>'); + expect(location.countryCode).toBe('<script>alert("malicious")</script>'); expect(location.place).toBe('Berlin'); done(); }) @@ -77,12 +77,12 @@ describe('z.location', () => { }); }); - describe('get_maps_url', () => { + describe('getMapsUrl', () => { it('should return the proper urls', () => { - expect(z.location.get_maps_url(52, 13)).toBe('https://google.com/maps/@52,13'); - expect(z.location.get_maps_url(52, 13, null, 14)).toBe('https://google.com/maps/@52,13,14z'); - expect(z.location.get_maps_url(52, 13, 'Berlin')).toBe('https://google.com/maps/place/Berlin/@52,13'); - expect(z.location.get_maps_url(52, 13, 'Berlin', 14)).toBe('https://google.com/maps/place/Berlin/@52,13,14z'); + expect(z.location.getMapsUrl(52, 13)).toBe('https://google.com/maps/@52,13'); + expect(z.location.getMapsUrl(52, 13, null, 14)).toBe('https://google.com/maps/@52,13,14z'); + expect(z.location.getMapsUrl(52, 13, 'Berlin')).toBe('https://google.com/maps/place/Berlin/@52,13'); + expect(z.location.getMapsUrl(52, 13, 'Berlin', 14)).toBe('https://google.com/maps/place/Berlin/@52,13,14z'); }); }); });