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
|
||||
follow_symlink = false
|
||||
full_bin = ""
|
||||
include_dir = []
|
||||
include_dir = [".", "views", "api"]
|
||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||
include_file = []
|
||||
kill_delay = "0s"
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
version: '3.9'
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:16.1-alpine3.18
|
||||
container_name: arimelody.me-db
|
||||
ports:
|
||||
- 5432:5432
|
||||
volumes:
|
||||
- arimelody-db:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_DB: arimelody
|
||||
POSTGRES_USER: arimelody
|
||||
POSTGRES_PASSWORD: fuckingpassword
|
||||
db:
|
||||
image: postgres:16.1-alpine3.18
|
||||
container_name: arimelody.me-db
|
||||
ports:
|
||||
- 5432:5432
|
||||
volumes:
|
||||
- arimelody-db:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_DB: arimelody
|
||||
POSTGRES_USER: arimelody
|
||||
POSTGRES_PASSWORD: fuckingpassword
|
||||
|
||||
volumes:
|
||||
arimelody-db:
|
||||
external: true
|
||||
arimelody-db:
|
||||
external: true
|
||||
|
|
64
main.go
64
main.go
|
@ -6,7 +6,6 @@ import (
|
|||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -37,7 +36,7 @@ var base_template = template.Must(template.ParseFiles(
|
|||
"views/footer.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) {
|
||||
now := time.Now()
|
||||
|
@ -61,25 +60,27 @@ func handle_request(writer http.ResponseWriter, req *http.Request) {
|
|||
uri := req.URL.Path
|
||||
start_time := time.Now()
|
||||
|
||||
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!
|
||||
if hx_request && len(req.Header["Referer"]) > 0 && len(req.Header["Hx-Current-Url"]) > 0 {
|
||||
regex := regexp.MustCompile(`https?:\/\/[^\/]+`)
|
||||
current_location := regex.ReplaceAllString(req.Header["Hx-Current-Url"][0], "")
|
||||
if current_location == req.URL.Path {
|
||||
writer.WriteHeader(204);
|
||||
return
|
||||
}
|
||||
}
|
||||
// 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!
|
||||
// if hx_request && len(req.Header["Referer"]) > 0 && len(req.Header["Hx-Current-Url"]) > 0 {
|
||||
// regex := regexp.MustCompile(`https?:\/\/[^\/]+`)
|
||||
// current_location := regex.ReplaceAllString(req.Header["Hx-Current-Url"][0], "")
|
||||
// if current_location == req.URL.Path {
|
||||
// writer.WriteHeader(204);
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
code := func(writer http.ResponseWriter, req *http.Request) int {
|
||||
var root *template.Template
|
||||
if hx_request {
|
||||
root = template.Must(htmx_template.Clone())
|
||||
} else {
|
||||
root = template.Must(base_template.Clone())
|
||||
}
|
||||
// var root *template.Template
|
||||
// if hx_request {
|
||||
// root = template.Must(htmx_template.Clone())
|
||||
// } else {
|
||||
// root = template.Must(base_template.Clone())
|
||||
// }
|
||||
|
||||
var root = template.Must(base_template.Clone())
|
||||
|
||||
if req.URL.Path == "/" {
|
||||
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 static_handler(writer, req)
|
||||
return static_handler(writer, req, root)
|
||||
}(writer, req)
|
||||
|
||||
log_request(req, code, start_time)
|
||||
|
@ -126,8 +127,7 @@ func music_gateway_handler(writer http.ResponseWriter, req *http.Request, root *
|
|||
// return
|
||||
release, ok := music.GetRelease(id)
|
||||
if !ok {
|
||||
http.Error(writer, "404 not found", http.StatusNotFound)
|
||||
return 404
|
||||
return handle_not_found(writer, req, root)
|
||||
}
|
||||
gateway_template := template.Must(root.ParseFiles("views/music-gateway.html"))
|
||||
err := gateway_template.Execute(writer, release)
|
||||
|
@ -138,14 +138,13 @@ func music_gateway_handler(writer http.ResponseWriter, req *http.Request, root *
|
|||
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:]
|
||||
|
||||
// check the file's metadata
|
||||
info, err := os.Stat(filename)
|
||||
if err != nil {
|
||||
http.Error(writer, "404 not found", http.StatusNotFound)
|
||||
return 404
|
||||
return handle_not_found(writer, req, root)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock
|
||||
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 hamburger = document.getElementById("header-links-toggle");
|
||||
|
||||
function toggle_header_links() {
|
||||
header_links.classList.toggle("open");
|
||||
}
|
||||
|
||||
document.addEventListener("click", event => {
|
||||
if (!header_links.contains(event.target) && !hamburger.contains(event.target) && !header_links.href) {
|
||||
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 "./accessibility.js";
|
||||
import "./config.js";
|
||||
|
||||
function type_out(e) {
|
||||
const text = e.innerText;
|
||||
|
@ -34,46 +34,50 @@ function type_out(e) {
|
|||
}
|
||||
|
||||
function fill_list(list) {
|
||||
const items = list.querySelectorAll("li a, li span");
|
||||
items.innerText = "";
|
||||
const delay = 100;
|
||||
const items = list.querySelectorAll("li a, li span");
|
||||
items.innerText = "";
|
||||
const delay = 100;
|
||||
|
||||
items.forEach((item, iter) => {
|
||||
item.style.animationDelay = `${iter * delay}ms`;
|
||||
item.style.animationPlayState = "playing";
|
||||
});
|
||||
items.forEach((item, iter) => {
|
||||
item.style.animationDelay = `${iter * delay}ms`;
|
||||
item.style.animationPlayState = "playing";
|
||||
});
|
||||
}
|
||||
|
||||
[...document.querySelectorAll("h1, h2, h3, h4, h5, h6")]
|
||||
.filter((e) => e.innerText != "")
|
||||
.forEach((e) => {
|
||||
type_out(e);
|
||||
});
|
||||
[...document.querySelectorAll("ol, ul")]
|
||||
.filter((e) => e.innerText != "")
|
||||
.forEach((e) => {
|
||||
fill_list(e);
|
||||
});
|
||||
function start() {
|
||||
[...document.querySelectorAll("h1, h2, h3, h4, h5, h6")]
|
||||
.filter((e) => e.innerText != "")
|
||||
.forEach((e) => {
|
||||
type_out(e);
|
||||
});
|
||||
[...document.querySelectorAll("ol, ul")]
|
||||
.filter((e) => e.innerText != "")
|
||||
.forEach((e) => {
|
||||
fill_list(e);
|
||||
});
|
||||
|
||||
document.addEventListener("htmx:afterSwap", async event => {
|
||||
const res = await event.detail.xhr.response;
|
||||
var new_head = res.substring(res.indexOf("<head>")+1, res.indexOf("</head>"));
|
||||
if (new_head) {
|
||||
document.head.innerHTML = new_head;
|
||||
}
|
||||
window.scrollY = 0;
|
||||
});
|
||||
document.addEventListener("htmx:afterSwap", async event => {
|
||||
const res = await event.detail.xhr.response;
|
||||
var new_head = res.substring(res.indexOf("<head>")+1, res.indexOf("</head>"));
|
||||
if (new_head) {
|
||||
document.head.innerHTML = new_head;
|
||||
}
|
||||
window.scrollY = 0;
|
||||
});
|
||||
|
||||
const top_button = document.getElementById("backtotop");
|
||||
window.onscroll = () => {
|
||||
if (!top_button) return;
|
||||
const btt_threshold = 100;
|
||||
if (
|
||||
document.body.scrollTop > btt_threshold ||
|
||||
const top_button = document.getElementById("backtotop");
|
||||
window.onscroll = () => {
|
||||
if (!top_button) return;
|
||||
const btt_threshold = 100;
|
||||
if (
|
||||
document.body.scrollTop > btt_threshold ||
|
||||
document.documentElement.scrollTop > btt_threshold
|
||||
) {
|
||||
top_button.classList.add("active");
|
||||
} else {
|
||||
top_button.classList.remove("active");
|
||||
) {
|
||||
top_button.classList.add("active");
|
||||
} else {
|
||||
top_button.classList.remove("active");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start();
|
||||
|
|
|
@ -1,44 +1,24 @@
|
|||
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() {
|
||||
const upcomingTags = document.querySelectorAll("#type.upcoming");
|
||||
for (var i = 0; i < upcomingTags.length; i++) {
|
||||
const tag = upcomingTags[i];
|
||||
const upcoming_tags = document.querySelectorAll("#type.upcoming");
|
||||
for (var i = 0; i < upcoming_tags.length; i++) {
|
||||
const tag = upcoming_tags[i];
|
||||
const chars = tag.innerText.split("");
|
||||
const result = chars.map((c, i) => `<span style="animation-delay: -${i * 100}ms;">${c}</span>`);
|
||||
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() {
|
||||
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_y = info_rect.y;
|
||||
const font_size = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||
|
@ -52,57 +32,38 @@ function update_extras_buttons() {
|
|||
if (scroll_diff <= 0) current = pair;
|
||||
})
|
||||
current.button.classList.add("active");
|
||||
}
|
||||
update_extras_buttons();
|
||||
|
||||
document.querySelectorAll("div#extras ul li a[href]").forEach(link => {
|
||||
link.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
location.replace(link.href);
|
||||
document.querySelectorAll("div#extras ul li a[href]").forEach(link => {
|
||||
link.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
location.replace(link.href);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* handling track previews (currently not implemented)
|
||||
|
||||
const previews = document.querySelectorAll("[id^=preview-]");
|
||||
for (const preview of previews) {
|
||||
preview.addEventListener("click", (e) => {
|
||||
if (e.target.classList.contains('playing')) {
|
||||
stopPreview(e.target);
|
||||
} else {
|
||||
playPreview(e.target);
|
||||
}
|
||||
function bind_go_back_btn() {
|
||||
const go_back_btn = document.getElementById("go-back")
|
||||
go_back_btn.innerText = "<";
|
||||
go_back_btn.addEventListener("click", () => {
|
||||
window.history.back();
|
||||
});
|
||||
preview.querySelector('audio').addEventListener("ended", () => { stopPreview(preview); });
|
||||
}
|
||||
|
||||
var stupidsounds = false;
|
||||
|
||||
function stopPreviews() {
|
||||
for (const preview of previews) stopPreview(preview);
|
||||
function bind_share_btn() {
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
||||
function playPreview(preview) {
|
||||
if (!stupidsounds) stopPreviews();
|
||||
const btn = preview.querySelector('i');
|
||||
btn.classList.remove("play", "fa-play");
|
||||
btn.classList.add("pause", "fa-pause");
|
||||
const audio = preview.querySelector('audio');
|
||||
audio.play();
|
||||
preview.classList.add('playing');
|
||||
function start() {
|
||||
bind_share_btn();
|
||||
bind_go_back_btn();
|
||||
apply_funny_bob_to_upcoming_tags();
|
||||
update_extras_buttons();
|
||||
}
|
||||
|
||||
function stopPreview(preview) {
|
||||
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();
|
||||
|
||||
*/
|
||||
start();
|
||||
|
|
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}}
|
||||
|
||||
<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>
|
||||
<!-- <meta name="htmx-config" content='{"htmx.config.scrollIntoViewOnBoost":false}'> -->
|
||||
<!-- <script type="application/javascript" src="/script/lib/htmx.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>
|
||||
|
||||
<body hx-ext="head-support">
|
||||
<body hx-ext="head-support, preload">
|
||||
{{template "header"}}
|
||||
|
||||
{{block "content" .}}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<header>
|
||||
<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="">
|
||||
<div id="header-text">
|
||||
<h1>ari melody</h1>
|
||||
|
@ -16,12 +16,12 @@
|
|||
<rect y="40" width="70" height="10" rx="5" fill="#eee" />
|
||||
</svg>
|
||||
</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>
|
||||
<a href="/">home</a>
|
||||
<a href="/" preload="mouseover">home</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/music">music</a>
|
||||
<a href="/music" preload="mouseover">music</a>
|
||||
</li>
|
||||
<li>
|
||||
<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 💫">
|
||||
|
||||
<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">
|
||||
{{end}}
|
||||
|
||||
|
@ -153,6 +153,9 @@
|
|||
<a href="https://mae.wtf" target="_blank">
|
||||
<img src="/img/buttons/mae.png" alt="vimae web button">
|
||||
</a>
|
||||
<a href="https://zvava.org" target="_blank">
|
||||
<img src="/img/buttons/zvava.png" alt="zvava web button">
|
||||
</a>
|
||||
|
||||
<hr>
|
||||
|
||||
|
@ -177,5 +180,7 @@
|
|||
<img src="/img/buttons/misc/epicblazed.png" alt="epic blazed">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<script type="module" src="/script/main.js" defer></script>
|
||||
</main>
|
||||
{{end}}
|
||||
|
|
|
@ -25,14 +25,14 @@
|
|||
<meta name="twitter:image:alt" content="Cover art for "{{.Title}}"">
|
||||
|
||||
<link rel="stylesheet" href="/style/music-gateway.css">
|
||||
<script type="module" src="/script/music-gateway.js" defer></script>
|
||||
{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<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>
|
||||
|
||||
<div id="music-container">
|
||||
|
@ -161,5 +161,7 @@
|
|||
<!-- <% } %> -->
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
|
||||
<script type="module" src="/script/music-gateway.js" defer></script>
|
||||
</main>
|
||||
{{end}}
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<meta property="og:description" content="music from your local SPACEGIRL 💫">
|
||||
|
||||
<link rel="stylesheet" href="/style/music.css">
|
||||
<script type="module" src="/script/music.js" defer></script>
|
||||
{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
|
@ -23,11 +22,11 @@
|
|||
|
||||
<div id="music-container">
|
||||
{{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">
|
||||
<img src="{{$Album.ResolveArtwork}}" alt="{{$Album.Title}} artwork" width="128">
|
||||
</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>
|
||||
<h2 class="music-artist">{{$Album.PrintPrimaryArtists false}}</h2>
|
||||
<h3 class="music-type-{{.ResolveType}}">{{$Album.ResolveType}}</h3>
|
||||
|
@ -85,5 +84,7 @@
|
|||
</div>
|
||||
|
||||
<a href="#" id="backtotop">back to top</a>
|
||||
|
||||
<script type="module" src="/script/music.js" defer></script>
|
||||
</main>
|
||||
{{end}}
|
||||
|
|
Loading…
Reference in a new issue