1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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();
}
})
});
|