Compare commits

..

No commits in common. "e9386060c04147e05acf12cc9e3e1c9928ec09de" and "9287bb0d33bf6c7491bb2121325216e2d3930651" have entirely different histories.

4 changed files with 292 additions and 44 deletions

View file

@ -4,30 +4,26 @@
<meta charset="utf-8">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.2/css/all.min.css" integrity="sha512-1sCRPdkRXhBV2PBLUdRb4tMg1w2YPf37qatUFeS7zlBy7jJI8Lf4VHwWfZZfpXtYSLy85pkm9GaYVYMfw5BC1A==" crossorigin="anonymous">
<title>MusicThread Test</title>
<script>
// replace theme-song placeholder text when the musicthread response comes in
document.addEventListener('DOMContentLoaded', function() {
const themeSongContainer = document.getElementById('theme-song');
if (themeSongContainer) {
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === 'childList' && themeSongContainer.childNodes.length > 2) {
themeSongContainer.removeChild(themeSongContainer.firstChild);
observer.disconnect();
}
});
<script>
// retrieves latest link from a musicthread thread and displays it on the page
document.addEventListener("DOMContentLoaded", function() {
const musicthread = "https://musicthread.app/api/v0/thread/2aVjZUocjk96LELFbV5JvJjm14v";
const nowPlayingElement = document.getElementById("now-playing");
if (nowPlayingElement) {
fetch(musicthread)
.then(res => res.json())
.then(function(res){
let nowPlaying = res;
nowPlayingElement.innerHTML = "<a href='" + nowPlaying.page_url + "'>" + nowPlaying.title + "</a> by " + nowPlaying.artist;
});
}
});
observer.observe(themeSongContainer, { childList: true });
} else {
console.Warn('Theme song container not found');
}
});
</script>
</script>
</head>
<body>
<h1>Now Playing</h1>
<ul>
<li><span id="theme-song">Silence<script src="https://res.jbowdre.lol/js/theme-song.js?id=2aVjZUocjk96LELFbV5JvJjm14v&plain=true" defer></script></span><i class='fa-solid fa-headphones'></i></li>
<li><span id="now-playing"></span> <i class='fa-solid fa-headphones'></i></li>
</ul>
</body>
</html>

3
now.md
View file

@ -39,7 +39,7 @@
- [Truth of the Divine](https://openlibrary.org/works/OL24198736W/Truth_of_the_Divine) by Lindsay Ellis {book}
- [White Collar](https://www.imdb.com/title/tt1358522), [Canada's Drag Race: Canada vs the World](https://www.imdb.com/title/tt19357598/) {display}
- [The Last of Us Part I](https://store.steampowered.com/app/1888930/The_Last_of_Us_Part_I/) {gamepad}
- <span id="theme-song">Silence<script src="https://res.jbowdre.lol/js/theme-song.js?id=2aVjZUocjk96LELFbV5JvJjm14v&plain=true" defer></script></span> {headphones}
- <span id="now-playing">Silence</span> {headphones}
- [more recent faves](https://musicthread.app/thread/2aVjZUocjk96LELFbV5JvJjm14v) {music}
<br>
@ -63,4 +63,3 @@

View file

@ -4,7 +4,133 @@
<meta charset="utf-8">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.2/css/all.min.css" integrity="sha512-1sCRPdkRXhBV2PBLUdRb4tMg1w2YPf37qatUFeS7zlBy7jJI8Lf4VHwWfZZfpXtYSLy85pkm9GaYVYMfw5BC1A==" crossorigin="anonymous">
<title>Weather Test</title>
<script src="weather.js"></script>
<script>
// get weather data from pastebin
document.addEventListener("DOMContentLoaded", function() {
const hasWeather = document.getElementById('conditions');
if (hasWeather) {
const wx_endpoint = 'https://paste.jbowdre.lol/tempest.json/raw';
// get ready to calculate relative time
const units = {
year : 24 * 60 * 60 * 1000 * 365,
month : 24 * 60 * 60 * 1000 * 365/12,
day : 24 * 60 * 60 * 1000,
hour : 60 * 60 * 1000,
minute: 60 * 1000,
second: 1000
}
let rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' })
let getRelativeTime = (d1, d2 = new Date()) => {
let elapsed = d1 - d2
for (var u in units)
if (Math.abs(elapsed) > units[u] || u === 'second')
return rtf.format(Math.round(elapsed/units[u]), u)
}
// set up temperature and rain ranges
const tempRanges = [
{ upper: 32, label: 'cold' },
{ upper: 60, label: 'cool' },
{ upper: 80, label: 'warm' },
{ upper: Infinity, label: 'hot' }
];
const rainRanges = [
{ upper: 0.02, label: 'none' },
{ upper: 0.2, label: 'light' },
{ upper: 1.4, label: 'moderate' },
{ upper: Infinity, label: 'heavy' }
]
// maps for selecting icons
const CLASS_MAP_PRESS = {
'steady': 'fa-solid fa-arrow-right-long',
'rising': 'fa-solid fa-arrow-trend-up',
'falling': 'fa-solid fa-arrow-trend-down'
}
const CLASS_MAP_RAIN = {
'none': 'fa-solid fa-droplet-slash',
'light': 'fa-solid fa-glass-water-droplet',
'moderate': 'fa-solid fa-glass-water',
'heavy': 'fa-solid fa-bucket'
}
const CLASS_MAP_TEMP = {
'hot': 'fa-solid fa-thermometer-full',
'warm': 'fa-solid fa-thermometer-half',
'cool': 'fa-solid fa-thermometer-quarter',
'cold': 'fa-solid fa-thermometer-empty'
}
const CLASS_MAP_WX = {
'clear-day': 'fa-solid fa-sun',
'clear-night': 'fa-solid fa-moon',
'cloudy': 'fa-solid fa-cloud',
'foggy': 'fa-solid fa-cloud-showers-smog',
'partly-cloudy-day': 'fa-solid fa-cloud-sun',
'partly-cloudy-night': 'fa-solid fa-cloud-moon',
'possibly-rainy-day': 'fa-solid fa-cloud-sun-rain',
'possibly-rainy-night': 'fa-solid fa-cloud-moon-rain',
'possibly-sleet-day': 'fa-solid fa-cloud-meatball',
'possibly-sleet-night': 'fa-solid fa-cloud-moon-rain',
'possibly-snow-day': 'fa-solid fa-snowflake',
'possibly-snow-night': 'fa-solid fa-snowflake',
'possibly-thunderstorm-day': 'fa-solid fa-cloud-bolt',
'possibly-thunderstorm-night': 'fa-solid fa-cloud-bolt',
'rainy': 'fa-solid fa-cloud-showers-heavy',
'sleet': 'fa-solid fa-cloud-rain',
'snow': 'fa-solid fa-snowflake',
'thunderstorm': 'fa-solid fa-cloud-bolt',
'windy': 'fa-solid fa-wind',
}
// fetch data from pastebin
fetch(wx_endpoint)
.then(res => res.json())
.then(function(res){
// calculate age of last update
let updateTime = res.time;
updateTime = parseInt(updateTime);
updateTime = updateTime*1000;
updateTime = new Date(updateTime);
let updateAge = getRelativeTime(updateTime);
// parse data
let conditions = (res.conditions).toLowerCase();
let tempDiff = Math.abs(res.temperature - res.feels_like);
let temp = `${res.temperature}°f (${(((res.temperature - 32) * 5) / 9).toFixed(1)}°c)`;
if (tempDiff >= 5) {
temp += `, feels ${res.feels_like}°f (${(((res.feels_like - 32) *5) / 9).toFixed(1)}°c)`;
}
let tempLabel = (tempRanges.find(range => res.feels_like < range.upper)).label;
let humidity = `${res.humidity}% humidity`;
let wind = `${res.wind_gust}mph (${(res.wind_gust*1.609344).toFixed(1)}kph) from ${(res.wind_direction).toLowerCase()}`;
let rainLabel = (rainRanges.find(range => res.rain_today < range.upper)).label;
let rainToday;
if (res.rain_today === 0) {
rainToday = 'no rain today';
} else {
rainToday = `${res.rain_today}" rain today`;
}
let pressureTrend = res.pressure_trend;
let pressure = `${res.pressure}inhg and ${pressureTrend}`;
let icon = res.icon;
// display data
document.getElementById('time').innerHTML = updateAge;
document.getElementById('conditions').innerHTML = conditions;
document.getElementById('temp').innerHTML = temp;
document.getElementById('humidity').innerHTML = humidity;
document.getElementById('wind').innerHTML = wind;
document.getElementById('rainToday').innerHTML = rainToday;
document.getElementById('pressure').innerHTML = pressure;
// update icons
document.getElementsByClassName('fa-cloud-sun-rain')[0].classList = CLASS_MAP_WX[icon];
document.getElementsByClassName('fa-temperature-half')[0].classList = CLASS_MAP_TEMP[tempLabel];
document.getElementsByClassName('fa-droplet-slash')[0].classList = CLASS_MAP_RAIN[rainLabel];
document.getElementsByClassName('fa-arrow-right-long')[0].classList = CLASS_MAP_PRESS[pressureTrend];
});
}
});
</script>
</head>
<body>
<h1>Local Weather</h1>
@ -16,6 +142,5 @@
<li>Precipitation: <i class='fa-solid fa-droplet-slash'></i><span id="rainToday">Loading...</span></li>
<li>Pressure: <i class='fa-solid fa-arrow-right-long'></i><span id="pressure">Loading...</span></li>
<li><i>Last Update: <span id="time">Loading...</span></i></li>
</ul>
</body>
</html>

View file

@ -3,32 +3,160 @@
<!-- cabin analytics -->
<script async defer src="https://cabin.jbowdre.lol/hello.js"></script>
<!-- typo animation -->
<script src="https://res.jbowdre.lol/js/typo.js" defer></script>
<script src="https://cdn.jbowdre.lol/typo.js"></script>
<script>
// implement typo
document.addEventListener('DOMContentLoaded', function() {
let typoElement = document.getElementById('typo');
if (typoElement) {
let typoText = "I write code to make imaginary computers run code written by less imaginary developers";
typo(typoElement, typoText);
let element = document.getElementById('typo');
let text = "I write code to make imaginary computers run code written by less imaginary developers";
typo(element, text);
});
</script>
<script>
// get weather data from pastebin
document.addEventListener("DOMContentLoaded", function() {
const hasWeather = document.getElementById('conditions');
if (hasWeather) {
const wx_endpoint = 'https://paste.jbowdre.lol/tempest.json/raw';
// get ready to calculate relative time
const units = {
year : 24 * 60 * 60 * 1000 * 365,
month : 24 * 60 * 60 * 1000 * 365/12,
day : 24 * 60 * 60 * 1000,
hour : 60 * 60 * 1000,
minute: 60 * 1000,
second: 1000
}
let rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' })
let getRelativeTime = (d1, d2 = new Date()) => {
let elapsed = d1 - d2
for (var u in units)
if (Math.abs(elapsed) > units[u] || u === 'second')
return rtf.format(Math.round(elapsed/units[u]), u)
}
// set up temperature and rain ranges
const tempRanges = [
{ upper: 32, label: 'cold' },
{ upper: 60, label: 'cool' },
{ upper: 80, label: 'warm' },
{ upper: Infinity, label: 'hot' }
];
const rainRanges = [
{ upper: 0.02, label: 'none' },
{ upper: 0.2, label: 'light' },
{ upper: 1.4, label: 'moderate' },
{ upper: Infinity, label: 'heavy' }
]
// maps for selecting icons
const CLASS_MAP_PRESS = {
'steady': 'fa-solid fa-arrow-right-long',
'rising': 'fa-solid fa-arrow-trend-up',
'falling': 'fa-solid fa-arrow-trend-down'
}
const CLASS_MAP_RAIN = {
'none': 'fa-solid fa-droplet-slash',
'light': 'fa-solid fa-glass-water-droplet',
'moderate': 'fa-solid fa-glass-water',
'heavy': 'fa-solid fa-bucket'
}
const CLASS_MAP_TEMP = {
'hot': 'fa-solid fa-thermometer-full',
'warm': 'fa-solid fa-thermometer-half',
'cool': 'fa-solid fa-thermometer-quarter',
'cold': 'fa-solid fa-thermometer-empty'
}
const CLASS_MAP_WX = {
'clear-day': 'fa-solid fa-sun',
'clear-night': 'fa-solid fa-moon',
'cloudy': 'fa-solid fa-cloud',
'foggy': 'fa-solid fa-cloud-showers-smog',
'partly-cloudy-day': 'fa-solid fa-cloud-sun',
'partly-cloudy-night': 'fa-solid fa-cloud-moon',
'possibly-rainy-day': 'fa-solid fa-cloud-sun-rain',
'possibly-rainy-night': 'fa-solid fa-cloud-moon-rain',
'possibly-sleet-day': 'fa-solid fa-cloud-meatball',
'possibly-sleet-night': 'fa-solid fa-cloud-moon-rain',
'possibly-snow-day': 'fa-solid fa-snowflake',
'possibly-snow-night': 'fa-solid fa-snowflake',
'possibly-thunderstorm-day': 'fa-solid fa-cloud-bolt',
'possibly-thunderstorm-night': 'fa-solid fa-cloud-bolt',
'rainy': 'fa-solid fa-cloud-showers-heavy',
'sleet': 'fa-solid fa-cloud-rain',
'snow': 'fa-solid fa-snowflake',
'thunderstorm': 'fa-solid fa-cloud-bolt',
'windy': 'fa-solid fa-wind',
}
// fetch data from pastebin
fetch(wx_endpoint)
.then(res => res.json())
.then(function(res){
// calculate age of last update
let updateTime = res.time;
updateTime = parseInt(updateTime);
updateTime = updateTime*1000;
updateTime = new Date(updateTime);
let updateAge = getRelativeTime(updateTime);
// parse data
let conditions = (res.conditions).toLowerCase();
let tempDiff = Math.abs(res.temperature - res.feels_like);
let temp = `${res.temperature}°f (${(((res.temperature - 32) * 5) / 9).toFixed(1)}°c)`;
if (tempDiff >= 5) {
temp += `, feels ${res.feels_like}°f (${(((res.feels_like - 32) *5) / 9).toFixed(1)}°c)`;
}
let tempLabel = (tempRanges.find(range => res.feels_like < range.upper)).label;
let humidity = `${res.humidity}% humidity`;
let wind = `${res.wind_gust}mph (${(res.wind_gust*1.609344).toFixed(1)}kph) from ${(res.wind_direction).toLowerCase()}`;
let rainLabel = (rainRanges.find(range => res.rain_today < range.upper)).label;
let rainToday;
if (res.rain_today === 0) {
rainToday = 'no rain today';
} else {
rainToday = `${res.rain_today}" rain today`;
}
let pressureTrend = res.pressure_trend;
let pressure = `${res.pressure}inhg and ${pressureTrend}`;
let icon = res.icon;
// display data
document.getElementById('time').innerHTML = updateAge;
document.getElementById('conditions').innerHTML = conditions;
document.getElementById('temp').innerHTML = temp;
document.getElementById('humidity').innerHTML = humidity;
document.getElementById('wind').innerHTML = wind;
document.getElementById('rainToday').innerHTML = rainToday;
document.getElementById('pressure').innerHTML = pressure;
// update icons
document.getElementsByClassName('fa-cloud-sun-rain')[0].classList = CLASS_MAP_WX[icon];
document.getElementsByClassName('fa-temperature-half')[0].classList = CLASS_MAP_TEMP[tempLabel];
document.getElementsByClassName('fa-droplet-slash')[0].classList = CLASS_MAP_RAIN[rainLabel];
document.getElementsByClassName('fa-arrow-right-long')[0].classList = CLASS_MAP_PRESS[pressureTrend];
});
}
});
</script>
<script src="https://res.jbowdre.lol/js/weather.js" defer></script>
<script>
// replace theme-song placeholder text when the musicthread response comes in
document.addEventListener('DOMContentLoaded', function() {
const themeSongContainer = document.getElementById('theme-song');
if (themeSongContainer) {
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === 'childList' && themeSongContainer.childNodes.length > 2) {
themeSongContainer.removeChild(themeSongContainer.firstChild);
observer.disconnect();
}
});
// retrieves latest link from a musicthread thread and displays it on the page
document.addEventListener("DOMContentLoaded", function() {
const musicthread = 'https://musicthread.app/api/v0/thread/2aVjZUocjk96LELFbV5JvJjm14v';
const nowPlayingElement = document.getElementById('now-playing');
if (nowPlayingElement) {
fetch(musicthread)
.then(res => res.json())
.then(function(res){
let nowPlaying = res.links[0];
nowPlayingElement.innerHTML = "<a href='" + nowPlaying.page_url + "'>" + nowPlaying.title + "</a> by " + nowPlaying.artist;
});
observer.observe(themeSongContainer, { childList: true });
}
});
}
});
</script>