commit 81a5954c7f669ae4ea6bfd85cb0b2cd95f372e33
parent 56467579e3365b61ff7c8480d197044d7f93662e
Author: Sergey Orlov <wladimirych@gmail.com>
Date: Sun, 2 Aug 2020 11:16:35 +0200
search: extract view from short links. Fixes #486
Diffstat:
2 files changed, 113 insertions(+), 35 deletions(-)
diff --git a/src/lib/leaflet.control.search/providers/links.js b/src/lib/leaflet.control.search/providers/links.js
@@ -1,7 +1,11 @@
import L from 'leaflet';
+import {fetch} from '~/lib/xhr-promise';
+import urlViaCorsProxy from '~/lib/CORSProxy';
+
const MAX_ZOOM = 18;
const MESSAGE_LINK_MALFORMED = 'Invalid coordinates in {name} link';
+const MESSAGE_SHORT_LINK_MALFORMED = 'Broken {name} short link';
function makeSearchResults(lat, lon, zoom, title) {
if (
@@ -37,18 +41,32 @@ function makeSearchResults(lat, lon, zoom, title) {
const YandexMapsUrl = {
isOurUrl: function(url) {
- return Boolean(url.hostname.match(/\byandex\.[^.]+$/u) && url.pathname.match(/^\/maps\//u));
+ return (
+ (url.hostname.match(/\byandex\./u) && url.pathname.match(/^\/maps\//u)) ||
+ url.hostname.match(/static-maps\.yandex\./u)
+ );
},
- getResults: function(url) {
- const paramLl = url.searchParams.get('ll');
- const paramZ = url.searchParams.get('z');
+ getResults: async function(url) {
+ let isShort = false;
try {
+ if (url.pathname.match(/^\/maps\/-\//u)) {
+ isShort = true;
+ const xhr = await fetch(urlViaCorsProxy(url.toString()));
+ const dom = new DOMParser().parseFromString(xhr.response, 'text/html');
+ url = new URL(dom.querySelector('meta[property="og:image:secure_url"]').content);
+ }
+ const paramLl = url.searchParams.get('ll');
+ const paramZ = url.searchParams.get('z');
const [lon, lat] = paramLl.split(',').map(parseFloat);
const zoom = Math.round(parseFloat(paramZ));
return makeSearchResults(lat, lon, zoom, 'Yandex map view');
} catch (_) {
- return {error: L.Util.template(MESSAGE_LINK_MALFORMED, {name: 'Yandex'})};
+ return {
+ error: L.Util.template(isShort ? MESSAGE_SHORT_LINK_MALFORMED : MESSAGE_LINK_MALFORMED, {
+ name: 'Yandex',
+ }),
+ };
}
},
};
@@ -70,19 +88,15 @@ const GoogleMapsSimpleMapUrl = {
} else {
title = 'Google map view';
}
- try {
- const lat = parseFloat(viewMatch[1]);
- const lon = parseFloat(viewMatch[2]);
- let zoom = parseFloat(viewMatch[3]);
- // zoom for satellite images is expressed in meters
- if (viewMatch[4] === 'm') {
- zoom = Math.log2(149175296 / zoom * Math.cos(lat / 180 * Math.PI));
- }
- zoom = Math.round(zoom);
- return makeSearchResults(lat, lon, zoom, title);
- } catch (_) {
- return {error: L.Util.template(MESSAGE_LINK_MALFORMED, {name: 'Google'})};
+ const lat = parseFloat(viewMatch[1]);
+ const lon = parseFloat(viewMatch[2]);
+ let zoom = parseFloat(viewMatch[3]);
+ // zoom for satellite images is expressed in meters
+ if (viewMatch[4] === 'm') {
+ zoom = Math.log2((149175296 / zoom) * Math.cos((lat / 180) * Math.PI));
}
+ zoom = Math.round(zoom);
+ return makeSearchResults(lat, lon, zoom, title);
},
};
@@ -97,30 +111,42 @@ const GoogleMapsQueryUrl = {
getResults: function(url) {
const data = url.searchParams.get('q');
const m = data.match(/^(?:loc:)?([-\d.]+),([-\d.]+)$/u);
- try {
- const lat = parseFloat(m[1]);
- const lon = parseFloat(m[2]);
- return makeSearchResults(lat, lon, this.zoom, this.title);
- } catch (_) {
- return {error: L.Util.template(MESSAGE_LINK_MALFORMED, {name: 'Google'})};
- }
- }
+ const lat = parseFloat(m[1]);
+ const lon = parseFloat(m[2]);
+ return makeSearchResults(lat, lon, this.zoom, this.title);
+ },
};
const GoogleMapsUrl = {
subprocessors: [GoogleMapsSimpleMapUrl, GoogleMapsQueryUrl],
isOurUrl: function(url) {
- return Boolean(url.hostname.match(/\bgoogle\..+$/u) && url.pathname.match(/^\/maps(\/|$)/u));
+ return (url.hostname.match(/\bgoogle\./u) || url.hostname === 'goo.gl') && url.pathname.match(/^\/maps(\/|$)/u);
},
- getResults: function(url) {
+ getResults: async function(url) {
+ let isShort = false;
+ try {
+ if (url.hostname === 'goo.gl') {
+ isShort = true;
+ const xhr = await fetch(urlViaCorsProxy(url.toString()), {method: 'HEAD'});
+ url = new URL(xhr.responseURL);
+ }
+ } catch (e) {
+ // pass
+ }
for (let subprocessor of this.subprocessors) {
- if (subprocessor.isOurUrl(url)) {
- return subprocessor.getResults(url);
+ try {
+ if (subprocessor.isOurUrl(url)) {
+ return subprocessor.getResults(url);
+ }
+ } catch (e) {
+ // pass
}
}
- return {error: L.Util.template(MESSAGE_LINK_MALFORMED, {name: 'Google'})};
+ return {
+ error: L.Util.template(isShort ? MESSAGE_SHORT_LINK_MALFORMED : MESSAGE_LINK_MALFORMED, {name: 'Google'}),
+ };
},
};
@@ -129,14 +155,24 @@ const MapyCzUrl = {
return Boolean(url.hostname.match(/\bmapy\.cz$/u));
},
- getResults: function(url) {
+ getResults: async function(url) {
+ let isShort = false;
try {
+ if (url.pathname.match(/^\/s\//u)) {
+ isShort = true;
+ const xhr = await fetch(urlViaCorsProxy(url.toString()), {method: 'HEAD'});
+ url = new URL(xhr.responseURL);
+ }
const lon = parseFloat(url.searchParams.get('x'));
const lat = parseFloat(url.searchParams.get('y'));
const zoom = Math.round(parseFloat(url.searchParams.get('z')));
return makeSearchResults(lat, lon, zoom, 'Mapy.cz view');
} catch (_) {
- return {error: L.Util.template(MESSAGE_LINK_MALFORMED, {name: 'Mapy.cz'})};
+ return {
+ error: L.Util.template(isShort ? MESSAGE_SHORT_LINK_MALFORMED : MESSAGE_LINK_MALFORMED, {
+ name: 'Mapy.cz',
+ }),
+ };
}
},
};
diff --git a/test/test_search_links.js b/test/test_search_links.js
@@ -23,6 +23,28 @@ suite('LinksProvider - parsing valid links');
14,
],
['https://yandex.ru/maps/?ll=16.548629%2C49.219896&z=14', 'Yandex map view', {lat: 49.219896, lng: 16.548629}, 14],
+ [
+ 'https://yandex.ru/maps/?l=sat&ll=16.843527%2C49.363860&z=13',
+ 'Yandex map view',
+ {lat: 49.36386, lng: 16.843527},
+ 13,
+ ],
+ [
+ 'https://yandex.ru/maps/?l=sat%2Cskl&ll=16.843527%2C49.363860&z=13',
+ 'Yandex map view',
+ {lat: 49.36386, lng: 16.843527},
+ 13,
+ ],
+ [
+ 'https://static-maps.yandex.ru/1.x/?lang=ru_RU&size=520%2C440&l=sat%2Cskl&z=14&ll=16.548629%2C49.219896',
+ 'Yandex map view',
+ {lat: 49.219896, lng: 16.548629},
+ 14,
+ ],
+ ['https://yandex.ru/maps/-/CCQpqZXJCB', 'Yandex map view', {lat: 49.219896, lng: 16.548629}, 14],
+ ['https://yandex.ru/maps/-/CCQpqZdgpA', 'Yandex map view', {lat: 49.219896, lng: 16.548629}, 14],
+ ['https://yandex.ru/maps/-/CCQpqZhrsB', 'Yandex map view', {lat: 49.219896, lng: 16.548629}, 14],
+
['https://www.openstreetmap.org/#map=14/49.2199/16.5486', 'OpenStreetMap view', {lat: 49.2199, lng: 16.5486}, 14],
[
'https://en.mapy.cz/turisticka?x=16.5651083&y=49.2222502&z=14',
@@ -94,6 +116,22 @@ suite('LinksProvider - parsing valid links');
['https://nakarte.me/#m=11/49.44893/16.59897&l=O', 'Nakarte view', {lat: 49.44893, lng: 16.59897}, 11],
['https://nakarte.me/#l=O&m=11/49.44893/16.59897', 'Nakarte view', {lat: 49.44893, lng: 16.59897}, 11],
['https://example.com/#l=O&m=11/49.44893/16.59897', 'Nakarte view', {lat: 49.44893, lng: 16.59897}, 11],
+ ['https://en.mapy.cz/s/favepemeko', 'Mapy.cz view', {lat: 49.4113109, lng: 16.8975623}, 11],
+ ['https://en.mapy.cz/s/lucacunomo', 'Mapy.cz view', {lat: 49.4113109, lng: 16.8975623}, 11],
+ ['https://en.mapy.cz/s/mepevemazo', 'Mapy.cz view', {lat: 50.1592323, lng: 16.8245081}, 12],
+ ['https://goo.gl/maps/cJ8wwQi9oMYM9yiy6', 'Google map view', {lat: 49.0030846, lng: 15.2993434}, 14],
+ [
+ 'https://goo.gl/maps/ZvjVBY78HUP8HjQi6',
+ 'Google map - 561 69 Dolnà Morava',
+ {lat: 50.1568257, lng: 16.754047},
+ 12,
+ ],
+ [
+ 'https://goo.gl/maps/iMv4esLL1nwF9yns7',
+ 'Google map - 561 69 Dolnà Morava',
+ {lat: 50.1568257, lng: 16.754047},
+ 12,
+ ],
].forEach(function([query, expectedTitle, expectedCoordinates, expectedZoom]) {
test(`Parse link ${query}`, async function() {
assert.isTrue(links.isOurQuery(query));
@@ -117,13 +155,14 @@ suite('LinksProvider - parse invalid links');
['https://', 'Invalid link'],
['http://', 'Invalid link'],
['https://example.com', 'Unsupported link'],
- ['https://yandex.ru/maps/-/CCQlZLeFHA', 'Invalid coordinates in Yandex link'],
['https://yandex.ru/maps/', 'Invalid coordinates in Yandex link'],
['https://yandex.ru/maps/10509/brno/?ll=16.548629%2C149.219896&z=14', 'Invalid coordinates in Yandex link'],
- ['https://en.mapy.cz/s/kofosuhuda', 'Invalid coordinates in Mapy.cz link'],
+ [
+ 'https://static-maps.yandex.ru/1.x/?lang=ru_RU&size=520%2C440&l=sat%2Cskl&ll=16.548629%2C49.219896',
+ 'Invalid coordinates in Yandex link',
+ ],
['https://en.mapy.cz/turisticka?x=16.5651083&y=49.2222502&z=', 'Invalid coordinates in Mapy.cz link'],
['https://www.google.com/maps', 'Invalid coordinates in Google link'],
- ['https://goo.gl/maps/igLWhY3jFpifZhTk6', 'Unsupported link'],
['https://www.google.com/maps/@99.1906435,16.5429962,14z', 'Invalid coordinates in Google link'],
['https://www.google.com/maps/@49.1906435,190.5429962,14z', 'Invalid coordinates in Google link'],
['https://www.google.com/maps/@49.1906435,19.5429962,45z', 'Invalid coordinates in Google link'],
@@ -140,6 +179,9 @@ suite('LinksProvider - parse invalid links');
['https://nakarte.me/#l=O', 'Invalid coordinates in Nakarte link'],
['https://example.com/#l=O&m=11/49.44893/', 'Unsupported link'],
['https://example.com/#l=O&m=99/49.44893/52.5547', 'Unsupported link'],
+ ['https://en.mapy.cz/s/lucacunom', 'Broken Mapy.cz short link'],
+ ['https://goo.gl/maps/ZvjVBY78HUP8HjQi', 'Broken Google short link'],
+ // ['https://yandex.ru/maps/-/CCQpqZXJ', 'Broken Yandex short link'], // Yandex returns good result for broken link
].forEach(function([query, expectedError]) {
test(`Invalid link ${query}`, async function() {
assert.isTrue(links.isOurQuery(query));