moving to custom swap engine
Signed-off-by: ari melody <ari@arimelody.me>
This commit is contained in:
parent
749f9bc8b7
commit
13d802d361
|
@ -13,7 +13,7 @@ tmp_dir = "tmp"
|
||||||
exclude_unchanged = false
|
exclude_unchanged = false
|
||||||
follow_symlink = false
|
follow_symlink = false
|
||||||
full_bin = ""
|
full_bin = ""
|
||||||
include_dir = []
|
include_dir = [".", "views", "api"]
|
||||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||||
include_file = []
|
include_file = []
|
||||||
kill_delay = "0s"
|
kill_delay = "0s"
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
version: '3.9'
|
version: '3.9'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
image: postgres:16.1-alpine3.18
|
image: postgres:16.1-alpine3.18
|
||||||
container_name: arimelody.me-db
|
container_name: arimelody.me-db
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
volumes:
|
volumes:
|
||||||
- arimelody-db:/var/lib/postgresql/data
|
- arimelody-db:/var/lib/postgresql/data
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: arimelody
|
POSTGRES_DB: arimelody
|
||||||
POSTGRES_USER: arimelody
|
POSTGRES_USER: arimelody
|
||||||
POSTGRES_PASSWORD: fuckingpassword
|
POSTGRES_PASSWORD: fuckingpassword
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
arimelody-db:
|
arimelody-db:
|
||||||
external: true
|
external: true
|
||||||
|
|
64
main.go
64
main.go
|
@ -6,7 +6,6 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -37,7 +36,7 @@ var base_template = template.Must(template.ParseFiles(
|
||||||
"views/footer.html",
|
"views/footer.html",
|
||||||
"views/prideflag.html",
|
"views/prideflag.html",
|
||||||
))
|
))
|
||||||
var htmx_template = template.Must(template.New("root").Parse(`<head>{{block "head" .}}{{end}}</head>{{block "content" .}}{{end}}`))
|
// var htmx_template = template.Must(template.New("root").Parse(`<head>{{block "head" .}}{{end}}</head>{{block "content" .}}{{end}}`))
|
||||||
|
|
||||||
func log_request(req *http.Request, code int, start_time time.Time) {
|
func log_request(req *http.Request, code int, start_time time.Time) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
@ -61,25 +60,27 @@ func handle_request(writer http.ResponseWriter, req *http.Request) {
|
||||||
uri := req.URL.Path
|
uri := req.URL.Path
|
||||||
start_time := time.Now()
|
start_time := time.Now()
|
||||||
|
|
||||||
hx_request := len(req.Header["Hx-Request"]) > 0 && req.Header["Hx-Request"][0] == "true"
|
// hx_request := len(req.Header["Hx-Request"]) > 0 && req.Header["Hx-Request"][0] == "true"
|
||||||
|
//
|
||||||
// don't bother fulfilling requests to a page that's already loaded on the client!
|
// // don't bother fulfilling requests to a page that's already loaded on the client!
|
||||||
if hx_request && len(req.Header["Referer"]) > 0 && len(req.Header["Hx-Current-Url"]) > 0 {
|
// if hx_request && len(req.Header["Referer"]) > 0 && len(req.Header["Hx-Current-Url"]) > 0 {
|
||||||
regex := regexp.MustCompile(`https?:\/\/[^\/]+`)
|
// regex := regexp.MustCompile(`https?:\/\/[^\/]+`)
|
||||||
current_location := regex.ReplaceAllString(req.Header["Hx-Current-Url"][0], "")
|
// current_location := regex.ReplaceAllString(req.Header["Hx-Current-Url"][0], "")
|
||||||
if current_location == req.URL.Path {
|
// if current_location == req.URL.Path {
|
||||||
writer.WriteHeader(204);
|
// writer.WriteHeader(204);
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
code := func(writer http.ResponseWriter, req *http.Request) int {
|
code := func(writer http.ResponseWriter, req *http.Request) int {
|
||||||
var root *template.Template
|
// var root *template.Template
|
||||||
if hx_request {
|
// if hx_request {
|
||||||
root = template.Must(htmx_template.Clone())
|
// root = template.Must(htmx_template.Clone())
|
||||||
} else {
|
// } else {
|
||||||
root = template.Must(base_template.Clone())
|
// root = template.Must(base_template.Clone())
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
var root = template.Must(base_template.Clone())
|
||||||
|
|
||||||
if req.URL.Path == "/" {
|
if req.URL.Path == "/" {
|
||||||
return index_handler(writer, root)
|
return index_handler(writer, root)
|
||||||
|
@ -93,7 +94,7 @@ func handle_request(writer http.ResponseWriter, req *http.Request) {
|
||||||
return music_gateway_handler(writer, req, root)
|
return music_gateway_handler(writer, req, root)
|
||||||
}
|
}
|
||||||
|
|
||||||
return static_handler(writer, req)
|
return static_handler(writer, req, root)
|
||||||
}(writer, req)
|
}(writer, req)
|
||||||
|
|
||||||
log_request(req, code, start_time)
|
log_request(req, code, start_time)
|
||||||
|
@ -126,8 +127,7 @@ func music_gateway_handler(writer http.ResponseWriter, req *http.Request, root *
|
||||||
// return
|
// return
|
||||||
release, ok := music.GetRelease(id)
|
release, ok := music.GetRelease(id)
|
||||||
if !ok {
|
if !ok {
|
||||||
http.Error(writer, "404 not found", http.StatusNotFound)
|
return handle_not_found(writer, req, root)
|
||||||
return 404
|
|
||||||
}
|
}
|
||||||
gateway_template := template.Must(root.ParseFiles("views/music-gateway.html"))
|
gateway_template := template.Must(root.ParseFiles("views/music-gateway.html"))
|
||||||
err := gateway_template.Execute(writer, release)
|
err := gateway_template.Execute(writer, release)
|
||||||
|
@ -138,14 +138,13 @@ func music_gateway_handler(writer http.ResponseWriter, req *http.Request, root *
|
||||||
return 200
|
return 200
|
||||||
}
|
}
|
||||||
|
|
||||||
func static_handler(writer http.ResponseWriter, req *http.Request) int {
|
func static_handler(writer http.ResponseWriter, req *http.Request, root *template.Template) int {
|
||||||
filename := "public/" + req.URL.Path[1:]
|
filename := "public/" + req.URL.Path[1:]
|
||||||
|
|
||||||
// check the file's metadata
|
// check the file's metadata
|
||||||
info, err := os.Stat(filename)
|
info, err := os.Stat(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(writer, "404 not found", http.StatusNotFound)
|
return handle_not_found(writer, req, root)
|
||||||
return 404
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(req.Header["If-Modified-Since"]) > 0 && req.Header["If-Modified-Since"][0] != "" {
|
if len(req.Header["If-Modified-Since"]) > 0 && req.Header["If-Modified-Since"][0] != "" {
|
||||||
|
@ -185,6 +184,21 @@ func static_handler(writer http.ResponseWriter, req *http.Request) int {
|
||||||
return 200
|
return 200
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handle_not_found(writer http.ResponseWriter, req *http.Request, root *template.Template) int {
|
||||||
|
type ErrorData struct {
|
||||||
|
Target string
|
||||||
|
}
|
||||||
|
error_data := ErrorData{ Target: req.URL.Path }
|
||||||
|
writer.WriteHeader(404);
|
||||||
|
error_template := template.Must(root.ParseFiles("views/404.html"))
|
||||||
|
err := error_template.Execute(writer, error_data)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(writer, err.Error(), http.StatusInternalServerError)
|
||||||
|
return 500
|
||||||
|
}
|
||||||
|
return 404
|
||||||
|
}
|
||||||
|
|
||||||
func parse_markdown(md []byte) []byte {
|
func parse_markdown(md []byte) []byte {
|
||||||
extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock
|
extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock
|
||||||
p := parser.NewWithExtensions(extensions)
|
p := parser.NewWithExtensions(extensions)
|
||||||
|
|
BIN
public/img/buttons/zvava.png
Normal file
BIN
public/img/buttons/zvava.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
|
@ -1,44 +0,0 @@
|
||||||
const accessibility = JSON.parse(localStorage.getItem("accessibility")) || {};
|
|
||||||
|
|
||||||
function toggle_accessibility_setting(name) {
|
|
||||||
if (accessibility[name]) {
|
|
||||||
delete accessibility[name];
|
|
||||||
update_accessibility();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
accessibility[name] = true;
|
|
||||||
update_accessibility();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function set_accessibility_setting(name, value) {
|
|
||||||
accessibility[name] = value;
|
|
||||||
update_accessibility();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function clear_accessibility_setting(name) {
|
|
||||||
if (!accessibility[name]) return false;
|
|
||||||
delete accessibility[name];
|
|
||||||
update_accessibility();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function update_accessibility() {
|
|
||||||
localStorage.setItem("accessibility", JSON.stringify(accessibility));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (accessibility) {
|
|
||||||
if (accessibility.disable_crt) {
|
|
||||||
document.querySelector('div#overlay').setAttribute("hidden", true);
|
|
||||||
document.body.style.textShadow = "none";
|
|
||||||
document.getElementById('toggle-crt').classList.add("disabled");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById("toggle-crt").addEventListener("click", () => {
|
|
||||||
toggle_accessibility_setting("disable_crt");
|
|
||||||
document.querySelector('div#overlay').toggleAttribute("hidden");
|
|
||||||
document.getElementById('toggle-crt').className = accessibility.disable_crt ? "disabled" : "";
|
|
||||||
});
|
|
||||||
|
|
42
public/script/config.js
Normal file
42
public/script/config.js
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
function toggle_config_setting(config, name) {
|
||||||
|
if (config[name]) {
|
||||||
|
delete config[name];
|
||||||
|
update_config(config);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
config[name] = true;
|
||||||
|
update_config(config);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_config_setting(config, name, value) {
|
||||||
|
config[name] = value;
|
||||||
|
update_config(config);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear_config_setting(config, name) {
|
||||||
|
if (!config[name]) return false;
|
||||||
|
delete config[name];
|
||||||
|
update_config(config);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function update_config(config) {
|
||||||
|
localStorage.setItem("config", JSON.stringify(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = JSON.parse(localStorage.getItem("config")) || {};
|
||||||
|
if (config) {
|
||||||
|
if (config.disable_crt) {
|
||||||
|
document.querySelector('div#overlay').setAttribute("hidden", true);
|
||||||
|
document.body.style.textShadow = "none";
|
||||||
|
document.getElementById('toggle-crt').classList.add("disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("toggle-crt").addEventListener("click", () => {
|
||||||
|
toggle_config_setting(config, "disable_crt");
|
||||||
|
document.querySelector('div#overlay').toggleAttribute("hidden");
|
||||||
|
document.getElementById('toggle-crt').className = config.disable_crt ? "disabled" : "";
|
||||||
|
});
|
|
@ -1,14 +1,12 @@
|
||||||
const header_links = document.getElementById("header-links");
|
const header_links = document.getElementById("header-links");
|
||||||
const hamburger = document.getElementById("header-links-toggle");
|
const hamburger = document.getElementById("header-links-toggle");
|
||||||
|
|
||||||
function toggle_header_links() {
|
|
||||||
header_links.classList.toggle("open");
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("click", event => {
|
document.addEventListener("click", event => {
|
||||||
if (!header_links.contains(event.target) && !hamburger.contains(event.target) && !header_links.href) {
|
if (!header_links.contains(event.target) && !hamburger.contains(event.target) && !header_links.href) {
|
||||||
header_links.classList.remove("open");
|
header_links.classList.remove("open");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
hamburger.addEventListener("click", event => { toggle_header_links(); });
|
hamburger.addEventListener("click", event => {
|
||||||
|
header_links.classList.toggle("open");
|
||||||
|
});
|
||||||
|
|
147
public/script/lib/htmx-preload.js
Normal file
147
public/script/lib/htmx-preload.js
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
// This adds the "preload" extension to htmx. By default, this will
|
||||||
|
// preload the targets of any tags with `href` or `hx-get` attributes
|
||||||
|
// if they also have a `preload` attribute as well. See documentation
|
||||||
|
// for more details
|
||||||
|
htmx.defineExtension("preload", {
|
||||||
|
|
||||||
|
onEvent: function(name, event) {
|
||||||
|
|
||||||
|
// Only take actions on "htmx:afterProcessNode"
|
||||||
|
if (name !== "htmx:afterProcessNode") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SOME HELPER FUNCTIONS WE'LL NEED ALONG THE WAY
|
||||||
|
|
||||||
|
// attr gets the closest non-empty value from the attribute.
|
||||||
|
var attr = function(node, property) {
|
||||||
|
if (node == undefined) {return undefined;}
|
||||||
|
return node.getAttribute(property) || node.getAttribute("data-" + property) || attr(node.parentElement, property)
|
||||||
|
}
|
||||||
|
|
||||||
|
// load handles the actual HTTP fetch, and uses htmx.ajax in cases where we're
|
||||||
|
// preloading an htmx resource (this sends the same HTTP headers as a regular htmx request)
|
||||||
|
var load = function(node) {
|
||||||
|
|
||||||
|
// Called after a successful AJAX request, to mark the
|
||||||
|
// content as loaded (and prevent additional AJAX calls.)
|
||||||
|
var done = function(html) {
|
||||||
|
if (!node.preloadAlways) {
|
||||||
|
node.preloadState = "DONE"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attr(node, "preload-images") == "true") {
|
||||||
|
document.createElement("div").innerHTML = html // create and populate a node to load linked resources, too.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return function() {
|
||||||
|
|
||||||
|
// If this value has already been loaded, then do not try again.
|
||||||
|
if (node.preloadState !== "READY") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special handling for HX-GET - use built-in htmx.ajax function
|
||||||
|
// so that headers match other htmx requests, then set
|
||||||
|
// node.preloadState = TRUE so that requests are not duplicated
|
||||||
|
// in the future
|
||||||
|
var hxGet = node.getAttribute("hx-get") || node.getAttribute("data-hx-get")
|
||||||
|
if (hxGet) {
|
||||||
|
htmx.ajax("GET", hxGet, {
|
||||||
|
source: node,
|
||||||
|
handler:function(elt, info) {
|
||||||
|
done(info.xhr.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, perform a standard xhr request, then set
|
||||||
|
// node.preloadState = TRUE so that requests are not duplicated
|
||||||
|
// in the future.
|
||||||
|
if (node.getAttribute("href")) {
|
||||||
|
var r = new XMLHttpRequest();
|
||||||
|
r.open("GET", node.getAttribute("href"));
|
||||||
|
r.onload = function() {done(r.responseText);};
|
||||||
|
r.send();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function processes a specific node and sets up event handlers.
|
||||||
|
// We'll search for nodes and use it below.
|
||||||
|
var init = function(node) {
|
||||||
|
|
||||||
|
// If this node DOES NOT include a "GET" transaction, then there's nothing to do here.
|
||||||
|
if (node.getAttribute("href") + node.getAttribute("hx-get") + node.getAttribute("data-hx-get") == "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guarantee that we only initialize each node once.
|
||||||
|
if (node.preloadState !== undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get event name from config.
|
||||||
|
var on = attr(node, "preload") || "mousedown"
|
||||||
|
const always = on.indexOf("always") !== -1
|
||||||
|
if (always) {
|
||||||
|
on = on.replace('always', '').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FALL THROUGH to here means we need to add an EventListener
|
||||||
|
|
||||||
|
// Apply the listener to the node
|
||||||
|
node.addEventListener(on, function(evt) {
|
||||||
|
if (node.preloadState === "PAUSE") { // Only add one event listener
|
||||||
|
node.preloadState = "READY"; // Required for the `load` function to trigger
|
||||||
|
|
||||||
|
// Special handling for "mouseover" events. Wait 100ms before triggering load.
|
||||||
|
if (on === "mouseover") {
|
||||||
|
window.setTimeout(load(node), 100);
|
||||||
|
} else {
|
||||||
|
load(node)() // all other events trigger immediately.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Special handling for certain built-in event handlers
|
||||||
|
switch (on) {
|
||||||
|
|
||||||
|
case "mouseover":
|
||||||
|
// Mirror `touchstart` events (fires immediately)
|
||||||
|
node.addEventListener("touchstart", load(node));
|
||||||
|
|
||||||
|
// WHhen the mouse leaves, immediately disable the preload
|
||||||
|
node.addEventListener("mouseout", function(evt) {
|
||||||
|
if ((evt.target === node) && (node.preloadState === "READY")) {
|
||||||
|
node.preloadState = "PAUSE";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "mousedown":
|
||||||
|
// Mirror `touchstart` events (fires immediately)
|
||||||
|
node.addEventListener("touchstart", load(node));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the node as ready to run.
|
||||||
|
node.preloadState = "PAUSE";
|
||||||
|
node.preloadAlways = always;
|
||||||
|
htmx.trigger(node, "preload:init") // This event can be used to load content immediately.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for all child nodes that have a "preload" attribute
|
||||||
|
event.target.querySelectorAll("[preload]").forEach(function(node) {
|
||||||
|
|
||||||
|
// Initialize the node with the "preload" attribute
|
||||||
|
init(node)
|
||||||
|
|
||||||
|
// Initialize all child elements that are anchors or have `hx-get` (use with care)
|
||||||
|
node.querySelectorAll("a,[hx-get],[data-hx-get]").forEach(init)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
3922
public/script/lib/htmx.js
Normal file
3922
public/script/lib/htmx.js
Normal file
File diff suppressed because it is too large
Load diff
2
public/script/lib/htmx.min.js
vendored
2
public/script/lib/htmx.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,5 @@
|
||||||
import "./header.js";
|
import "./header.js";
|
||||||
import "./accessibility.js";
|
import "./config.js";
|
||||||
|
|
||||||
function type_out(e) {
|
function type_out(e) {
|
||||||
const text = e.innerText;
|
const text = e.innerText;
|
||||||
|
@ -34,46 +34,50 @@ function type_out(e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function fill_list(list) {
|
function fill_list(list) {
|
||||||
const items = list.querySelectorAll("li a, li span");
|
const items = list.querySelectorAll("li a, li span");
|
||||||
items.innerText = "";
|
items.innerText = "";
|
||||||
const delay = 100;
|
const delay = 100;
|
||||||
|
|
||||||
items.forEach((item, iter) => {
|
items.forEach((item, iter) => {
|
||||||
item.style.animationDelay = `${iter * delay}ms`;
|
item.style.animationDelay = `${iter * delay}ms`;
|
||||||
item.style.animationPlayState = "playing";
|
item.style.animationPlayState = "playing";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[...document.querySelectorAll("h1, h2, h3, h4, h5, h6")]
|
function start() {
|
||||||
.filter((e) => e.innerText != "")
|
[...document.querySelectorAll("h1, h2, h3, h4, h5, h6")]
|
||||||
.forEach((e) => {
|
.filter((e) => e.innerText != "")
|
||||||
type_out(e);
|
.forEach((e) => {
|
||||||
});
|
type_out(e);
|
||||||
[...document.querySelectorAll("ol, ul")]
|
});
|
||||||
.filter((e) => e.innerText != "")
|
[...document.querySelectorAll("ol, ul")]
|
||||||
.forEach((e) => {
|
.filter((e) => e.innerText != "")
|
||||||
fill_list(e);
|
.forEach((e) => {
|
||||||
});
|
fill_list(e);
|
||||||
|
});
|
||||||
|
|
||||||
document.addEventListener("htmx:afterSwap", async event => {
|
document.addEventListener("htmx:afterSwap", async event => {
|
||||||
const res = await event.detail.xhr.response;
|
const res = await event.detail.xhr.response;
|
||||||
var new_head = res.substring(res.indexOf("<head>")+1, res.indexOf("</head>"));
|
var new_head = res.substring(res.indexOf("<head>")+1, res.indexOf("</head>"));
|
||||||
if (new_head) {
|
if (new_head) {
|
||||||
document.head.innerHTML = new_head;
|
document.head.innerHTML = new_head;
|
||||||
}
|
}
|
||||||
window.scrollY = 0;
|
window.scrollY = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const top_button = document.getElementById("backtotop");
|
const top_button = document.getElementById("backtotop");
|
||||||
window.onscroll = () => {
|
window.onscroll = () => {
|
||||||
if (!top_button) return;
|
if (!top_button) return;
|
||||||
const btt_threshold = 100;
|
const btt_threshold = 100;
|
||||||
if (
|
if (
|
||||||
document.body.scrollTop > btt_threshold ||
|
document.body.scrollTop > btt_threshold ||
|
||||||
document.documentElement.scrollTop > btt_threshold
|
document.documentElement.scrollTop > btt_threshold
|
||||||
) {
|
) {
|
||||||
top_button.classList.add("active");
|
top_button.classList.add("active");
|
||||||
} else {
|
} else {
|
||||||
top_button.classList.remove("active");
|
top_button.classList.remove("active");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
start();
|
||||||
|
|
|
@ -1,44 +1,24 @@
|
||||||
import "./main.js";
|
import "./main.js";
|
||||||
|
|
||||||
const bg = document.getElementById("background");
|
|
||||||
bg.style.backgroundImage = `url(${bg.dataset.url})`;
|
|
||||||
bg.removeAttribute("data-url");
|
|
||||||
|
|
||||||
const share_btn = document.getElementById("share");
|
|
||||||
share_btn.onclick = (e) => {
|
|
||||||
navigator.clipboard.writeText(window.location.href);
|
|
||||||
share_btn.classList.remove('active');
|
|
||||||
void share_btn.offsetWidth;
|
|
||||||
share_btn.classList.add('active');
|
|
||||||
}
|
|
||||||
|
|
||||||
const go_back_btn = document.getElementById("go-back")
|
|
||||||
go_back_btn.innerText = "<";
|
|
||||||
go_back_btn.addEventListener("click", () => {
|
|
||||||
window.history.back();
|
|
||||||
});
|
|
||||||
|
|
||||||
apply_funny_bob_to_upcoming_tags();
|
|
||||||
|
|
||||||
function apply_funny_bob_to_upcoming_tags() {
|
function apply_funny_bob_to_upcoming_tags() {
|
||||||
const upcomingTags = document.querySelectorAll("#type.upcoming");
|
const upcoming_tags = document.querySelectorAll("#type.upcoming");
|
||||||
for (var i = 0; i < upcomingTags.length; i++) {
|
for (var i = 0; i < upcoming_tags.length; i++) {
|
||||||
const tag = upcomingTags[i];
|
const tag = upcoming_tags[i];
|
||||||
const chars = tag.innerText.split("");
|
const chars = tag.innerText.split("");
|
||||||
const result = chars.map((c, i) => `<span style="animation-delay: -${i * 100}ms;">${c}</span>`);
|
const result = chars.map((c, i) => `<span style="animation-delay: -${i * 100}ms;">${c}</span>`);
|
||||||
tag.innerHTML = result.join("");
|
tag.innerHTML = result.join("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const extras_pairs = Array.from(document.querySelectorAll("div#info > div").values()).map(container => {
|
|
||||||
return {
|
|
||||||
container,
|
|
||||||
button: document.getElementById("extras").querySelector(`ul li a[href="#${container.id}"]`)
|
|
||||||
};
|
|
||||||
});
|
|
||||||
const info_container = document.getElementById("info")
|
|
||||||
info_container.addEventListener("scroll", update_extras_buttons);
|
|
||||||
function update_extras_buttons() {
|
function update_extras_buttons() {
|
||||||
|
const extras_pairs = Array.from(document.querySelectorAll("div#info > div").values()).map(container => {
|
||||||
|
return {
|
||||||
|
container,
|
||||||
|
button: document.getElementById("extras").querySelector(`ul li a[href="#${container.id}"]`)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const info_container = document.getElementById("info")
|
||||||
|
info_container.addEventListener("scroll", update_extras_buttons);
|
||||||
const info_rect = info_container.getBoundingClientRect();
|
const info_rect = info_container.getBoundingClientRect();
|
||||||
const info_y = info_rect.y;
|
const info_y = info_rect.y;
|
||||||
const font_size = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
const font_size = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||||
|
@ -52,57 +32,38 @@ function update_extras_buttons() {
|
||||||
if (scroll_diff <= 0) current = pair;
|
if (scroll_diff <= 0) current = pair;
|
||||||
})
|
})
|
||||||
current.button.classList.add("active");
|
current.button.classList.add("active");
|
||||||
}
|
|
||||||
update_extras_buttons();
|
|
||||||
|
|
||||||
document.querySelectorAll("div#extras ul li a[href]").forEach(link => {
|
document.querySelectorAll("div#extras ul li a[href]").forEach(link => {
|
||||||
link.addEventListener("click", event => {
|
link.addEventListener("click", event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
location.replace(link.href);
|
location.replace(link.href);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
/*
|
function bind_go_back_btn() {
|
||||||
* handling track previews (currently not implemented)
|
const go_back_btn = document.getElementById("go-back")
|
||||||
|
go_back_btn.innerText = "<";
|
||||||
const previews = document.querySelectorAll("[id^=preview-]");
|
go_back_btn.addEventListener("click", () => {
|
||||||
for (const preview of previews) {
|
window.history.back();
|
||||||
preview.addEventListener("click", (e) => {
|
|
||||||
if (e.target.classList.contains('playing')) {
|
|
||||||
stopPreview(e.target);
|
|
||||||
} else {
|
|
||||||
playPreview(e.target);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
preview.querySelector('audio').addEventListener("ended", () => { stopPreview(preview); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var stupidsounds = false;
|
function bind_share_btn() {
|
||||||
|
const share_btn = document.getElementById("share");
|
||||||
function stopPreviews() {
|
share_btn.onclick = (e) => {
|
||||||
for (const preview of previews) stopPreview(preview);
|
navigator.clipboard.writeText(window.location.href);
|
||||||
|
share_btn.classList.remove('active');
|
||||||
|
void share_btn.offsetWidth;
|
||||||
|
share_btn.classList.add('active');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function playPreview(preview) {
|
function start() {
|
||||||
if (!stupidsounds) stopPreviews();
|
bind_share_btn();
|
||||||
const btn = preview.querySelector('i');
|
bind_go_back_btn();
|
||||||
btn.classList.remove("play", "fa-play");
|
apply_funny_bob_to_upcoming_tags();
|
||||||
btn.classList.add("pause", "fa-pause");
|
update_extras_buttons();
|
||||||
const audio = preview.querySelector('audio');
|
|
||||||
audio.play();
|
|
||||||
preview.classList.add('playing');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopPreview(preview) {
|
start();
|
||||||
const btn = preview.querySelector('i');
|
|
||||||
btn.classList.remove("pause", "fa-pause");
|
|
||||||
btn.classList.add("play", "fa-play");
|
|
||||||
const audio = preview.querySelector('audio');
|
|
||||||
audio.pause();
|
|
||||||
audio.currentTime = 0;
|
|
||||||
preview.classList.remove('playing');
|
|
||||||
}
|
|
||||||
|
|
||||||
stopPreviews();
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
53
public/script/swap.js
Normal file
53
public/script/swap.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
async function swap(url) {
|
||||||
|
const res = await fetch(url);
|
||||||
|
|
||||||
|
if (res.status !== 200) return;
|
||||||
|
if (!res.headers.get("content-type").startsWith("text/html")) return;
|
||||||
|
|
||||||
|
const text = await res.text();
|
||||||
|
const content = new DOMParser().parseFromString(text, "text/html");
|
||||||
|
|
||||||
|
const stylesheets = [...content.querySelectorAll("link[rel='stylesheet']")];
|
||||||
|
|
||||||
|
// swap title
|
||||||
|
document.title = content.title;
|
||||||
|
// swap body html
|
||||||
|
document.body.innerHTML = content.body.innerHTML;
|
||||||
|
// swap head html
|
||||||
|
document.head.innerHTML = content.head.innerHTML;
|
||||||
|
// swap stylesheets
|
||||||
|
// const old_sheets = document.head.querySelectorAll("link[rel='stylesheet']");
|
||||||
|
// stylesheets.forEach(stylesheet => { document.head.appendChild(stylesheet) });
|
||||||
|
// old_sheets.forEach(stylesheet => { stylesheet.remove(); });
|
||||||
|
// push history
|
||||||
|
window.history.pushState({}, "", url);
|
||||||
|
|
||||||
|
bind(document.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
function bind(content) {
|
||||||
|
if (typeof(content) !== 'object' || content.nodeType !== Node.ELEMENT_NODE) return;
|
||||||
|
|
||||||
|
content.querySelectorAll("a[href]").forEach(element => {
|
||||||
|
if (element.href.includes("#")) return;
|
||||||
|
if (!element.href.startsWith(window.location.origin)) return;
|
||||||
|
const href = element.href.substring(window.location.origin.length);
|
||||||
|
if (href.includes(".")) return;
|
||||||
|
|
||||||
|
element.addEventListener("click", event => {
|
||||||
|
event.preventDefault();
|
||||||
|
swap(element.href);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
content.querySelectorAll("[swap-url]").forEach(element => {
|
||||||
|
const href = element.attributes.getNamedItem('swap-url').value;
|
||||||
|
|
||||||
|
element.addEventListener("click", event => {
|
||||||
|
event.preventDefault();
|
||||||
|
swap(href);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(document.body);
|
18
public/style/error.css
Normal file
18
public/style/error.css
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
@import url("/style/main.css");
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: min(calc(100% - 4rem), 720px);
|
||||||
|
min-height: calc(100vh - 11.5rem);
|
||||||
|
margin: 0 auto 2rem auto;
|
||||||
|
padding-top: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
main h1 {
|
||||||
|
line-height: 3rem;
|
||||||
|
color: #f7215b;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 1em;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
21
views/404.html
Normal file
21
views/404.html
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{{define "head"}}
|
||||||
|
<title>404 - ari melody 💫</title>
|
||||||
|
<link rel="shortcut icon" href="/img/favicon.png" type="image/x-icon">
|
||||||
|
<link rel="stylesheet" href="/style/error.css">
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "content"}}
|
||||||
|
<main>
|
||||||
|
<h1>
|
||||||
|
# 404 - not found!
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
the page you're looking for does not exist.
|
||||||
|
<br>
|
||||||
|
if you like, you can head back <a href="/">home</a> or <a href="{{.Target}}">try again!</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><small>status: ERR_NOT_FOUND</small></p>
|
||||||
|
</main>
|
||||||
|
{{end}}
|
|
@ -9,12 +9,14 @@
|
||||||
|
|
||||||
{{block "head" .}}{{end}}
|
{{block "head" .}}{{end}}
|
||||||
|
|
||||||
<meta name="htmx-config" content='{"htmx.config.scrollIntoViewOnBoost":false}'>
|
<!-- <meta name="htmx-config" content='{"htmx.config.scrollIntoViewOnBoost":false}'> -->
|
||||||
<script type="application/javascript" src="/script/lib/htmx.min.js"></script>
|
<!-- <script type="application/javascript" src="/script/lib/htmx.js"></script> -->
|
||||||
<script type="application/javascript" src="/script/lib/htmx-head-support.js"></script>
|
<!-- <script type="application/javascript" src="/script/lib/htmx.min.js"></script> -->
|
||||||
|
<!-- <script type="application/javascript" src="/script/lib/htmx-preload.js"></script> -->
|
||||||
|
<script type="application/javascript" src="/script/swap.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body hx-ext="head-support">
|
<body hx-ext="head-support, preload">
|
||||||
{{template "header"}}
|
{{template "header"}}
|
||||||
|
|
||||||
{{block "content" .}}
|
{{block "content" .}}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<nav>
|
<nav>
|
||||||
<div id="header-home" hx-get="/" hx-on="click" hx-target="main" hx-swap="outerHTML show:window:top" hx-push-url="true">
|
<div id="header-home" hx-get="/" hx-on="click" hx-target="body" hx-swap="outerHTML show:window:top" preload="mouseover" hx-push-url="true">
|
||||||
<img src="/img/favicon.png" id="header-icon" width="100" height="100" alt="">
|
<img src="/img/favicon.png" id="header-icon" width="100" height="100" alt="">
|
||||||
<div id="header-text">
|
<div id="header-text">
|
||||||
<h1>ari melody</h1>
|
<h1>ari melody</h1>
|
||||||
|
@ -16,12 +16,12 @@
|
||||||
<rect y="40" width="70" height="10" rx="5" fill="#eee" />
|
<rect y="40" width="70" height="10" rx="5" fill="#eee" />
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
<ul id="header-links" hx-boost="true" hx-target="main" hx-swap="outerHTML show:window:top">
|
<ul id="header-links" hx-boost="true" hx-target="body" hx-swap="outerHTML show:window:top">
|
||||||
<li>
|
<li>
|
||||||
<a href="/">home</a>
|
<a href="/" preload="mouseover">home</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/music">music</a>
|
<a href="/music" preload="mouseover">music</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://git.arimelody.me/ari/arimelody.me" target="_blank">source</a>
|
<a href="https://git.arimelody.me/ari/arimelody.me" target="_blank">source</a>
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
{{block "head" .}}{{end}}
|
|
||||||
<meta name="htmx-config" content='{"htmx.config.scrollIntoViewOnBoost":false}'>
|
|
||||||
<script type="application/javascript" src="/script/lib/htmx.min.js"></script>
|
|
||||||
<script type="application/javascript" src="/script/lib/htmx-head-support.js"></script>
|
|
||||||
|
|
||||||
{{block "content" .}}{{end}}
|
|
|
@ -12,7 +12,7 @@
|
||||||
<meta property="og:description" content="home to your local SPACEGIRL 💫">
|
<meta property="og:description" content="home to your local SPACEGIRL 💫">
|
||||||
|
|
||||||
<link rel="stylesheet" href="/style/index.css">
|
<link rel="stylesheet" href="/style/index.css">
|
||||||
<script type="module" src="/script/main.js" defer></script>
|
<link rel="me" href="https://ice.arimelody.me/@ari">
|
||||||
<link rel="me" href="https://wetdry.world/@ari">
|
<link rel="me" href="https://wetdry.world/@ari">
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
@ -153,6 +153,9 @@
|
||||||
<a href="https://mae.wtf" target="_blank">
|
<a href="https://mae.wtf" target="_blank">
|
||||||
<img src="/img/buttons/mae.png" alt="vimae web button">
|
<img src="/img/buttons/mae.png" alt="vimae web button">
|
||||||
</a>
|
</a>
|
||||||
|
<a href="https://zvava.org" target="_blank">
|
||||||
|
<img src="/img/buttons/zvava.png" alt="zvava web button">
|
||||||
|
</a>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
@ -177,5 +180,7 @@
|
||||||
<img src="/img/buttons/misc/epicblazed.png" alt="epic blazed">
|
<img src="/img/buttons/misc/epicblazed.png" alt="epic blazed">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script type="module" src="/script/main.js" defer></script>
|
||||||
</main>
|
</main>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -25,14 +25,14 @@
|
||||||
<meta name="twitter:image:alt" content="Cover art for "{{.Title}}"">
|
<meta name="twitter:image:alt" content="Cover art for "{{.Title}}"">
|
||||||
|
|
||||||
<link rel="stylesheet" href="/style/music-gateway.css">
|
<link rel="stylesheet" href="/style/music-gateway.css">
|
||||||
<script type="module" src="/script/music-gateway.js" defer></script>
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<main>
|
<main>
|
||||||
<div id="background" data-url="{{.ResolveArtwork}}"></div>
|
<div id="background" style="background-image: url({{.ResolveArtwork}})"></div>
|
||||||
|
|
||||||
<a href="/music" id="go-back" title="back to arimelody.me">back to arimelody.me</a>
|
<!-- <a href="/music" hx-boost="true" hx-target="body" hx-swap="innerHTML" id="go-back" title="back to arimelody.me">back to arimelody.me</a> -->
|
||||||
|
<a href="/music" hx-boost="true" hx-target="body" hx-swap="innerHTML" id="go-back" title="back to arimelody.me"><</a>
|
||||||
<br><br>
|
<br><br>
|
||||||
|
|
||||||
<div id="music-container">
|
<div id="music-container">
|
||||||
|
@ -161,5 +161,7 @@
|
||||||
<!-- <% } %> -->
|
<!-- <% } %> -->
|
||||||
<!-- </div> -->
|
<!-- </div> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script type="module" src="/script/music-gateway.js" defer></script>
|
||||||
</main>
|
</main>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
<meta property="og:description" content="music from your local SPACEGIRL 💫">
|
<meta property="og:description" content="music from your local SPACEGIRL 💫">
|
||||||
|
|
||||||
<link rel="stylesheet" href="/style/music.css">
|
<link rel="stylesheet" href="/style/music.css">
|
||||||
<script type="module" src="/script/music.js" defer></script>
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
|
@ -23,11 +22,11 @@
|
||||||
|
|
||||||
<div id="music-container">
|
<div id="music-container">
|
||||||
{{range $Album := .}}
|
{{range $Album := .}}
|
||||||
<div class="music" id="{{$Album.Id}}" hx-get="/music/{{$Album.Id}}" hx-trigger="click" hx-target="main" hx-swap="outerHTML" hx-push-url="true">
|
<div class="music" id="{{$Album.Id}}" swap-url="/music/{{$Album.Id}}">
|
||||||
<div class="music-artwork">
|
<div class="music-artwork">
|
||||||
<img src="{{$Album.ResolveArtwork}}" alt="{{$Album.Title}} artwork" width="128">
|
<img src="{{$Album.ResolveArtwork}}" alt="{{$Album.Title}} artwork" width="128">
|
||||||
</div>
|
</div>
|
||||||
<div class="music-details" hx-boost="true" hx-target="main" hx-swap="outerHTML">
|
<div class="music-details">
|
||||||
<a href="/music/{{$Album.Id}}"><h1 class="music-title">{{$Album.Title}}</h1></a>
|
<a href="/music/{{$Album.Id}}"><h1 class="music-title">{{$Album.Title}}</h1></a>
|
||||||
<h2 class="music-artist">{{$Album.PrintPrimaryArtists false}}</h2>
|
<h2 class="music-artist">{{$Album.PrintPrimaryArtists false}}</h2>
|
||||||
<h3 class="music-type-{{.ResolveType}}">{{$Album.ResolveType}}</h3>
|
<h3 class="music-type-{{.ResolveType}}">{{$Album.ResolveType}}</h3>
|
||||||
|
@ -85,5 +84,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a href="#" id="backtotop">back to top</a>
|
<a href="#" id="backtotop">back to top</a>
|
||||||
|
|
||||||
|
<script type="module" src="/script/music.js" defer></script>
|
||||||
</main>
|
</main>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
Loading…
Reference in a new issue