create bug.tools
BIN
background.mp4
Normal file
BIN
fonts/BRAILLE1.ttf
Normal file
BIN
fonts/FORCEDSQUARE.ttf
Normal file
BIN
fonts/SFTelegraphicLight.ttf
Normal file
BIN
fonts/nitrods-font.otf
Normal file
BIN
fonts/nitrods-font.ttf
Normal file
BIN
fonts/spaceage.ttf
Normal file
BIN
fonts/war-games.ttf
Normal file
BIN
images/ascii-lab.gif
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
images/ascii_cat.gif
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
images/ascii_cat_sleep.gif
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
images/dotloader.gif
Normal file
|
After Width: | Height: | Size: 563 KiB |
BIN
images/dsonlineicon.png
Normal file
|
After Width: | Height: | Size: 418 B |
BIN
images/loadercircle.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
images/loadingicon.gif
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
images/nav.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
images/navcircle.png
Normal file
|
After Width: | Height: | Size: 286 B |
BIN
images/navcircleblue.png
Normal file
|
After Width: | Height: | Size: 467 B |
BIN
images/pitcochatmenu.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
images/snowflake.gif
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
images/snowflake/frame001.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
images/snowflake/frame002.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
images/snowflake/frame003.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
images/snowflake/frame004.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
images/snowflake/frame005.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
images/snowflake/frame006.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
images/snowflake/frame007.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
images/snowflake/frame008.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
images/snowflake/frame009.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
images/snowflake/frame010.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
images/snowflake/frame011.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
images/snowflake/frame012.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
images/snowflake/frame013.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
images/snowflake/frame014.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
images/snowflake/frame015.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
images/snowflake/frame016.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
images/snowflake/frame017.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
images/snowflake/frame018.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
images/snowflake/frame019.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
images/snowflake/frame020.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
images/snowflake/frame021.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
images/snowflake/frame022.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
images/snowflake/frame023.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
images/xbutton.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
281
index.html
Normal file
@@ -0,0 +1,281 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>bug.tools</title>
|
||||
<link id="dynamic-favicon" rel="icon" href="./images/snowflake/frame001.png" type="image/png">
|
||||
<link href="style.css" rel="stylesheet" type="text/css" media="all">
|
||||
<link rel="icon" type="image/x-icon" href="images/favicon.ico">
|
||||
<script src="js/onLoad.js" defer></script>
|
||||
<script src="js/switchPage.js" defer></script>
|
||||
<script src="js/updates.js" defer></script>
|
||||
<script src="js/player.js" defer></script>
|
||||
<script src="js/counter.js" defer></script>
|
||||
<script src="js/favicon.js" defer></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="row">
|
||||
<div class="column left">
|
||||
<div class="navbar">
|
||||
<img src="images/loadercircle.png" id="loadercircle">
|
||||
<div class="navbar-title">bug</div>
|
||||
<div class="navbar-title-2">.tools</div>
|
||||
<table id="navtable">
|
||||
<tr>
|
||||
<td id="navhover"><a href="#" onclick="switchPage('home'); return false;">HOME</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="navhover"><a href="#" onclick="switchPage('links'); return false;">LINKS</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="navhover"><a href="#" onclick="switchPage('contact'); return false;">CONTACT</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="navhover"><a href="#" onclick="switchPage('sitemap'); return false;">SITEMAP</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="navhover"><a href="gallery.html">GALLERY</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="navhover"><a href="#"><!-- Empty --></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="vidbutton">
|
||||
<button id="vidBtn" onclick="PlayPauseVideo()">TURN OFF</button>
|
||||
<p>moving background</p>
|
||||
</div>
|
||||
|
||||
<div class="small-content-mood">
|
||||
<div class="small-content-toolbar">
|
||||
<span data-text="toolbar text">STATUS</span>
|
||||
<img src="images/xbutton.png" id="small-xbutton-right">
|
||||
</div>
|
||||
|
||||
<div class="small-content-inner">
|
||||
<span class="status-text" style="margin-top:20px;">
|
||||
<div id="status" class="fade-in visible">
|
||||
<div id="status-username"><a href="https://bug.tools/" target="_blank">bug</a> ❄️</div>
|
||||
<div id="status-content">working on something...</div>
|
||||
<div id="status-date">// 3 days ago</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="small-content-mood">
|
||||
<div class="small-content-toolbar">
|
||||
<span data-text="toolbar text">STATS</span>
|
||||
<img src="images/xbutton.png" id="small-xbutton-right">
|
||||
</div>
|
||||
|
||||
<div class="small-content-inner"
|
||||
style="height: 30px; padding-left:0px; padding-right:0px; padding-bottom:0px; margin-top:40px;">
|
||||
<span style="top:-22px; left:10px; font-family: 'Aldrich', sans-serif;">
|
||||
your digital ID number<br>
|
||||
</span>
|
||||
<span
|
||||
style="height:25px; margin-left:auto; margin-right:auto; margin-top:-8px; margin-bottom:5px;text-align: center;">
|
||||
<span id="counter" style="font-variant-numeric: tabular-nums;">0000000</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column middle">
|
||||
<div class="content">
|
||||
<div class="content-toolbar">
|
||||
<span data-text="welcome" id="toolbar-title">welcome</span>
|
||||
<img src="images/xbutton.png" id="xbutton">
|
||||
</div>
|
||||
|
||||
<div class="content-inner">
|
||||
<div id="page-content" class="fade-in"></div>
|
||||
|
||||
<template id="page-home">
|
||||
<h1>Where am I?</h1>
|
||||
<span>
|
||||
Welcome to my corner of the web. I'm bug, a self-taught programmer.
|
||||
This site is my personal space where I collect, create, and share
|
||||
whatever catches my interest.
|
||||
</span>
|
||||
<span class="blink_text" style="color: #93bdec;">
|
||||
this website is meant to be viewed on a computer.
|
||||
still under construction, thank you for your patience.
|
||||
</span>
|
||||
|
||||
<h1 style="margin-top:-10px;">What lies here?
|
||||
<img src="images/ascii_cat.gif"
|
||||
style="mix-blend-mode: normal; opacity: 0.5; display:inline; margin-left:90px;">
|
||||
</h1>
|
||||
<span>
|
||||
I run <a href="https://nixos.org">NixOS</a> on everything, desktop, laptop, and my home server that this site is hosted on.
|
||||
You can find my <a href="https://git.bug.tools/bug/nix/" target="_blank">nix configuration</a> on my git instance.
|
||||
<a href="https://www.lua.org">Lua</a> is my favorite
|
||||
programming language, though I dabble in plenty of others. I'm a strong believer in
|
||||
privacy and self-hosting; if there's a service I can run myself, I probably do.
|
||||
I also have a deep appreciation for digital archival and preserving things that
|
||||
might otherwise disappear from the internet.
|
||||
The <a href="https://git.bug.tools/bug/bug.tools/" target="_blank">source code for this website</a> is also available if you're curious.
|
||||
</span>
|
||||
|
||||
<h1 style="margin-top:-10px;">A little about me.
|
||||
<img src="images/ascii_cat_sleep.gif"
|
||||
style="mix-blend-mode: normal; opacity: 0.5; display:inline; margin-left:90px;">
|
||||
</h1>
|
||||
<span>
|
||||
When I'm not writing code, I'm usually listening to music or playing games. My taste
|
||||
in music spans metal, DnB, and a lot in between, some of my favorite artists are
|
||||
<a href="https://files.bug.tools/music/Pendulum/">Pendulum</a>, <a href="https://files.bug.tools/music/The%20Qemists/">The Qemists</a>, <a href="https://files.bug.tools/music/Linkin%20Park/">Linkin Park</a>, <a href="https://files.bug.tools/music/GUNSHIP/">GUNSHIP</a>.
|
||||
I also love playing <a href="https://store.steampowered.com/app/2073850/THE_FINALS/">THE FINALS</a> and <a href="https://store.steampowered.com/app/2694490/Path_of_Exile_2/">Path of Exile 2</a>, and I'm always down for a good MMORPG.
|
||||
Feel free to check out the music player on this page or explore the rest of
|
||||
the site, there's always something being worked on.
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template id="page-contact">
|
||||
<h1>Traces of me on the web</h1>
|
||||
<span>
|
||||
<img src="images/navcircleblue.png" style="margin-top:5px; margin-left:15px;"> <a href="mailto:contact@bug.tools" target="_blank">Email</a> (my contact email)<br>
|
||||
<img src="images/navcircleblue.png" style="margin-top:10px; margin-left:15px;"> <a href="https://git.bug.tools/bug" target="_blank">Gitea</a> (my personal
|
||||
Gitea instance)<br>
|
||||
<img src="images/navcircleblue.png" style="margin-top:10px; margin-left:15px;"> <a href="https://github.com/4DBug" target="_blank">GitHub</a> (my
|
||||
GitHub account)<br>
|
||||
<img src="images/navcircleblue.png" style="margin-top:10px; margin-left:15px;"> <a href="https://discordapp.com/users/1475869049526292534" target="_blank">Discord</a> (my discord profile)<br>
|
||||
<img src="images/navcircleblue.png" style="margin-top:10px; margin-left:15px;"> <a href="https://steamcommunity.com/id/0zBug/" target="_blank">Steam</a> (where I play games)<br>
|
||||
<img src="images/navcircleblue.png" style="margin-top:10px; margin-left:15px;"> <a href="https://www.roblox.com/users/2520646240/profile" target="_blank">Roblox</a> (where I waste my time)<br>
|
||||
</span>
|
||||
<h1>Contact me ?</h1>
|
||||
<span>
|
||||
Feel free to send me a message on any of my social medias, I do my best to respond to
|
||||
everyone.<br>
|
||||
</span>
|
||||
|
||||
<span class="ascii-art" style="width:100%;">
|
||||
<img src="images/ascii-lab.gif" alt="ascii-lab">
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template id="page-links">
|
||||
<h1>Links</h1>
|
||||
<span>The links page still needs to be implemented.</span>
|
||||
</template>
|
||||
|
||||
<template id="page-sitemap">
|
||||
<span style="font-family: 'Source Code Pro', monospace;">
|
||||
<b style="font-weight: 600;">.bug.tools</b><br>
|
||||
│<br>
|
||||
├──<b style="font-weight: 600;"><a href="https://bug.tools/">@.</a></b> _the homepage (you are here) <br>
|
||||
│ ├── <b style="font-weight: 600;">links</b> _cool or useful links<br>
|
||||
│ ├── <b style="font-weight: 600;">contact</b> _how to contact me<br>
|
||||
│ ├── <b style="font-weight: 600;">sitemap</b> _map of all pages<br>
|
||||
│ └── <b style="font-weight: 600;">gallery</b> _gallery<br>
|
||||
│<br>
|
||||
├──<b style="font-weight: 600;"><a href="https://tv.bug.tools/">tv.</a></b> _movie and television streaming <br>
|
||||
│ ├── <b style="font-weight: 600;">search</b> _search for entertainment<br>
|
||||
│ └── <b style="font-weight: 600;">watch</b> _watch movies and tv shows<br>
|
||||
│<br>
|
||||
├──<b style="font-weight: 600;"><a href="https://fm.bug.tools/">fm.</a></b> _music streaming site<br>
|
||||
│<br>
|
||||
├──<b style="font-weight: 600;"><a href="https://git.bug.tools/">git.</a></b> _gitea instance<br>
|
||||
│ └── <b style="font-weight: 600;"><a href="https://git.bug.tools/bug">bug</a></b> _my gitea account<br>
|
||||
│<br>
|
||||
├──<b style="font-weight: 600;"><a href="https://files.bug.tools/">files.</a></b> _files storage<br>
|
||||
│ ├── <b style="font-weight: 600;">music</b> _my music collection<br>
|
||||
│ ├── <b style="font-weight: 600;">raknet</b> _raknet documentation<br>
|
||||
│ └── <b style="font-weight: 600;">media</b> _various media<br>
|
||||
│<br>
|
||||
├──<b style="font-weight: 600;">project.</b> _generic project<br>
|
||||
│ ├── <b style="font-weight: 600;">docs</b> _documentation<br>
|
||||
│ ├── <b style="font-weight: 600;">src</b> _source files<br>
|
||||
│ ├── <b style="font-weight: 600;">assets</b> _static assets<br>
|
||||
│ ├── <b style="font-weight: 600;">tests</b> _test files<br>
|
||||
│ └── <b style="font-weight: 600;">misc</b> _other files<br>
|
||||
│<br>
|
||||
├── <b style="font-weight: 600;"><a href="https://gallery.bug.tools/">gallery.</a></b> _gallery of random images<br>
|
||||
│<br>
|
||||
└── <b style="font-weight: 600;"><a href="https://monitor.bug.tools/">monitor.</a></b> _status of my homeserver<br>
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
<div class="content-footer"><span id="factDisplay"></span></div>
|
||||
</div>
|
||||
|
||||
<div id="middle-column-img">
|
||||
<img src="images/dotloader.gif" style="mix-blend-mode: normal; width:500px; opacity: 0.3;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column right">
|
||||
<div class="small-content-updates"
|
||||
style="position: relative; top: 0px; left: 0px; width: 285px; height: auto;">
|
||||
<div class="small-content-toolbar">
|
||||
<span data-text="toolbar text">UPDATES</span>
|
||||
<img src="images/xbutton.png" id="small-xbutton-right">
|
||||
</div>
|
||||
|
||||
<div id="small-content-pictochat">
|
||||
<div id="pictochat-content">
|
||||
<div class="picto"></div>
|
||||
<img src="images/pitcochatmenu.png"
|
||||
style="border-radius: 0 0 10px 10px; position:relative; top: 0px; left:1px; width:257px; image-rendering: pixelated;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="small-content-updates"
|
||||
style="position:relative; top:15px; left:0; width:285px; height:auto; margin-bottom:25px;">
|
||||
<div class="small-content-toolbar">
|
||||
<span data-text="toolbar text">MUSIC</span>
|
||||
<img src="images/xbutton.png" id="small-xbutton-right">
|
||||
</div>
|
||||
|
||||
<div id="small-content-todo" class="fade-in">
|
||||
<div id="music-player">
|
||||
|
||||
<div id="mp-info">
|
||||
<div id="mp-title">no track selected</div>
|
||||
<div id="mp-artist">---</div>
|
||||
</div>
|
||||
|
||||
<div id="mp-progress-bg">
|
||||
<div id="mp-progress-fill"></div>
|
||||
<input type="range" id="mp-seek" min="0" max="100" value="0" step="0.1">
|
||||
</div>
|
||||
<div id="mp-time">
|
||||
<span id="mp-current">0:00</span>
|
||||
<span id="mp-duration">0:00</span>
|
||||
</div>
|
||||
|
||||
<div id="mp-controls">
|
||||
<button class="mp-btn mp-btn-sm" id="mp-loop" title="Loop">↻</button>
|
||||
<button class="mp-btn" id="mp-prev">◀◀</button>
|
||||
<button class="mp-btn mp-btn-play" id="mp-play">▶</button>
|
||||
<button class="mp-btn" id="mp-next">▶▶</button>
|
||||
<button class="mp-btn mp-btn-sm" id="mp-shuffle" title="Shuffle">⇄</button>
|
||||
</div>
|
||||
|
||||
<div id="mp-vol-row">
|
||||
<span id="mp-vol-icon">♬</span>
|
||||
<input type="range" id="mp-volume" min="0" max="1" step="0.01" value="0.7">
|
||||
</div>
|
||||
|
||||
<ul id="mp-playlist"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="bgVideoContainer">
|
||||
<video autoplay muted loop id="bgVideo" style="pointer-events: none">
|
||||
<source src="background.mp4" type="video/mp4" style="pointer-events: none">
|
||||
</video>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
21
js/counter.js
Normal file
@@ -0,0 +1,21 @@
|
||||
(function () {
|
||||
const COUNTER_DURATION = 2000;
|
||||
|
||||
fetch("https://counter.bug-dev-mail.workers.dev")
|
||||
.then(r => r.text())
|
||||
.then(count => {
|
||||
const target = parseInt(count, 10);
|
||||
const el = document.getElementById("counter");
|
||||
const start = performance.now();
|
||||
|
||||
function tick(now) {
|
||||
const t = Math.min((now - start) / COUNTER_DURATION, 1);
|
||||
const eased = 1 - Math.pow(1 - t, 3);
|
||||
const current = Math.round(eased * target);
|
||||
el.textContent = String(current).padStart(7, '0');
|
||||
if (t < 1) requestAnimationFrame(tick);
|
||||
}
|
||||
|
||||
requestAnimationFrame(tick);
|
||||
});
|
||||
})();
|
||||
33
js/favicon.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const frames = [
|
||||
'./images/snowflake/frame001.png',
|
||||
'./images/snowflake/frame002.png',
|
||||
'./images/snowflake/frame003.png',
|
||||
'./images/snowflake/frame004.png',
|
||||
'./images/snowflake/frame005.png',
|
||||
'./images/snowflake/frame006.png',
|
||||
'./images/snowflake/frame007.png',
|
||||
'./images/snowflake/frame008.png',
|
||||
'./images/snowflake/frame009.png',
|
||||
'./images/snowflake/frame010.png',
|
||||
'./images/snowflake/frame011.png',
|
||||
'./images/snowflake/frame012.png',
|
||||
'./images/snowflake/frame013.png',
|
||||
'./images/snowflake/frame014.png',
|
||||
'./images/snowflake/frame015.png',
|
||||
'./images/snowflake/frame016.png',
|
||||
'./images/snowflake/frame017.png',
|
||||
'./images/snowflake/frame018.png',
|
||||
'./images/snowflake/frame019.png',
|
||||
'./images/snowflake/frame020.png',
|
||||
'./images/snowflake/frame021.png',
|
||||
'./images/snowflake/frame022.png',
|
||||
'./images/snowflake/frame023.png'
|
||||
];
|
||||
|
||||
let current = 0;
|
||||
const favicon = document.getElementById('dynamic-favicon');
|
||||
|
||||
setInterval(() => {
|
||||
current = (current + 1) % frames.length;
|
||||
favicon.href = frames[current];
|
||||
}, 150);
|
||||
51
js/onLoad.js
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
function newFact() {
|
||||
const facts = [
|
||||
'Are you watching me, or am I watching you?',
|
||||
'Is the self you know, the self that exists?',
|
||||
'When freedom is lost, what is left?',
|
||||
'Protect your privacy, embrace your freedom',
|
||||
'I am a wizard, and this is my realm.',
|
||||
];
|
||||
|
||||
const randomFact = Math.floor(Math.random() * facts.length);
|
||||
const factDisplayElement = document.getElementById("factDisplay");
|
||||
if (factDisplayElement) {
|
||||
factDisplayElement.innerHTML = facts[randomFact];
|
||||
} else {
|
||||
console.warn('Element with ID "factDisplay" not found.');
|
||||
}
|
||||
}
|
||||
|
||||
function initVideoControls() {
|
||||
const video = document.getElementById("bgVideo");
|
||||
const btn = document.getElementById("vidBtn");
|
||||
|
||||
window.PlayPauseVideo = function PlayPauseVideo() {
|
||||
if (!video || !btn) return;
|
||||
|
||||
if (video.paused) {
|
||||
video.play();
|
||||
btn.innerHTML = "TURN OFF";
|
||||
} else {
|
||||
video.pause();
|
||||
btn.innerHTML = "TURN ON";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function applyFadeIn() {
|
||||
const fadeInElements = document.querySelectorAll(".fade-in");
|
||||
|
||||
fadeInElements.forEach((element) => {
|
||||
requestAnimationFrame(() => {
|
||||
element.classList.add("visible");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
newFact();
|
||||
applyFadeIn();
|
||||
initVideoControls();
|
||||
});
|
||||
249
js/player.js
Normal file
@@ -0,0 +1,249 @@
|
||||
(function () {
|
||||
const playlist = [
|
||||
//{ title: "Sanctuary OS", artist: "Dusqk", src: "music/SanctuaryOS.flac" },
|
||||
{ title: "Sanctuary 1", artist: "Dusqk", src: "music/sanctuary_1.m4a" },
|
||||
{ title: "cloud zone", artist: "oooz", src: "music/cloud_zone.m4a" },
|
||||
{ title: "ketamina", artist: "yaego", src: "music/ketamina.m4a" },
|
||||
{ title: "fine night", artist: "goreshit", src: "music/fine_night.m4a" },
|
||||
{ title: "what we did in the desert", artist: "eightiesheadachetape", src: "music/what_we_did_in_the_desert.m4a" },
|
||||
{ title: "Fleeting Frozen Heart", artist: "Xxtarlit⚸", src: "music/fleeting_frozen_heart.m4a" },
|
||||
{ title: "Heaven", artist: "i\\believe\\in\\angels", src: "music/heaven.m4a" },
|
||||
{ title: "i wish to have a happy end", artist: "イア / ia", src: "music/i_wish_to_have_a_happy_end.m4a" },
|
||||
{ title: "it is not my fault and never will be", artist: "Skeinn", src: "music/it_is_not_my_fault_and_never_will_be.m4a" },
|
||||
{ title: "Sanctuary 10", artist: "Dusqk", src: "music/sanctuary_10.m4a" },
|
||||
{ title: "Lexapro Delirium", artist: "sewerslvt", src: "music/lexapro_delirium.m4a" },
|
||||
];
|
||||
|
||||
let currentIdx = -1;
|
||||
let isPlaying = false;
|
||||
let isShuffle = false;
|
||||
let isLoop = false;
|
||||
let wasEnded = false;
|
||||
|
||||
const audio = new Audio();
|
||||
audio.volume = 0.7;
|
||||
audio.preload = "metadata";
|
||||
|
||||
const elTitle = document.getElementById('mp-title');
|
||||
const elArtist = document.getElementById('mp-artist');
|
||||
const elPlay = document.getElementById('mp-play');
|
||||
const elPrev = document.getElementById('mp-prev');
|
||||
const elNext = document.getElementById('mp-next');
|
||||
const elShuffle = document.getElementById('mp-shuffle');
|
||||
const elLoop = document.getElementById('mp-loop');
|
||||
const elSeek = document.getElementById('mp-seek');
|
||||
const elFill = document.getElementById('mp-progress-fill');
|
||||
const elCurrent = document.getElementById('mp-current');
|
||||
const elDur = document.getElementById('mp-duration');
|
||||
const elVol = document.getElementById('mp-volume');
|
||||
const elList = document.getElementById('mp-playlist');
|
||||
|
||||
function buildList() {
|
||||
elList.innerHTML = '';
|
||||
if (!playlist.length) {
|
||||
const li = document.createElement('li');
|
||||
li.className = 'mp-empty';
|
||||
li.textContent = 'playlist is empty';
|
||||
elList.appendChild(li);
|
||||
return;
|
||||
}
|
||||
|
||||
playlist.forEach((track, i) => {
|
||||
const li = document.createElement('li');
|
||||
li.textContent = (i + 1) + '. ' + track.title;
|
||||
li.title = track.title + (track.artist ? ' · ' + track.artist : '');
|
||||
li.addEventListener('click', () => loadTrack(i, true));
|
||||
elList.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
function formatTime(s) {
|
||||
if (isNaN(s) || s < 0) return '0:00';
|
||||
const m = Math.floor(s / 60);
|
||||
const sec = Math.floor(s % 60);
|
||||
return m + ':' + (sec < 10 ? '0' : '') + sec;
|
||||
}
|
||||
|
||||
function highlightRow(idx) {
|
||||
const items = elList.querySelectorAll('li');
|
||||
items.forEach((li, i) => li.classList.toggle('playing', i === idx));
|
||||
}
|
||||
|
||||
function setPlayIcon() {
|
||||
elPlay.innerHTML = isPlaying ? '▮▮' : '▶';
|
||||
}
|
||||
|
||||
function loadTrack(idx, autoplay) {
|
||||
if (!playlist.length) return;
|
||||
currentIdx = idx;
|
||||
const t = playlist[idx];
|
||||
audio.src = t.src;
|
||||
elTitle.textContent = t.title || 'untitled';
|
||||
elArtist.textContent = t.artist || '---';
|
||||
elFill.style.width = '0%';
|
||||
elSeek.value = 0;
|
||||
elCurrent.textContent = '0:00';
|
||||
elDur.textContent = '0:00';
|
||||
highlightRow(idx);
|
||||
|
||||
if (autoplay) {
|
||||
audio.play().then(() => { isPlaying = true; setPlayIcon(); })
|
||||
.catch(() => {});
|
||||
} else {
|
||||
isPlaying = false;
|
||||
setPlayIcon();
|
||||
}
|
||||
}
|
||||
|
||||
function nextTrack() {
|
||||
if (!playlist.length) return;
|
||||
let idx;
|
||||
if (isShuffle) {
|
||||
idx = Math.floor(Math.random() * playlist.length);
|
||||
} else {
|
||||
idx = (currentIdx + 1) % playlist.length;
|
||||
}
|
||||
loadTrack(idx, true);
|
||||
}
|
||||
|
||||
function prevTrack() {
|
||||
if (!playlist.length) return;
|
||||
|
||||
if (audio.currentTime > 3) {
|
||||
audio.currentTime = 0;
|
||||
return;
|
||||
}
|
||||
let idx = (currentIdx - 1 + playlist.length) % playlist.length;
|
||||
loadTrack(idx, true);
|
||||
}
|
||||
|
||||
elPlay.addEventListener('click', () => {
|
||||
if (!playlist.length) return;
|
||||
if (currentIdx === -1) { loadTrack(0, true); return; }
|
||||
if (isPlaying) {
|
||||
audio.pause();
|
||||
isPlaying = false;
|
||||
} else {
|
||||
audio.play().catch(() => {});
|
||||
isPlaying = true;
|
||||
}
|
||||
setPlayIcon();
|
||||
});
|
||||
|
||||
elNext.addEventListener('click', nextTrack);
|
||||
elPrev.addEventListener('click', prevTrack);
|
||||
|
||||
elShuffle.addEventListener('click', () => {
|
||||
isShuffle = !isShuffle;
|
||||
elShuffle.classList.toggle('active', isShuffle);
|
||||
});
|
||||
|
||||
elLoop.addEventListener('click', () => {
|
||||
isLoop = !isLoop;
|
||||
audio.loop = isLoop;
|
||||
elLoop.classList.toggle('active', isLoop);
|
||||
});
|
||||
|
||||
function updateVolFill() {
|
||||
const pct = (elVol.value / elVol.max) * 100;
|
||||
elVol.style.background = 'linear-gradient(90deg, #a9c6f0 0%, #74a4db ' + pct + '%, #e8ecf0 ' + pct + '%)';
|
||||
}
|
||||
|
||||
elVol.addEventListener('input', () => { audio.volume = elVol.value; updateVolFill(); });
|
||||
|
||||
elSeek.addEventListener('input', () => {
|
||||
if (audio.duration) {
|
||||
audio.currentTime = (elSeek.value / 100) * audio.duration;
|
||||
}
|
||||
});
|
||||
|
||||
audio.addEventListener('timeupdate', () => {
|
||||
if (!audio.duration) return;
|
||||
const pct = (audio.currentTime / audio.duration) * 100;
|
||||
elFill.style.width = pct + '%';
|
||||
elSeek.value = pct;
|
||||
elCurrent.textContent = formatTime(audio.currentTime);
|
||||
});
|
||||
|
||||
audio.addEventListener('loadedmetadata', () => {
|
||||
elDur.textContent = formatTime(audio.duration);
|
||||
});
|
||||
|
||||
audio.addEventListener('ended', () => {
|
||||
if (!isLoop) nextTrack();
|
||||
});
|
||||
|
||||
audio.addEventListener('play', () => { isPlaying = true; setPlayIcon(); });
|
||||
audio.addEventListener('pause', () => { isPlaying = false; setPlayIcon(); });
|
||||
|
||||
function setCookie(name, value, days) {
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + days * 86400000);
|
||||
document.cookie = name + '=' + encodeURIComponent(value) + ';expires=' + d.toUTCString() + ';path=/;SameSite=Lax';
|
||||
}
|
||||
|
||||
function getCookie(name) {
|
||||
const match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]*)'));
|
||||
return match ? decodeURIComponent(match[1]) : null;
|
||||
}
|
||||
|
||||
function saveState() {
|
||||
setCookie('mp_track', currentIdx, 30);
|
||||
setCookie('mp_time', Math.floor(audio.currentTime), 30);
|
||||
setCookie('mp_volume', audio.volume, 30);
|
||||
setCookie('mp_shuffle', isShuffle ? '1' : '0', 30);
|
||||
setCookie('mp_loop', isLoop ? '1' : '0', 30);
|
||||
}
|
||||
|
||||
setInterval(saveState, 5000);
|
||||
window.addEventListener('beforeunload', saveState);
|
||||
|
||||
buildList();
|
||||
setPlayIcon();
|
||||
updateVolFill();
|
||||
|
||||
const savedTrack = getCookie('mp_track');
|
||||
const savedTime = getCookie('mp_time');
|
||||
const savedVol = getCookie('mp_volume');
|
||||
const savedShuf = getCookie('mp_shuffle');
|
||||
const savedLoop = getCookie('mp_loop');
|
||||
|
||||
if (savedVol !== null) {
|
||||
audio.volume = parseFloat(savedVol);
|
||||
elVol.value = audio.volume;
|
||||
updateVolFill();
|
||||
}
|
||||
|
||||
if (savedShuf === '1') {
|
||||
isShuffle = true;
|
||||
elShuffle.classList.add('active');
|
||||
}
|
||||
|
||||
if (savedLoop === '1') {
|
||||
isLoop = true;
|
||||
audio.loop = true;
|
||||
elLoop.classList.add('active');
|
||||
}
|
||||
|
||||
if (savedTrack !== null && playlist[parseInt(savedTrack)]) {
|
||||
const idx = parseInt(savedTrack);
|
||||
loadTrack(idx, false);
|
||||
if (savedTime !== null) {
|
||||
audio.addEventListener('loadedmetadata', function onceSeek() {
|
||||
audio.currentTime = Math.min(parseInt(savedTime), audio.duration || 0);
|
||||
audio.removeEventListener('loadedmetadata', onceSeek);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function autoplayOnce() {
|
||||
if (currentIdx >= 0 && !isPlaying) {
|
||||
audio.play().catch(() => {});
|
||||
}
|
||||
document.removeEventListener('click', autoplayOnce);
|
||||
document.removeEventListener('keydown', autoplayOnce);
|
||||
}
|
||||
|
||||
document.addEventListener('click', autoplayOnce);
|
||||
document.addEventListener('keydown', autoplayOnce);
|
||||
})();
|
||||
41
js/switchPage.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const pages = {
|
||||
home: { title: "welcome" },
|
||||
contact: { title: "contact" },
|
||||
links: { title: "links" },
|
||||
sitemap: { title: "sitemap" }
|
||||
};
|
||||
|
||||
function switchPage(pageKey) {
|
||||
const page = pages[pageKey];
|
||||
const template = document.getElementById('page-' + pageKey);
|
||||
if (!page || !template) return;
|
||||
|
||||
const contentDiv = document.getElementById('page-content');
|
||||
const toolbarTitle = document.getElementById('toolbar-title');
|
||||
|
||||
location.hash = pageKey === 'home' ? '' : pageKey;
|
||||
|
||||
contentDiv.style.opacity = 0;
|
||||
|
||||
setTimeout(() => {
|
||||
contentDiv.innerHTML = '';
|
||||
contentDiv.appendChild(template.content.cloneNode(true));
|
||||
if (toolbarTitle) {
|
||||
toolbarTitle.textContent = page.title;
|
||||
toolbarTitle.setAttribute('data-text', page.title);
|
||||
}
|
||||
|
||||
contentDiv.style.transition = 'opacity 0.3s ease';
|
||||
contentDiv.style.opacity = 1;
|
||||
}, 150);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const hash = location.hash.replace('#', '');
|
||||
switchPage(pages[hash] ? hash : 'home');
|
||||
});
|
||||
|
||||
window.addEventListener('hashchange', () => {
|
||||
const hash = location.hash.replace('#', '');
|
||||
switchPage(pages[hash] ? hash : 'home');
|
||||
});
|
||||
40
js/updates.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const updates = [
|
||||
{ date: "28.02.2026", message: 'Music player now remembers your track and position via cookies \uE026' },
|
||||
{ date: "28.02.2026", message: 'Added autoplay on first interaction \uE026' },
|
||||
{ date: "28.02.2026", message: 'Added hash-based routing so refreshing keeps your page \uE026' },
|
||||
{ date: "27.02.2026", message: 'Added the music player with shuffle, loop, and volume controls \uE026' },
|
||||
{ date: "27.02.2026", message: 'Created the sitemap page \uE026' },
|
||||
{ date: "27.02.2026", message: 'Created the contact page \uE026' },
|
||||
{ date: "26.02.2026", message: 'Created the links page \uE026' },
|
||||
{ date: "26.02.2026", message: 'Added this pictochat updates box \uE026' },
|
||||
{ date: "25.02.2026", message: 'Wrote the home page' },
|
||||
{ date: "24.02.2026", message: 'Styled everything with custom CSS \uE026' },
|
||||
{ date: "24.02.2026", message: 'Added the video background \uE026' },
|
||||
{ date: "23.02.2026", message: 'Built the page switching system \uE026' },
|
||||
{ date: "23.02.2026", message: 'Created the navigation menu' },
|
||||
{ date: "22.02.2026", message: 'Set up the main layout' },
|
||||
{ date: "21.02.2026", message: 'Started building the site \uE026' },
|
||||
];
|
||||
|
||||
(function () {
|
||||
const picto = document.querySelector('#pictochat-content .picto');
|
||||
if (!picto) return;
|
||||
|
||||
updates.forEach(({ date, message }) => {
|
||||
const entry = document.createElement('div');
|
||||
entry.style.setProperty('--col', '#90c9ff');
|
||||
|
||||
const nameDiv = document.createElement('div');
|
||||
nameDiv.textContent = 'bug';
|
||||
|
||||
const msgDiv = document.createElement('div');
|
||||
msgDiv.innerHTML = message;
|
||||
|
||||
entry.appendChild(nameDiv);
|
||||
entry.appendChild(document.createTextNode(date));
|
||||
entry.appendChild(document.createElement('br'));
|
||||
entry.appendChild(msgDiv);
|
||||
|
||||
picto.appendChild(entry);
|
||||
});
|
||||
})();
|
||||