602 lines
18 KiB
JavaScript
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}`);
|
|
});
|
|
}
|
|
});
|