Ethan Dalool
4c65ccaf68
I found that the strict heirarchy was not satisfying the situation where one tag is the intersection of two others, but we can only pick one as the parent For example, does red_jacket belong under clothes.red_clothes or clothes.jackets? A search for "red_clothes AND jackets" might give us someone wearing red pants and a black jacket, so this definitely needs to be a separate tag, but picking only one parent for it is not sufficient. Now, a search for red_clothes and a search for jackets will both find our red_jacket photo. The change also applies to Albums because why not, and I'm sure a similar case can be made. Unfortunately this means tags no longer have one true qualname. The concept of qualnames has not been completely phased out but it's in progress. This commit is very big because I was not sure for a long time whether to go through with it, and so much stuff had to change that I don't want to go back and figure out what could be grouped together.
339 lines
8.7 KiB
HTML
339 lines
8.7 KiB
HTML
<!DOCTYPE html5>
|
|
<html>
|
|
<head>
|
|
{% import "header.html" as header %}
|
|
{% import "clipboard_tray.html" as clipboard_tray %}
|
|
<title>Clipboard</title>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
<link rel="stylesheet" href="/static/css/common.css">
|
|
<link rel="stylesheet" href="/static/css/photo_card.css">
|
|
<link rel="stylesheet" href="/static/css/clipboard_tray.css">
|
|
<script src="/static/js/common.js"></script>
|
|
<script src="/static/js/hotkeys.js"></script>
|
|
<script src="/static/js/photoclipboard.js"></script>
|
|
|
|
<style>
|
|
body
|
|
{
|
|
display: grid;
|
|
grid-template-rows: auto 1fr;
|
|
grid-template-columns: 1fr 300px;
|
|
grid-template-areas:
|
|
"header header"
|
|
"left right";
|
|
}
|
|
#header
|
|
{
|
|
grid-area: header;
|
|
}
|
|
#left
|
|
{
|
|
word-break: break-word;
|
|
grid-area: left;
|
|
}
|
|
#right
|
|
{
|
|
position: fixed;
|
|
right: 8px;
|
|
bottom: 8px;
|
|
top: 30px;
|
|
width: 300px;
|
|
grid-area: right;
|
|
|
|
display: grid;
|
|
grid-template-rows: 75px 75px 75px 75px auto;
|
|
grid-template-areas:
|
|
"add_tag_area"
|
|
"remove_tag_area"
|
|
"refresh_metadata_area"
|
|
"searchhidden_area"
|
|
"message_area";
|
|
|
|
background-color: rgba(0, 0, 0, 0.1);
|
|
}
|
|
#add_tag_area
|
|
{
|
|
grid-area: add_tag_area;
|
|
margin: auto;
|
|
}
|
|
#remove_tag_area
|
|
{
|
|
grid-area: remove_tag_area;
|
|
margin: auto;
|
|
}
|
|
#refresh_metadata_area
|
|
{
|
|
grid-area: refresh_metadata_area;
|
|
margin: auto;
|
|
}
|
|
#searchhidden_area
|
|
{
|
|
grid-area: searchhidden_area;
|
|
margin: auto;
|
|
}
|
|
#message_area
|
|
{
|
|
grid-area: message_area;
|
|
margin: 8px;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
|
|
<body>
|
|
{{header.make_header(session=session)}}
|
|
<div id="left">
|
|
<span>The clipboard contains <span class="clipboard_count">0</span> items.</span>
|
|
<button id="clear_clipboard_button" class="red_button" onclick="clear_photo_clipboard()">Clear it.</button>
|
|
<div id="photo_card_holder">
|
|
</div>
|
|
</div>
|
|
<div id="right">
|
|
<div id="add_tag_area">
|
|
<input type="text" id="add_tag_textbox">
|
|
<button class="add_tag_button green_button" id="add_tag_button" onclick="submit_add_tag(add_remove_callback);">Add tag</button>
|
|
</div>
|
|
|
|
<div id="remove_tag_area">
|
|
<input type="text" id="remove_tag_textbox">
|
|
<button class="red_button" id="remove_tag_button" onclick="submit_remove_tag(add_remove_callback);">Remove tag</button>
|
|
</div>
|
|
|
|
<div id="refresh_metadata_area">
|
|
<button class="green_button" id="refresh_metadata_button" onclick="submit_refresh_metadata(refresh_metadata_callback);">Refresh metadata</button>
|
|
</div>
|
|
|
|
<div id="searchhidden_area">
|
|
<span>
|
|
<button class="yellow_button" id="set_searchhidden_button" onclick="submit_set_searchhidden(searchhidden_callback)">Searchhide</button>
|
|
<button class="yellow_button" id="unset_searchhidden_button" onclick="submit_unset_searchhidden(searchhidden_callback)">Unhide</button>
|
|
</span>
|
|
</div>
|
|
|
|
<div id="message_area">
|
|
</div>
|
|
</div>
|
|
</body>
|
|
|
|
|
|
<script type="text/javascript">
|
|
var divs = {};
|
|
var needed = new Set();
|
|
var holder = document.getElementById("photo_card_holder");
|
|
|
|
var add_box = document.getElementById("add_tag_textbox");
|
|
var add_button = document.getElementById("add_tag_button");
|
|
add_box.addEventListener("keyup", entry_with_history_hook);
|
|
bind_box_to_button(add_box, add_button);
|
|
|
|
var remove_box = document.getElementById("remove_tag_textbox");
|
|
var remove_button = document.getElementById("remove_tag_button");
|
|
remove_box.addEventListener("keyup", entry_with_history_hook);
|
|
bind_box_to_button(remove_box, remove_button);
|
|
|
|
function recalculate_needed()
|
|
{
|
|
needed = new Set();
|
|
photo_clipboard.forEach(function(photo_id)
|
|
{
|
|
if (!(photo_id in divs))
|
|
{
|
|
needed.add(photo_id);
|
|
}
|
|
});
|
|
}
|
|
|
|
function refresh_divs()
|
|
{
|
|
for (var photo_id in divs)
|
|
{
|
|
var photo_div = divs[photo_id];
|
|
var should_keep = photo_clipboard.has(photo_id);
|
|
var on_page = holder.contains(photo_div);
|
|
if (on_page && !should_keep)
|
|
{
|
|
holder.removeChild(photo_div)
|
|
}
|
|
if (!on_page && should_keep)
|
|
{
|
|
holder.appendChild(photo_div)
|
|
}
|
|
}
|
|
}
|
|
|
|
function request_more_divs()
|
|
{
|
|
if (needed.size == 0)
|
|
{
|
|
return;
|
|
}
|
|
var url = "/batch/photos/photo_card";
|
|
var data = new FormData();
|
|
var photo_ids = Array.from(needed).join(",");
|
|
data.append("photo_ids", photo_ids);
|
|
function callback(response)
|
|
{
|
|
if (response["meta"]["status"] !== 200)
|
|
{
|
|
return;
|
|
}
|
|
response = response["data"];
|
|
var holder = document.getElementById("photo_card_holder");
|
|
for (photo_id in response)
|
|
{
|
|
photo_div = html_to_element(response[photo_id]);
|
|
divs[photo_id] = photo_div;
|
|
needed.delete(photo_id)
|
|
holder.appendChild(photo_div);
|
|
}
|
|
apply_check_all();
|
|
}
|
|
post(url, data, callback);
|
|
}
|
|
|
|
function myhook()
|
|
{
|
|
recalculate_needed();
|
|
request_more_divs();
|
|
refresh_divs();
|
|
}
|
|
|
|
on_clipboard_load_hooks.push(myhook);
|
|
on_clipboard_save_hooks.push(myhook);
|
|
|
|
|
|
function submit_add_tag(callback)
|
|
{
|
|
var box = document.getElementById("add_tag_textbox");
|
|
var tagname = box.value.trim();
|
|
if (! tagname)
|
|
{return}
|
|
|
|
box.value = "";
|
|
return submit_add_remove_tag("add", tagname, callback);
|
|
}
|
|
function submit_remove_tag(callback)
|
|
{
|
|
var box = document.getElementById("remove_tag_textbox");
|
|
var tagname = box.value.trim();
|
|
if (! tagname)
|
|
{return}
|
|
|
|
box.value = "";
|
|
return submit_add_remove_tag("remove", tagname, callback);
|
|
}
|
|
function submit_add_remove_tag(action, tagname, callback)
|
|
{
|
|
if (photo_clipboard.size == 0)
|
|
{return;}
|
|
var url = "/batch/photos/" + action + "_tag";
|
|
var photo_ids = Array.from(photo_clipboard).join(",");
|
|
var data = new FormData();
|
|
data.append("photo_ids", photo_ids);
|
|
data.append("tagname", tagname);
|
|
post(url, data, callback);
|
|
}
|
|
function add_remove_callback(response)
|
|
{
|
|
response = response["data"];
|
|
var tagname = response["tagname"];
|
|
var message_area = document.getElementById("message_area");
|
|
var message_positivity;
|
|
var message_text;
|
|
|
|
if ("error_type" in response)
|
|
{
|
|
message_positivity = "message_negative";
|
|
message_text = response["error_message"];
|
|
}
|
|
else if ("action" in response)
|
|
{
|
|
var action = response["action"];
|
|
message_positivity = "message_positive";
|
|
if (action == "add")
|
|
{message_text = "Added tag " + tagname;}
|
|
|
|
else if (action == "remove")
|
|
{message_text = "Removed tag " + tagname;}
|
|
}
|
|
create_message_bubble(message_area, message_positivity, message_text, 8000);
|
|
}
|
|
|
|
var refresh_in_progress = false;
|
|
function submit_refresh_metadata(callback)
|
|
{
|
|
if (refresh_in_progress)
|
|
{return;}
|
|
|
|
if (photo_clipboard.size == 0)
|
|
{return;}
|
|
|
|
var url = "/batch/photos/refresh_metadata";
|
|
var photo_ids = Array.from(photo_clipboard).join(",");
|
|
var data = new FormData();
|
|
data.append("photo_ids", photo_ids);
|
|
refresh_in_progress = true;
|
|
post(url, data, callback);
|
|
}
|
|
function refresh_metadata_callback(response)
|
|
{
|
|
response = response["data"];
|
|
refresh_in_progress = false;
|
|
if ("error_type" in response)
|
|
{
|
|
var message_area = document.getElementById("message_area");
|
|
var message_positivity = "message_negative";
|
|
var message_text = response["error_message"];
|
|
create_message_bubble(message_area, message_positivity, message_text, 8000);
|
|
}
|
|
else
|
|
{
|
|
location.reload();
|
|
}
|
|
}
|
|
|
|
function searchhidden_callback(response)
|
|
{
|
|
response = response["data"];
|
|
var message_area = document.getElementById("message_area");
|
|
var message_positivity;
|
|
var message_text;
|
|
if ("error_type" in response)
|
|
{
|
|
message_positivity = "message_negative";
|
|
message_text = response["error_message"];
|
|
}
|
|
else
|
|
{
|
|
message_positivity = "message_positive";
|
|
message_text = "Success."
|
|
}
|
|
create_message_bubble(message_area, message_positivity, message_text, 8000);
|
|
}
|
|
function submit_set_searchhidden(callback)
|
|
{
|
|
if (photo_clipboard.size == 0)
|
|
{return;}
|
|
|
|
var url = "/batch/photos/set_searchhidden";
|
|
var data = new FormData();
|
|
var photo_ids = Array.from(photo_clipboard).join(",");
|
|
|
|
data.append("photo_ids", photo_ids);
|
|
post(url, data, callback);
|
|
}
|
|
function submit_unset_searchhidden(callback)
|
|
{
|
|
if (photo_clipboard.size == 0)
|
|
{return;}
|
|
|
|
var url = "/batch/photos/unset_searchhidden";
|
|
var data = new FormData();
|
|
var photo_ids = Array.from(photo_clipboard).join(",");
|
|
|
|
data.append("photo_ids", photo_ids);
|
|
post(url, data, callback);
|
|
}
|
|
</script>
|
|
</html>
|