Ethan Dalool
e4f686c86f
I've moved the thumbnails around many times over this project and hopefully it doesn't happen too many more. Once the database has tens of thousands of items, the thumbnails start to become the biggest headache on the disk. Backing up, restoring, and sharding files per directory are slower and more effortful with separate files. In the db means the db is a larger file, but this is disk space that was already getting used anyway. Now it's simpler and has atomic transactions.
324 lines
9.6 KiB
JavaScript
324 lines
9.6 KiB
JavaScript
const cards = {};
|
|
|
|
/******************************************************************************/
|
|
cards.albums = {};
|
|
|
|
cards.albums.drag_start =
|
|
function drag_start(event)
|
|
{
|
|
const album_card = event.target.closest(".album_card");
|
|
event.dataTransfer.setData("text/plain", album_card.id);
|
|
}
|
|
|
|
cards.albums.drag_end =
|
|
function drag_end(event)
|
|
{
|
|
}
|
|
|
|
cards.albums.drag_over =
|
|
function drag_over(event)
|
|
{
|
|
event.preventDefault();
|
|
}
|
|
|
|
cards.albums.drag_drop =
|
|
function drag_drop(event)
|
|
{
|
|
const child = document.getElementById(event.dataTransfer.getData("text"));
|
|
const child_id = child.dataset.id;
|
|
const parent = event.currentTarget;
|
|
const parent_id = parent.dataset.id;
|
|
event.dataTransfer.clearData();
|
|
|
|
if (child_id == parent_id)
|
|
{
|
|
return;
|
|
}
|
|
|
|
let prompt;
|
|
if (parent_id === "root")
|
|
{
|
|
const child_title = child.querySelector('.album_card_title').textContent.trim();
|
|
prompt = `Remove child\n${child_title}?`;
|
|
}
|
|
else
|
|
{
|
|
const child_title = child.querySelector('.album_card_title').textContent.trim();
|
|
const parent_title = parent.querySelector('.album_card_title').textContent.trim();
|
|
prompt = `Move\n${child_title}\ninto\n${parent_title}?`;
|
|
}
|
|
|
|
if (! confirm(prompt))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (parent_id === "root")
|
|
{
|
|
api.albums.remove_child(ALBUM_ID, child_id, common.refresh_or_alert);
|
|
}
|
|
else if (ALBUM_ID)
|
|
{
|
|
api.albums.add_child(parent_id, child_id, null);
|
|
api.albums.remove_child(ALBUM_ID, child_id, common.refresh_or_alert);
|
|
}
|
|
else
|
|
{
|
|
api.albums.add_child(parent_id, child_id, common.refresh_or_alert);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
cards.bookmarks = {};
|
|
|
|
cards.bookmarks.create =
|
|
function create(bookmark, add_author, add_delete_button, add_url_element)
|
|
{
|
|
const bookmark_card = document.createElement("div");
|
|
bookmark_card.className = "bookmark_card"
|
|
bookmark_card.dataset.id = bookmark.id;
|
|
|
|
const h2 = document.createElement("h2");
|
|
const bookmark_title = document.createElement("a");
|
|
bookmark_title.className = "bookmark_title";
|
|
bookmark_title.href = bookmark.url;
|
|
bookmark_title.innerText = bookmark.display_name;
|
|
h2.appendChild(bookmark_title);
|
|
bookmark_card.appendChild(h2);
|
|
|
|
// The URL element is always display:none, but its presence is useful in
|
|
// facilitating the Editor object. If this bookmark will not be editable,
|
|
// there is no need for it.
|
|
if (add_url_element)
|
|
{
|
|
const bookmark_url = document.createElement("a");
|
|
bookmark_url.className = "bookmark_url";
|
|
bookmark_url.href = bookmark.url;
|
|
bookmark_url.innerText = bookmark.url;
|
|
bookmark_card.appendChild(bookmark_url);
|
|
}
|
|
|
|
// If more tools are added, this will become an `or`.
|
|
// I just want to have the structure in place now.
|
|
if (add_delete_button)
|
|
{
|
|
const bookmark_toolbox = document.createElement("div");
|
|
bookmark_toolbox.className = "bookmark_toolbox"
|
|
bookmark_card.appendChild(bookmark_toolbox);
|
|
|
|
if (add_delete_button)
|
|
{
|
|
const delete_button = document.createElement("button");
|
|
delete_button.className = "red_button button_with_confirm";
|
|
delete_button.dataset.onclick = "return delete_bookmark_form(event);";
|
|
delete_button.dataset.prompt = "Delete Bookmark?";
|
|
delete_button.dataset.cancelClass = "gray_button";
|
|
delete_button.innerText = "Delete";
|
|
bookmark_toolbox.appendChild(delete_button);
|
|
common.init_button_with_confirm(delete_button);
|
|
}
|
|
}
|
|
|
|
return bookmark_card;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
cards.photos = {};
|
|
|
|
cards.photos.MIMETYPE_THUMBNAILS = {
|
|
"svg": "svg",
|
|
|
|
"application/zip": "archive",
|
|
"application/x-tar": "archive",
|
|
|
|
"archive": "archive",
|
|
"audio": "audio",
|
|
"image": "image",
|
|
"video": "video",
|
|
"text": "txt",
|
|
};
|
|
|
|
cards.photos.file_link =
|
|
function file_link(photo, short)
|
|
{
|
|
if (short)
|
|
{
|
|
return `/photo/${photo.id}/download/${photo.id}${photo.dot_extension}`;
|
|
}
|
|
const basename = escape(photo.filename);
|
|
return `/photo/${photo.id}/download/${basename}`;
|
|
}
|
|
|
|
cards.photos.create =
|
|
function create(photo, view)
|
|
{
|
|
if (view !== "list" && view !== "grid")
|
|
{
|
|
view = "grid";
|
|
}
|
|
|
|
const photo_card = document.createElement("div");
|
|
photo_card.id = `photo_card_${photo.id}`;
|
|
photo_card.dataset.id = photo.id;
|
|
photo_card.className = `photo_card photo_card_${view} photo_card_unselected`
|
|
if (photo.searchhidden)
|
|
{
|
|
photo_card.classList.add("photo_card_searchhidden");
|
|
}
|
|
photo_card.ondragstart = "return cards.photos.drag_start(event);";
|
|
photo_card.ondragend = "return cards.photos.drag_end(event);";
|
|
photo_card.ondragover = "return cards.photos.drag_over(event);";
|
|
photo_card.ondrop = "return cards.photos.drag_drop(event);";
|
|
photo_card.draggable = true;
|
|
|
|
const photo_card_filename = document.createElement("div");
|
|
photo_card_filename.className = "photo_card_filename";
|
|
const filename_link = document.createElement("a");
|
|
filename_link.href = `/photo/${photo.id}`;
|
|
filename_link.draggable = false;
|
|
filename_link.innerText = photo.filename;
|
|
photo_card_filename.appendChild(filename_link);
|
|
photo_card.appendChild(photo_card_filename);
|
|
|
|
const photo_card_metadata = document.createElement("span");
|
|
photo_card_metadata.className = "photo_card_metadata";
|
|
const metadatas = [];
|
|
if (photo.width)
|
|
{
|
|
metadatas.push(`${photo.width}×${photo.height}`);
|
|
}
|
|
if (photo.duration)
|
|
{
|
|
metadatas.push(`${photo.duration_string}`);
|
|
}
|
|
photo_card_metadata.innerHTML = common.join_and_trail(metadatas, ", ");
|
|
const filesize_file_link = document.createElement("a");
|
|
filesize_file_link.href = cards.photos.file_link(photo);
|
|
filesize_file_link.target = "_blank";
|
|
filesize_file_link.draggable = false;
|
|
filesize_file_link.innerText = photo.bytes_string;
|
|
photo_card_metadata.append(filesize_file_link);
|
|
photo_card.appendChild(photo_card_metadata);
|
|
|
|
if (view == "grid")
|
|
{
|
|
let thumbnail_src;
|
|
if (photo.has_thumbnail)
|
|
{
|
|
thumbnail_src = `/photo/${photo.id}/thumbnail/${photo.id}.jpg`;
|
|
}
|
|
else
|
|
{
|
|
thumbnail_src =
|
|
cards.photos.MIMETYPE_THUMBNAILS[photo.extension] ||
|
|
cards.photos.MIMETYPE_THUMBNAILS[photo.mimetype] ||
|
|
cards.photos.MIMETYPE_THUMBNAILS[photo.simple_mimetype] ||
|
|
"other";
|
|
thumbnail_src = `/static/basic_thumbnails/${thumbnail_src}.png`;
|
|
}
|
|
|
|
const photo_card_thumbnail = document.createElement("a");
|
|
photo_card_thumbnail.className = "photo_card_thumbnail";
|
|
photo_card_thumbnail.target = "_blank";
|
|
photo_card_thumbnail.href = `/photo/${photo.id}`;
|
|
photo_card_thumbnail.draggable = false;
|
|
const thumbnail_img = document.createElement("img");
|
|
thumbnail_img.loading = "lazy";
|
|
thumbnail_img.src = thumbnail_src;
|
|
thumbnail_img.draggable = false;
|
|
photo_card_thumbnail.appendChild(thumbnail_img);
|
|
photo_card.appendChild(photo_card_thumbnail);
|
|
}
|
|
|
|
let tag_names_title = [];
|
|
let tag_names_inner = "";
|
|
for (const tag of photo.tags)
|
|
{
|
|
tag_names_title.push(tag.name);
|
|
tag_names_inner = "T";
|
|
}
|
|
const photo_card_tags = document.createElement("span");
|
|
photo_card_tags.className = "photo_card_tags";
|
|
photo_card_tags.title = tag_names_title.join(",");
|
|
photo_card_tags.innerText = tag_names_inner;
|
|
photo_card.appendChild(photo_card_tags);
|
|
|
|
if (window.photo_clipboard !== undefined)
|
|
{
|
|
const clipboard_checkbox = photo_clipboard.give_checkbox(photo_card);
|
|
photo_clipboard.apply_check(clipboard_checkbox);
|
|
}
|
|
|
|
return photo_card;
|
|
}
|
|
|
|
cards.photos.drag_start =
|
|
function drag_start(event)
|
|
{
|
|
}
|
|
|
|
cards.photos.drag_end =
|
|
function drag_end(event)
|
|
{
|
|
}
|
|
|
|
cards.photos.drag_over =
|
|
function drag_over(event)
|
|
{
|
|
}
|
|
|
|
cards.photos.drag_drop =
|
|
function drag_drop(event)
|
|
{
|
|
}
|
|
|
|
cards.photos.photo_contextmenu = null;
|
|
cards.photos.set_contextmenu =
|
|
function set_contextmenu(element, build_function)
|
|
{
|
|
element.classList.add("photo_card_contextmenu");
|
|
element.classList.add("contextmenu");
|
|
element.onclick = "event.stopPropagation(); return;";
|
|
cards.photos.photo_contextmenu = element;
|
|
cards.photos.build_photo_contextmenu = build_function;
|
|
contextmenus.hide_open_menus();
|
|
}
|
|
|
|
cards.photos.right_clicked_photo = null;
|
|
cards.photos.photo_rightclick =
|
|
function photo_rightclick(event)
|
|
{
|
|
if (["A", "IMG"].includes(event.target.tagName))
|
|
{
|
|
return true;
|
|
}
|
|
if (cards.photos.photo_contextmenu === null)
|
|
{
|
|
return true;
|
|
}
|
|
if (event.ctrlKey || event.shiftKey || event.altKey)
|
|
{
|
|
return true;
|
|
}
|
|
const photo_card = event.target.closest(".photo_card");
|
|
if (! photo_card)
|
|
{
|
|
cards.photos.right_clicked_photo = null;
|
|
contextmenus.hide_open_menus();
|
|
return true;
|
|
}
|
|
if (contextmenus.menu_is_open())
|
|
{
|
|
contextmenus.hide_open_menus();
|
|
}
|
|
cards.photos.right_clicked_photo = photo_card;
|
|
const menu = cards.photos.photo_contextmenu;
|
|
cards.photos.build_photo_contextmenu(photo_card, menu);
|
|
setTimeout(() => {contextmenus.show_menu(event, menu);}, 0);
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
return false;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
cards.tags = {};
|