feat: prefer svg region classes for map selection
This commit is contained in:
284
map-prototype.js
284
map-prototype.js
@@ -1,6 +1,223 @@
|
||||
const MAP_SVG_SOURCE = 'assets/world_time_zones.svg';
|
||||
const OFFSET_PRECISION = 30; // Minuten-Schrittweite
|
||||
|
||||
const CLASS_TO_TIMEZONE = {
|
||||
// Europe
|
||||
ad: 'Europe/Madrid',
|
||||
al: 'Europe/Athens',
|
||||
at: 'Europe/Vienna',
|
||||
ba: 'Europe/Bucharest',
|
||||
be: 'Europe/Amsterdam',
|
||||
bg: 'Europe/Sofia',
|
||||
ch: 'Europe/Zurich',
|
||||
cz: 'Europe/Berlin',
|
||||
de: 'Europe/Berlin',
|
||||
dk: 'Europe/Copenhagen',
|
||||
ee: 'Europe/Helsinki',
|
||||
es: 'Europe/Madrid',
|
||||
fi: 'Europe/Helsinki',
|
||||
fr: 'Europe/Paris',
|
||||
gb: 'Europe/London',
|
||||
gr: 'Europe/Athens',
|
||||
hr: 'Europe/Rome',
|
||||
hu: 'Europe/Vienna',
|
||||
ie: 'Europe/Dublin',
|
||||
is: 'Atlantic/Reykjavik',
|
||||
it: 'Europe/Rome',
|
||||
li: 'Europe/Zurich',
|
||||
lt: 'Europe/Riga',
|
||||
lu: 'Europe/Amsterdam',
|
||||
lv: 'Europe/Riga',
|
||||
mt: 'Europe/Rome',
|
||||
nl: 'Europe/Amsterdam',
|
||||
no: 'Europe/Oslo',
|
||||
pl: 'Europe/Warsaw',
|
||||
pt0: 'Europe/Lisbon',
|
||||
pt: 'Europe/Lisbon',
|
||||
'pt-1': 'Europe/Lisbon',
|
||||
ro: 'Europe/Bucharest',
|
||||
rs: 'Europe/Athens',
|
||||
se: 'Europe/Stockholm',
|
||||
si: 'Europe/Vienna',
|
||||
sk: 'Europe/Vienna',
|
||||
ua: 'Europe/Kyiv',
|
||||
ua2: 'Europe/Kyiv',
|
||||
ua3: 'Europe/Kyiv',
|
||||
va: 'Europe/Rome',
|
||||
|
||||
// Americas
|
||||
ar: 'America/Argentina/Buenos_Aires',
|
||||
bb: 'America/Barbados',
|
||||
bo: 'America/La_Paz',
|
||||
'br-4': 'America/Manaus',
|
||||
'br-3': 'America/Sao_Paulo',
|
||||
'br-2': 'America/Sao_Paulo',
|
||||
'ca-4': 'America/Halifax',
|
||||
'ca-5': 'America/Toronto',
|
||||
'ca-6': 'America/Winnipeg',
|
||||
'ca-7': 'America/Edmonton',
|
||||
'ca-8': 'America/Vancouver',
|
||||
'ca-330': 'America/St_Johns',
|
||||
'ca-4n': 'America/Halifax',
|
||||
'ca-5n': 'America/Toronto',
|
||||
'ca-6n': 'America/Winnipeg',
|
||||
'ca-7n': 'America/Edmonton',
|
||||
'ca-8n': 'America/Vancouver',
|
||||
'cl-3': 'America/Santiago',
|
||||
'cl-4': 'America/Santiago',
|
||||
co: 'America/Bogota',
|
||||
cu: 'America/Havana',
|
||||
'ec-5': 'America/Guayaquil',
|
||||
'ec-6': 'America/Guayaquil',
|
||||
gt: 'America/Guatemala',
|
||||
hn: 'America/Tegucigalpa',
|
||||
jm: 'America/Jamaica',
|
||||
'mx-5': 'America/Mexico_City',
|
||||
'mx-6': 'America/Mexico_City',
|
||||
'mx-7': 'America/Ciudad_Juarez',
|
||||
'mx-8': 'America/Tijuana',
|
||||
'mx-5b': 'America/Mexico_City',
|
||||
'mx-6b': 'America/Mexico_City',
|
||||
'mx-7b': 'America/Ciudad_Juarez',
|
||||
'mx-8b': 'America/Tijuana',
|
||||
ni: 'America/Managua',
|
||||
pa: 'America/Panama',
|
||||
pe: 'America/Lima',
|
||||
pr: 'America/Puerto_Rico',
|
||||
'us-5': 'America/New_York',
|
||||
'us-6': 'America/Chicago',
|
||||
'us-7': 'America/Denver',
|
||||
'us-8': 'America/Los_Angeles',
|
||||
'us-9': 'America/Anchorage',
|
||||
'us-10': 'Pacific/Honolulu',
|
||||
'us-10n': 'Pacific/Honolulu',
|
||||
'us-9n': 'America/Anchorage',
|
||||
'us-8n': 'America/Los_Angeles',
|
||||
'us-7n': 'America/Denver',
|
||||
'us-6n': 'America/Chicago',
|
||||
'us-5n': 'America/New_York',
|
||||
uy: 'America/Montevideo',
|
||||
ve: 'America/Caracas',
|
||||
|
||||
// Africa
|
||||
ao: 'Africa/Luanda',
|
||||
bf: 'Africa/Abidjan',
|
||||
bi: 'Africa/Maputo',
|
||||
bj: 'Africa/Lagos',
|
||||
bw: 'Africa/Johannesburg',
|
||||
cd1: 'Africa/Kinshasa',
|
||||
cd2: 'Africa/Lubumbashi',
|
||||
cf: 'Africa/Bangui',
|
||||
cg: 'Africa/Lagos',
|
||||
ci: 'Africa/Abidjan',
|
||||
cm: 'Africa/Douala',
|
||||
dz: 'Africa/Algiers',
|
||||
eg: 'Africa/Cairo',
|
||||
et: 'Africa/Addis_Ababa',
|
||||
gh: 'Africa/Accra',
|
||||
gm: 'Africa/Abidjan',
|
||||
gn: 'Africa/Conakry',
|
||||
gq: 'Africa/Lagos',
|
||||
ke: 'Africa/Nairobi',
|
||||
lr: 'Africa/Monrovia',
|
||||
ls: 'Africa/Johannesburg',
|
||||
ly: 'Africa/Tripoli',
|
||||
ma: 'Africa/Casablanca',
|
||||
mg: 'Africa/Nairobi',
|
||||
ml: 'Africa/Bamako',
|
||||
mr: 'Africa/Nouakchott',
|
||||
mu: 'Africa/Nairobi',
|
||||
mw: 'Africa/Maputo',
|
||||
mz: 'Africa/Maputo',
|
||||
na: 'Africa/Windhoek',
|
||||
ne: 'Africa/Lagos',
|
||||
ng: 'Africa/Lagos',
|
||||
rw: 'Africa/Maputo',
|
||||
sd: 'Africa/Khartoum',
|
||||
sl: 'Africa/Abidjan',
|
||||
sn: 'Africa/Dakar',
|
||||
tn: 'Africa/Tunis',
|
||||
tz: 'Africa/Nairobi',
|
||||
ug: 'Africa/Nairobi',
|
||||
za2: 'Africa/Johannesburg',
|
||||
za3: 'Africa/Johannesburg',
|
||||
zm: 'Africa/Maputo',
|
||||
zw: 'Africa/Maputo',
|
||||
|
||||
// Middle East & Asia
|
||||
ae: 'Asia/Dubai',
|
||||
af: 'Asia/Kabul',
|
||||
am: 'Asia/Yerevan',
|
||||
az: 'Asia/Baku',
|
||||
bh: 'Asia/Bahrain',
|
||||
cn: 'Asia/Shanghai',
|
||||
ge: 'Asia/Tbilisi',
|
||||
il: 'Asia/Jerusalem',
|
||||
in: 'Asia/Kolkata',
|
||||
iq: 'Asia/Baghdad',
|
||||
ir: 'Asia/Tehran',
|
||||
jo: 'Asia/Amman',
|
||||
jp: 'Asia/Tokyo',
|
||||
kg: 'Asia/Bishkek',
|
||||
kz: 'Asia/Almaty',
|
||||
kw: 'Asia/Kuwait',
|
||||
lb: 'Asia/Beirut',
|
||||
lk: 'Asia/Colombo',
|
||||
mm: 'Asia/Yangon',
|
||||
mn7: 'Asia/Irkutsk',
|
||||
mn8: 'Asia/Irkutsk',
|
||||
mv: 'Asia/Dubai',
|
||||
my: 'Asia/Kuala_Lumpur',
|
||||
np: 'Asia/Kathmandu',
|
||||
om: 'Asia/Dubai',
|
||||
ph: 'Asia/Manila',
|
||||
pk: 'Asia/Karachi',
|
||||
qa: 'Asia/Qatar',
|
||||
ru2: 'Europe/Moscow',
|
||||
ru3: 'Europe/Moscow',
|
||||
ru4: 'Europe/Samara',
|
||||
ru5: 'Asia/Yekaterinburg',
|
||||
ru6: 'Asia/Omsk',
|
||||
ru7: 'Asia/Krasnoyarsk',
|
||||
ru8: 'Asia/Irkutsk',
|
||||
ru9: 'Asia/Yakutsk',
|
||||
ru10: 'Asia/Vladivostok',
|
||||
ru11: 'Asia/Magadan',
|
||||
ru12: 'Asia/Kamchatka',
|
||||
sa: 'Asia/Riyadh',
|
||||
sg: 'Asia/Singapore',
|
||||
sy: 'Asia/Damascus',
|
||||
th: 'Asia/Bangkok',
|
||||
tj: 'Asia/Dushanbe',
|
||||
tm: 'Asia/Ashgabat',
|
||||
tr: 'Europe/Istanbul',
|
||||
uz: 'Asia/Tashkent',
|
||||
vn: 'Asia/Ho_Chi_Minh',
|
||||
ye: 'Asia/Aden',
|
||||
|
||||
// Oceania
|
||||
au8: 'Australia/Perth',
|
||||
au10: 'Australia/Sydney',
|
||||
au10n: 'Australia/Sydney',
|
||||
au1030: 'Australia/Sydney',
|
||||
au930: 'Australia/Perth',
|
||||
au930n: 'Australia/Perth',
|
||||
au845: 'Australia/Perth',
|
||||
fj: 'Pacific/Fiji',
|
||||
gu: 'Pacific/Port_Moresby',
|
||||
nc: 'Pacific/Noumea',
|
||||
nz12: 'Pacific/Auckland',
|
||||
nz1245: 'Pacific/Auckland',
|
||||
'pf-10': 'Pacific/Honolulu',
|
||||
'pf-9': 'Pacific/Honolulu',
|
||||
'pf-930': 'Pacific/Honolulu',
|
||||
to: 'Pacific/Auckland',
|
||||
ws: 'Pacific/Auckland',
|
||||
tk: 'Pacific/Auckland'
|
||||
};
|
||||
|
||||
const timezoneById = new Map(TIMEZONE_DATA.map((entry) => [entry.timeZone, entry]));
|
||||
|
||||
const mapContainer = document.getElementById('map-container');
|
||||
const overlayEl = document.getElementById('map-overlay');
|
||||
const tooltipEl = document.getElementById('map-tooltip');
|
||||
@@ -9,6 +226,39 @@ if (mapContainer) {
|
||||
loadMap();
|
||||
}
|
||||
|
||||
function getEntryForTimeZone(timeZoneId) {
|
||||
if (!timeZoneId) {
|
||||
return null;
|
||||
}
|
||||
if (timezoneById.has(timeZoneId)) {
|
||||
return timezoneById.get(timeZoneId);
|
||||
}
|
||||
return SORTED_TIMEZONE_DATA.find((entry) => entry.timeZone === timeZoneId) || null;
|
||||
}
|
||||
|
||||
function resolveEntryFromElement(element) {
|
||||
if (!element) {
|
||||
return null;
|
||||
}
|
||||
const candidate = element.closest('[class]');
|
||||
if (!candidate) {
|
||||
return null;
|
||||
}
|
||||
const classAttr = candidate.getAttribute('class');
|
||||
if (!classAttr) {
|
||||
return null;
|
||||
}
|
||||
const classes = classAttr.split(/\s+/);
|
||||
for (const cls of classes) {
|
||||
const timeZoneId = CLASS_TO_TIMEZONE[cls];
|
||||
const entry = getEntryForTimeZone(timeZoneId);
|
||||
if (entry) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function loadMap() {
|
||||
fetch(MAP_SVG_SOURCE)
|
||||
.then((response) => response.text())
|
||||
@@ -51,6 +301,13 @@ function attachMapListeners(svg) {
|
||||
const { x, y } = getRelativePoint(event);
|
||||
const lon = normalizeLongitude(x, svg.viewBox.baseVal.width);
|
||||
const lat = normalizeLatitude(y, svg.viewBox.baseVal.height);
|
||||
const classEntry = resolveEntryFromElement(event.target);
|
||||
if (classEntry) {
|
||||
const suggestion = buildSuggestion(classEntry);
|
||||
positionTooltip(event.clientX, event.clientY, `${suggestion.offsetLabel}\n${classEntry.name}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const offsetMinutes = approximateOffsetMinutes(lon);
|
||||
const candidate = findTimezoneByOffset(offsetMinutes);
|
||||
|
||||
@@ -70,20 +327,31 @@ function attachMapListeners(svg) {
|
||||
function handleClick(event) {
|
||||
const { x } = getRelativePoint(event);
|
||||
const lon = normalizeLongitude(x, svg.viewBox.baseVal.width);
|
||||
const offsetMinutes = approximateOffsetMinutes(lon);
|
||||
const candidate = findTimezoneByOffset(offsetMinutes);
|
||||
const classEntry = resolveEntryFromElement(event.target);
|
||||
let labelToShow;
|
||||
|
||||
if (candidate) {
|
||||
const suggestion = buildSuggestion(candidate);
|
||||
labelToShow = `${suggestion.offsetLabel}`;
|
||||
if (classEntry) {
|
||||
const suggestion = buildSuggestion(classEntry);
|
||||
labelToShow = suggestion.offsetLabel;
|
||||
if (typeof addSelection === 'function') {
|
||||
addSelection(candidate);
|
||||
addSelection(classEntry);
|
||||
renderSelections();
|
||||
}
|
||||
} else {
|
||||
const { offsetLabel } = describeOffset(offsetMinutes);
|
||||
labelToShow = offsetLabel;
|
||||
const offsetMinutes = approximateOffsetMinutes(lon);
|
||||
const candidate = findTimezoneByOffset(offsetMinutes);
|
||||
|
||||
if (candidate) {
|
||||
const suggestion = buildSuggestion(candidate);
|
||||
labelToShow = suggestion.offsetLabel;
|
||||
if (typeof addSelection === 'function') {
|
||||
addSelection(candidate);
|
||||
renderSelections();
|
||||
}
|
||||
} else {
|
||||
const { offsetLabel } = describeOffset(offsetMinutes);
|
||||
labelToShow = offsetLabel;
|
||||
}
|
||||
}
|
||||
|
||||
showOverlay(labelToShow);
|
||||
|
Reference in New Issue
Block a user