Files
trpic/web/scripts/artwork_id/load.js

602 lines
18 KiB
JavaScript

class MetaParser {
static parse() {
const result = {
artwork: null,
socials: [],
files: [],
posts: [],
tags: []
};
const metas = document.querySelectorAll("meta[name]");
metas.forEach(meta => {
const name = meta.getAttribute("name");
const data = this.metaToObject(meta);
if (name === "artwork") {
result.artwork = data;
} else if (name.startsWith("social-")) {
result.socials.push(data);
} else if (name.startsWith("file-")) {
result.files.push(data);
} else if (name.startsWith("post-")) {
result.posts.push(data);
} else if (name.startsWith("tag-")) {
result.tags.push(data);
}
});
return result;
}
static metaToObject(meta) {
const obj = {};
for (const attr of meta.attributes) {
let value = attr.value;
if (value === "true") {
value = true;
} else if (value === "false") {
value = false;
} else if (Number.isSafeInteger(value) && !isNaN(value) && value.trim() !== "") {
value = Number(value);
}
obj[attr.name] = value;
}
return obj;
}
}
document.addEventListener('DOMContentLoaded', () => {
const pathIndex = parseInt(window.location.pathname.split("/photo/").pop());
let data = MetaParser.parse(),
currentScale = 1.0,
maxScale = 8,
minScale = 0.5,
isDragging = false,
startX = 0,
startY = 0,
currentX = 0,
currentY = 0,
index = pathIndex ? pathIndex : 0;
let preview = document.getElementById("preview");
preview.style.display = "flex";
let previewImg = document.getElementById("preview-img");
let prevBtn = document.getElementById("prev-btn");
prevBtn.addEventListener("click", showPrevImage.bind(this));
let nextBtn = document.getElementById("next-btn");
nextBtn.addEventListener("click", showNextImage.bind(this));
document.addEventListener("keydown", handleKeyDown.bind(this));
let imageWrapper = document.getElementById("image-wrapper");
imageWrapper.addEventListener("wheel", handleWheel.bind(this));
imageWrapper.addEventListener("mousedown", startDrag.bind(this));
let navControls = document.getElementById("nav-controls");
let downloadBtn = document.getElementById("download-btn");
downloadBtn.addEventListener("click", downloadImage.bind(this));
let deleteBtn = document.getElementById("delete-btn");
deleteBtn.addEventListener("click", deleteImage.bind(this));
let sidebar = document.getElementById("sidebar");
let sidebarBtn = document.getElementById("sidebar-btn");
sidebarBtn.addEventListener("click", toggleSidebar.bind(this));
let sidebarImageAuthor = document.getElementById("img-author");
let sidebarImageSource = document.getElementById("img-source");
let sidebarImageTag = document.getElementById("img-tag");
let sidebarImageInfo = document.getElementById("img-info");
let sidebarImageSetting = document.getElementById("img-setting");
let sidebarImageSettingSselect = document.getElementById("sensitive-select");
let sidebarImageSettingPselect = document.getElementById("private-select");
let sidebarImageSettingButton = document.getElementById("setting-button");
sidebarImageSettingButton.addEventListener("click", patchArtwork.bind(this));
let sidebarImageSettingPostInput = document.getElementById("post-input");
let sidebarImageSettingPostButton = document.getElementById("post-button");
sidebarImageSettingPostButton.addEventListener("click", postSource.bind(this));
let sidebarImageSettingUploadFileButton = document.getElementById("upload-file-button");
let sidebarImageSettingUploadURLInput = document.getElementById("upload-url-input");
let sidebarImageSettingUploadButton = document.getElementById("upload-button");
sidebarImageSettingUploadButton.addEventListener("click", postArtworkFile.bind(this));
let SettingSubmitting = false;
let on = false;
let status = localStorage.getItem("sidebar_on")
if (status) {
if (status === "true" && window.innerWidth > 640) {
on = true;
}
} else if (window.innerWidth > 1600) {
on = true;
}
if (on) openSidebar();
if (document.cookie.includes("_trpic_user")) {
deleteBtn.style.display = "flex";
sidebarImageSetting.style.display = "block";
}
previewImg.style.opacity = 0;
prevBtn.style.opacity = 0;
nextBtn.style.opacity = 0;
showImage();
function postArtworkFile() {
const link = sidebarImageSettingUploadURLInput.value;
const files = sidebarImageSettingUploadFileButton.files;
if (link == "" && files.length == 0) {
alert("请填入链接或文件");
return;
}
SettingSubmitting = true;
sidebarImageSettingUploadButton.style.backgroundColor = "rgb(50, 50, 50)";
if (link) {
const formData = new FormData();
formData.append("link", link);
fetch(`/api/artworks/${data.artwork.id}/files`, {
method: "POST",
body: formData,
})
.then(res => {
res.json().then(resjson => {
switch (res.status) {
case 201:
data.files = resjson.edges.files;
showImage();
break;
case 401:
alert("提交失败:需要验证");
break;
default:
alert(`提交失败:${resjson.msg}: ${resjson.error}`);
break;
}
})
})
.finally(() => {
sidebarImageSettingUploadButton.style.backgroundColor = "rgb(50, 50, 50)";
sidebarImageSettingUploadURLInput.value = "";
sidebarImageSettingUploadFileButton.value = "";
SettingSubmitting = false;
})
.catch(error => {
alert(`请求失败: ${error}`);
});
} else {
for (const file of files) {
const formData = new FormData();
formData.append("file", file);
fetch(`/api/artworks/${data.artwork.id}/files`, {
method: "POST",
body: formData,
})
.then(res => {
res.json().then(resjson => {
switch (res.status) {
case 201:
data.files = resjson.edges.files;
showImage();
break;
case 401:
alert("提交失败:需要验证");
break;
default:
alert(`提交失败:${resjson.msg}: ${resjson.error}`);
break;
}
})
})
.finally(() => {
sidebarImageSettingUploadButton.style.backgroundColor = "rgb(50, 50, 50)";
sidebarImageSettingUploadURLInput.value = "";
sidebarImageSettingUploadFileButton.value = "";
SettingSubmitting = false;
})
.catch(error => {
alert(`请求失败: ${error}`);
});
}
}
}
function postSource() {
const link = sidebarImageSettingPostInput.value;
if (link == "" || SettingSubmitting == true) return;
SettingSubmitting = true;
sidebarImageSettingPostButton.style.backgroundColor = "rgb(50, 80, 80)";
const formData = new FormData();
formData.append("link", link);
fetch(`/api/artworks/${data.artwork.id}/posts`, {
method: "POST",
body: formData,
})
.then(res => {
res.json().then(resjson => {
switch (res.status) {
case 200:
data.posts = resjson.edges.posts;
data.socials = resjson.edges.socials;
data.tags = resjson.edges.tags;
updateInfoBar();
break;
case 401:
alert("提交失败:需要验证");
break;
case 409:
if (confirm("此来源已链接到另一个作品,前往查看?")) {
window.location.href = `/artworks/${resjson.artwork_id}`;
}
break;
default:
alert(`提交失败:${resjson.msg}: ${resjson.error}`);
break;
}
})
})
.finally(() => {
sidebarImageSettingPostButton.style.backgroundColor = "rgb(50, 50, 50)";
sidebarImageSettingPostInput.value = "";
SettingSubmitting = false;
})
.catch(error => {
alert(`请求失败: ${error}`);
});
}
function patchArtwork() {
if (SettingSubmitting) return;
SettingSubmitting = true;
let Svalue = sidebarImageSettingSselect.value;
if (Svalue < 0 || Svalue > 5) {
SettingSubmitting = false;
return;
}
let Pvalue = sidebarImageSettingPselect.value;
const formData = new FormData();
formData.append("private", Pvalue);
formData.append("sensitive", Svalue);
sidebarImageSettingButton.style.backgroundColor = "rgb(50, 80, 80)";
fetch(`/api/artworks/${data.artwork.id}`, {
method: "PATCH",
body: formData,
})
.then(res => {
res.json().then(resjson => {
switch (res.status) {
case 200:
data.artwork.sensitive = resjson.sensitive ? resjson.sensitive : 0;
data.artwork.private = resjson.private ? true : false;
updateInfoBar();
break;
case 401:
alert("提交失败:需要验证");
break;
default:
alert(`提交失败:${resjson.msg}: ${resjson.error}`);
break;
}
})
})
.finally(() => {
SettingSubmitting = false;
setInterval(() => {
sidebarImageSettingButton.style.backgroundColor = "rgb(50, 50, 50)";
}, 1000)
})
.catch(error => {
alert(`请求失败: ${error}`);
});
}
function showImage() {
previewImg.style.opacity = 1;
resetTransform();
let image = data.files[index];
previewImg.src = `/files/${image.id_str}.${image.ext}`;
updateNavButtons();
updateInfoBar();
previewImg.onerror = () => {
alert("Failed to load image");
};
}
function showPrevImage() {
if (index > 0) {
index = index - 1;
showImage();
}
}
function showNextImage() {
if (index < data.files.length - 1) {
index = index + 1;
showImage();
}
}
function handleKeyDown(e) {
if (document.activeElement === sidebarImageSettingPostInput || document.activeElement === sidebarImageSettingUploadURLInput) return;
switch (e.key) {
case "Escape":
if (sidebar.classList.contains("open")) {
closeSidebar();
} else {
window.history.back();
}
break;
case "ArrowRight":
showNextImage();
break;
case "ArrowLeft":
showPrevImage();
break;
case "b":
if (e.ctrlKey) {
toggleSidebar();
}
break;
}
}
function handleWheel(e) {
e.preventDefault();
const delta = e.deltaY > 0 ? currentScale < 2 ? -0.1 : -0.2 : currentScale < 2 ? 0.1 : 0.2;
const newScale = parseFloat((currentScale + delta).toFixed(10));
if (newScale >= minScale && newScale <= maxScale) {
const ratio = newScale / currentScale;
const rect = imageWrapper.getBoundingClientRect();
const offsetX = e.clientX - (rect.left + rect.width / 2);
const offsetY = e.clientY - (rect.top + rect.height / 2);
currentX = offsetX * (1 - ratio) + currentX * ratio;
currentY = offsetY * (1 - ratio) + currentY * ratio;
currentScale = newScale;
updateImageTransform();
}
}
function startDrag(e) {
if (e.button !== 0) return;
e.preventDefault();
e.stopPropagation();
isDragging = true;
startX = e.clientX - currentX;
startY = e.clientY - currentY;
imageWrapper.classList.add("dragging");
bindDragEvents();
}
function doDrag(e) {
if (!isDragging) return;
e.preventDefault();
e.stopPropagation();
currentX = e.clientX - startX;
currentY = e.clientY - startY;
updateImageTransform();
}
function endDrag(e) {
if (!isDragging) return;
if (e) e.stopPropagation();
isDragging = false;
imageWrapper.classList.remove("dragging");
unbindDragEvents();
}
function bindDragEvents() {
imageWrapper.addEventListener("mousemove", doDrag.bind(this));
imageWrapper.addEventListener("mouseup", endDrag.bind(this));
imageWrapper.addEventListener("mouseleave", endDrag.bind(this));
}
function unbindDragEvents() {
imageWrapper.removeEventListener("mousemove", doDrag.bind(this));
imageWrapper.removeEventListener("mouseup", endDrag.bind(this));
imageWrapper.removeEventListener("mouseleave", endDrag.bind(this));
}
function resetTransform() {
currentScale = 1.0;
currentX = 0;
currentY = 0;
updateImageTransform();
}
function updateImageTransform() {
previewImg.style.transform = `translate(${currentX}px, ${currentY}px) scale(${currentScale})`;
}
function updateNavButtons() {
prevBtn.style.opacity = index <= 0 ? "0" : "1";
prevBtn.style.pointerEvents = index <= 0 ? "none" : "auto";
nextBtn.style.opacity = index >= data.files.length - 1 ? "0" : "1";
nextBtn.style.pointerEvents = index >= data.files.length - 1 ? "none" : "auto";
history.replaceState(null, null, `/artworks/${data.artwork.id}${index > 0 ? `/photo/${index}` : ""}`);
}
function updateInfoBar() {
sidebarImageAuthor.replaceChildren();
data.socials.forEach((social, index) => {
const author = document.createElement("p");
if (index > 0) {
author.style.borderTop = "1px solid #777";
}
const authorLink = document.createElement("a");
authorLink.textContent = `${social.platform}/${social.display_name}`;
authorLink.href = social.url;
authorLink.target = "_blank";
author.appendChild(authorLink);
sidebarImageAuthor.appendChild(author);
});
sidebarImageSource.replaceChildren();
data.posts.forEach((post, index) => {
const source = document.createElement("div");
if (index > 0) {
source.style.borderTop = "1px solid #777";
}
const link = document.createElement("a");
link.textContent = `${post.platform}/${post.post_id}`;
link.href = post.url;
link.target = "_blank";
source.appendChild(link);
if (post.time) {
const time = document.createElement("p");
time.classList.add("time");
time.textContent = post.time.substring(0, 19);
source.appendChild(time);
}
if (post.title) {
const title = document.createElement("p");
title.textContent = post.title;
source.appendChild(title);
}
if (post.text) {
const text = document.createElement("p");
text.classList.add("desc");
text.textContent = post.text;
// text.innerHTML = post.text;
source.appendChild(text);
}
sidebarImageSource.appendChild(source);
})
sidebarImageTag.replaceChildren();
data.tags.forEach(tag => {
const tagLink = document.createElement("a");
tagLink.textContent = `#${tag.text}`;
tagLink.href = `/artworks/?tags=${tag.id}`;
tagLink.target = "_blank";
sidebarImageTag.appendChild(tagLink);
})
const file = data.files[index];
const fileRes = document.createElement("p");
fileRes.textContent = `${file.width}*${file.height}px`;
const fileInfo = document.createElement("p");
fileInfo.textContent = `${file.size_str} · ${file.ext}`;
const id = document.createElement("p");
id.id = "img-id";
id.textContent = `${file.id_str} L${data.artwork.sensitive} ${data.artwork.private ? " (private)" : ""}`;
sidebarImageInfo.replaceChildren(fileRes, fileInfo, id);
if (document.cookie.includes("_trpic_user")) {
const a = document.createElement("a");
a.href = `/convert/${file.id_str}`;
a.target = "_blank";
a.textContent = `${file.quality ? `${file.quality}% ` : ""}Convert`;
sidebarImageInfo.appendChild(a);
}
sidebarImageSettingSselect.value = data.artwork.sensitive ? data.artwork.sensitive : 0;
sidebarImageSettingPselect.value = Boolean(data.artwork.private);
sidebarImageSettingPselect.style.backgroundColor = data.artwork.private ? "rgb(80, 70, 50)" : "rgb(50, 80, 50)";
}
function openSidebar() {
localStorage.setItem("sidebar_on", true);
sidebar.style.width = "30vw";
sidebar.classList.add("open");
navControls.classList.add("min-hide");
sidebarBtn.style.backgroundColor = "rgba(105, 105, 105, 0.3)";
}
function closeSidebar() {
localStorage.setItem("sidebar_on", false);
sidebar.style.width = "0px";
sidebar.classList.remove("open");
navControls.classList.remove("min-hide");
sidebarBtn.style.backgroundColor = "";
}
function toggleSidebar() {
if (sidebar.classList.contains("open")) {
closeSidebar();
} else {
openSidebar();
}
}
function downloadImage() {
const file = data.files[index];
const post = data.posts[0];
const link = document.createElement("a");
link.download = `${post.platform}_${post.post_id}_trpic_${file.id_str}.${file.ext}`;
link.href = `/files/${file.id_str}.${file.ext}`;
link.click();
link.remove();
}
function deleteImage() {
const file = data.files[index];
if (!confirm(`Are you sure you want to delete this ${file.id_str} image?`)) return;
fetch(`/api/files/${file.id_str}`, {
method: "DELETE",
})
.then((response) => {
if (response.ok) {
data.files.splice(index, 1);
index = 0;
showImage();
} else {
response.json().then((resjson) => {
if (resjson.code == 403) {
if (confirm(`This is last image of this artwork, are you want to delete this artwork?`)) deleteArtwork();
return
}
alert(`Failed to delete image: ${resjson.msg}`);
});
}
})
.catch((err) => {
console.error(err);
alert(`Failed to delete image: ${err}`);
});
}
function deleteArtwork() {
fetch(`/api/artworks/${data.artwork.id}`, {
method: "DELETE",
})
.then((response) => {
if (response.ok) {
window.location.href = "/";
} else {
response.json().then((resjson) => {
alert(`Failed to delete artwork: ${resjson.msg}`);
});
}
})
.catch((err) => {
console.error(err);
alert(`Failed to delete artwork: ${err}`);
});
}
});