vue-weather-widget
Version:
Weather forecast widget for Vuejs using DarkSky api
191 lines (171 loc) • 5.24 kB
JavaScript
import jsonp from "jsonp";
const IP_CACHE = "vww__cache_ip";
const IP_LOCATION_CACHE = "vww__cache_ip_location";
const GEOCODE_CACHE = "vww__cache_geocode";
const ICON_MAPPINGS = {
"clear-day": ["01d"],
"clear-night": ["01n"],
cloudy: ["03d", "03n"],
fog: ["50d", "50n"],
"partly-cloudy-day": ["02d", "04d"],
"partly-cloudy-night": ["02n", "04n"],
rain: ["09d", "09n", "10d", "10n", "11d", "11n"],
sleet: ["13d", "13n"],
snow: ["13d", "13n"],
wind: ["50d", "50n"],
};
const UNIT_MAPPINGS = {
auto: "standard",
us: "imperial",
uk: "metric",
};
const utils = {
lookupIP: () => {
let cache = localStorage[IP_CACHE] || "{}";
cache = JSON.parse(cache);
if (cache.ip) {
return Promise.resolve(cache);
}
return fetch("https://www.cloudflare.com/cdn-cgi/trace")
.then((resp) => resp.text())
.then((text) => {
return text
.split("\n")
.map((l) => l.split("="))
.filter((x) => x.length == 2)
.reduce((o, x) => {
o[x[0].trim()] = x[1].trim();
return o;
}, {});
})
.then((data) => {
localStorage[IP_CACHE] = JSON.stringify(data);
return data;
});
},
fetchLocationByIP: (apiKey, ip) => {
if (!ip) {
return utils.lookupIP().then((data) => {
return utils.fetchLocationByIP(apiKey, data["ip"]);
});
}
let cache = localStorage[IP_LOCATION_CACHE] || "{}";
cache = JSON.parse(cache);
if (cache[ip]) {
return cache[ip];
}
apiKey = apiKey || "f8n4kqe8pv4kii";
return fetch(`https://api.ipregistry.co/${ip}?key=${apiKey}`)
.then((resp) => resp.json())
.then((result) => {
cache[ip] = result.location || {};
localStorage[IP_LOCATION_CACHE] = JSON.stringify(cache);
return cache[ip];
});
// latitude, longitude, city, country.name
},
geocode: (apiKey, query, reversed = false) => {
let cache = localStorage[GEOCODE_CACHE] || "{}";
cache = JSON.parse(cache);
if (cache[query]) {
return Promise.resolve(cache[query]);
}
apiKey = apiKey || "c3bb8aa0a56b21122dea6a2a8ada70c8";
const apiType = reversed ? "reverse" : "forward";
return fetch(`//api.positionstack.com/v1/${apiType}?access_key=${apiKey}&query=${query}`)
.then((resp) => resp.json())
.then((result) => {
if (result.error) {
throw new Error("(api.positionstack.com) " + result.error.message);
}
cache[query] = result.data[0];
localStorage[GEOCODE_CACHE] = JSON.stringify(cache);
return cache[query];
});
// latitude, longitude, region, country
},
reverseGeocode: (apiKey, lat, lng) => {
return utils.geocode(apiKey, `${lat},${lng}`, true);
},
fetchWeather: (opts) => {
opts = opts || {};
opts.units = opts.units || "us";
opts.language = opts.language || "en";
if (!opts.lat || !opts.lng) {
throw new Error("Geolocation is required");
}
// return fetchJsonp(
// `https://api.darksky.net/forecast/${opts.apiKey}` +
// `/${opts.lat},${opts.lng}` +
// `?units=${opts.units}&lang=${opts.language}`
// ).then((resp) => resp.json());
return new Promise((resolve, reject) => {
jsonp(
`https://api.darksky.net/forecast/${opts.apiKey}` +
`/${opts.lat},${opts.lng}` +
`?units=${opts.units}&lang=${opts.language}`,
(err, data) => {
if (err) reject(err);
else resolve(data);
}
);
});
},
fetchOWMWeather: (opts = {}) => {
opts.units = opts.units || "auto";
opts.version = opts.version || "3.0";
opts.language = opts.language || "en";
if (!opts.lat || !opts.lng) {
throw new Error("Geolocation is required");
}
const units = UNIT_MAPPINGS[opts.units] || "standard";
return fetch(
`https://api.openweathermap.org/data/${opts.version}/onecall?appid=${opts.apiKey}` +
`&lat=${opts.lat}` +
`&lon=${opts.lng}` +
`&units=${units}` +
`&lang=${opts.language}`
)
.then((resp) => resp.json())
.then(utils.mapData);
},
mapData: (data) => {
const { current } = data;
const { weather } = current;
const [currentWeather] = weather;
const { description, icon } = currentWeather;
const iconName = utils.mapIcon(icon);
return {
currently: Object.assign({}, current, {
icon: iconName,
temperature: current.temp,
summary: description,
windSpeed: current.wind_speed,
windBearing: current.wind_deg,
}),
daily: {
data: data.daily.map((day) => {
return {
temperatureMax: day.temp.max,
temperatureMin: day.temp.min,
time: day.dt,
icon: utils.mapIcon(day.weather[0].icon),
};
}),
},
hourly: {
data: data.hourly.map((hour) => {
return {
temperature: hour.temp,
};
}),
},
};
},
mapIcon: (code) => {
return Object.keys(ICON_MAPPINGS).find((key) => {
return ICON_MAPPINGS[key].includes(code);
});
},
};
export default utils;