diff options
| author | Jakob L. Kreuze <zerodaysfordays@sdf.org> | 2020-07-28 12:56:02 -0400 |
|---|---|---|
| committer | Jakob L. Kreuze <zerodaysfordays@sdf.org> | 2020-07-28 12:57:18 -0400 |
| commit | aa0f62411452ab7d0f4ed4595937acb2ef8eac0c (patch) | |
| tree | ffc3e05c60937494c08a24415a66f0dddcd8fd52 /static | |
| parent | 03860ebc2e0624ecd0cf094498ac3bfc2df18183 (diff) | |
[kona-web] Implement mass selection and tagging.
Diffstat (limited to 'static')
| -rw-r--r-- | static/css/style.css | 6 | ||||
| -rw-r--r-- | static/js/mass_editor.js | 116 |
2 files changed, 122 insertions, 0 deletions
diff --git a/static/css/style.css b/static/css/style.css index 017622a..6c2f508 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -28,3 +28,9 @@ main { min-width: 100%; margin-bottom: 16px; } + +.image-container { + display: inline-block; + padding: 5px; + margin: 5px; +} diff --git a/static/js/mass_editor.js b/static/js/mass_editor.js new file mode 100644 index 0000000..949e130 --- /dev/null +++ b/static/js/mass_editor.js @@ -0,0 +1,116 @@ +// Copyright © 2020 Jakob L. Kreuze <zerodaysfordays@sdf.org> +// +// This file is part of Kona. +// +// Kona is free software; you can redistribute it and/or modify it under +// the terms of the GNU Affero General Public License as published by +// the Free Software Foundation; either version 3 of the License, or (at +// your option) any later version. +// +// Kona is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public +// License for more details. +// +// You should have received a copy of the GNU Affero General Public +// License along with Kona. If not, see <http://www.gnu.org/licenses/>. + +let selectedImages = []; + +window.addEventListener("load", () => { + // Enable visual queues that make sense only when edit mode is available. + document.styleSheets[0].insertRule('.image-container { border: none; }', 0); + document.styleSheets[0].insertRule('.image-container:hover { border: solid 1px; }', 0); + + // Similarly, add a tag dialog that makes sense only when edit mode is available. + let form = document.createElement("form"); + form.id = "update-form"; + + let input = document.createElement("input"); + input.id = "update-prompt"; + input.type = "text"; + + let updateButton = document.createElement("button"); + updateButton.id = "update-form-submit"; + updateButton.textContent = "Update Selected"; + + form.appendChild(input); + form.appendChild(updateButton); + document.getElementById("tag-view").appendChild(form); + + updateButton.addEventListener("click", (e) => { + let queryString = input.value.replace(" ", ","); + + // Send a post for each ID. + selectedImages.forEach((id) => { + let xhr = new XMLHttpRequest(); + + xhr.open("POST", `/api/posts/${id}`); + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + + // Update the tag counts when the update hits the database. + xhr.onload = () => { + let xhr = new XMLHttpRequest(); + xhr.open("GET", "/api/tags"); + xhr.onload = () => { + let topTags = document.getElementById("top-tags"); + topTags.textContent = ""; + JSON.parse(xhr.response).forEach((pair) => { + let count = pair[0]; + let tag = pair[1]; + + let a = document.createElement("a"); + a.href = `/posts?query=${tag}`; + a.textContent = tag; + + let li = document.createElement("li"); + li.appendChild(a); + li.innerHTML += " " + count; + + topTags.appendChild(li); + }); + }; + xhr.send(); + } + + xhr.send(encodeURI(`tags=${queryString}`)); + }); + + e.preventDefault(); + }); + + // Add event listeners for enabling "select" mode. + let selectingImages = false; + document.body.addEventListener("keydown", (e) => { if (e.keyCode == 17) selectingImages = true; }); + document.body.addEventListener("keyup", (e) => { if (e.keyCode == 17) selectingImages = false; }); + + // Handle potential selection. + document.getElementById("main").addEventListener("click", (e) => { + // Select the containing DIV, if the click event occurred on the IMG child. + let target = e.target.nodeName === "IMG" ? + e.target.parentElement.parentElement : + e.target; + + // Ignore clicks that don't land on any image container. + if (target.nodeName === "MAIN") { + return false; + } + + if (selectingImages) { + // Obtain the image's ID from the element ID. + let id = parseInt(/image-([\d]*)/.exec(target.id)[1]); + + // Either deselect, or select the image. + if (selectedImages.includes(id)) { + selectedImages = selectedImages.filter((n) => n !== id ); + target.style.border = "none"; + } else { + selectedImages.push(id); + target.style.border = "solid 2px red"; + } + + // Don't follow the link if we're holding the "select" hotkey. + e.preventDefault(); + } + }) +}); |