<!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/api.js"></script> <script src="/static/js/hotkeys.js"></script> <script src="/static/js/photo_clipboard.js"></script> <script src="/static/js/spinner.js"></script> <script src="/static/js/tag_autocomplete.js"></script> <style> #header { grid-area: header; } #left { word-break: break-word; grid-area: left; } #right { grid-template: "add_tag_area" "remove_tag_area" "refresh_metadata_area" "searchhidden_area" "download_zip_area" "message_area" 1fr; } #right > * { display: flex; padding: 8px; } #right button { flex: 1; } #add_tag_area { grid-area: add_tag_area; } #remove_tag_area { grid-area: remove_tag_area; } #add_tag_area input, #remove_tag_area input { flex: 1; } #add_tag_area button, #remove_tag_area button { flex: initial; } #refresh_metadata_area { grid-area: refresh_metadata_area; } #searchhidden_area { grid-area: searchhidden_area; } #download_zip_area { grid-area: download_zip_area; } #message_area { grid-area: message_area; margin: 8px; } @media screen and (max-width: 800px) { #content_body { grid-template: "left" 1fr "right" 200px; } #right { top: unset !important; width: unset !important; left: 8px; right: 8px; bottom: 8px; height: 200px; grid-template: "add_tag_area message_area" "remove_tag_area message_area" "refresh_metadata_area message_area" "searchhidden_area message_area" "download_zip_area message_area" "extra message_area" 1fr /1fr minmax(50px, 200px); } } </style> </head> <body> {{header.make_header(session=session)}} <div id="content_body" class="sticky_side_right"> <div id="left"> <span>The clipboard contains <span class="clipboard_count">0</span> items.</span> <button id="clear_clipboard_button" class="red_button" onclick="photo_clipboard.clear_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" list="tag_autocomplete_datalist"> <button class="add_tag_button green_button" id="add_tag_button" onclick="add_tag_form();">Add tag</button> </div> <div id="remove_tag_area"> <input type="text" id="remove_tag_textbox" list="tag_autocomplete_datalist"> <button class="red_button" id="remove_tag_button" onclick="remove_tag_form();">Remove tag</button> </div> <div id="refresh_metadata_area"> <button class="green_button" id="refresh_metadata_button" onclick="refresh_metadata_form();">Refresh metadata</button> <span id="refresh_metadata_spinner" class="hidden">Working...</span> </div> <div id="searchhidden_area"> <button class="yellow_button" id="set_searchhidden_button" onclick="set_searchhidden_form()">Searchhide</button> <button class="yellow_button" id="unset_searchhidden_button" onclick="unset_searchhidden_form()">Unhide</button> </div> <div id="download_zip_area"> <button class="yellow_button" id="download_zip_button" onclick="download_zip_form()">Download .zip</button> </div> <div id="message_area"> </div> </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", common.entry_with_history_hook); common.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", common.entry_with_history_hook); common.bind_box_to_button(remove_box, remove_button); function recalculate_needed() { needed = new Set(); photo_clipboard.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.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 = common.html_to_element(response[photo_id]); divs[photo_id] = photo_div; needed.delete(photo_id) holder.appendChild(photo_div); } photo_clipboard.apply_check_all(); } common.post(url, data, callback); } function my_clipboard_load_save_hook() { recalculate_needed(); request_more_divs(); refresh_divs(); } tag_autocomplete.init_datalist(); photo_clipboard.on_load_hooks.push(my_clipboard_load_save_hook); photo_clipboard.on_save_hooks.push(my_clipboard_load_save_hook); //////////////////////////////////////////////////////////////////////////////////////////////////// function add_remove_tag(action, tagname) { if (photo_clipboard.clipboard.size == 0) {return;} var url = "/batch/photos/" + action + "_tag"; var photo_ids = Array.from(photo_clipboard.clipboard).join(","); var data = new FormData(); data.append("photo_ids", photo_ids); data.append("tagname", tagname); common.post(url, data, add_remove_tag_callback); } function add_remove_tag_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;} } common.create_message_bubble(message_area, message_positivity, message_text, 8000); } function add_tag_form() { var box = document.getElementById("add_tag_textbox"); var tagname = box.value.trim(); if (! tagname) {return} box.value = ""; return add_remove_tag("add", tagname); } function remove_tag_form() { var box = document.getElementById("remove_tag_textbox"); var tagname = box.value.trim(); if (! tagname) {return} box.value = ""; return add_remove_tag("remove", tagname); } //////////////////////////////////////////////////////////////////////////////// function download_zip_callback(response) { var zip_token = response["data"]["zip_token"]; var url = `/batch/photos/download_zip/${zip_token}.zip`; window.location.href = url; } function download_zip_form() { if (photo_clipboard.clipboard.size == 0) {return;} var url = "/batch/photos/download_zip"; var photo_ids = Array.from(photo_clipboard.clipboard).join(","); var data = new FormData(); data.append("photo_ids", photo_ids); common.post(url, data, download_zip_callback); } //////////////////////////////////////////////////////////////////////////////// var refresh_metadata_button = document.getElementById("refresh_metadata_button"); var refresh_metadata_spinner = document.getElementById("refresh_metadata_spinner"); refresh_metadata_spinner = new spinner.Spinner(refresh_metadata_spinner); function refresh_metadata_callback(response) { response = response["data"]; refresh_metadata_button.disabled = false; refresh_metadata_spinner.hide(); if ("error_type" in response) { var message_area = document.getElementById("message_area"); var message_positivity = "message_negative"; var message_text = response["error_message"]; common.create_message_bubble(message_area, message_positivity, message_text, 8000); } else { common.refresh(); } } function refresh_metadata_form() { if (photo_clipboard.clipboard.size == 0) {return;} var url = "/batch/photos/refresh_metadata"; var photo_ids = Array.from(photo_clipboard.clipboard).join(","); var data = new FormData(); data.append("photo_ids", photo_ids); refresh_metadata_spinner.show(); refresh_metadata_button.disabled = true; common.post(url, data, refresh_metadata_callback); } //////////////////////////////////////////////////////////////////////////////// function set_unset_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." } common.create_message_bubble(message_area, message_positivity, message_text, 8000); } function set_searchhidden_form() { if (photo_clipboard.clipboard.size == 0) {return;} var url = "/batch/photos/set_searchhidden"; var data = new FormData(); var photo_ids = Array.from(photo_clipboard.clipboard).join(","); data.append("photo_ids", photo_ids); common.post(url, data, set_unset_searchhidden_callback); } function unset_searchhidden_form() { if (photo_clipboard.clipboard.size == 0) {return;} var url = "/batch/photos/unset_searchhidden"; var data = new FormData(); var photo_ids = Array.from(photo_clipboard.clipboard).join(","); data.append("photo_ids", photo_ids); common.post(url, data, set_unset_searchhidden_callback); } </script> </html>