artbound_python/static/script.js (view raw)
1const MAX_WIDTH = MAX_HEIGHT = 1000,
2 WATERMARK_SRC = "/static/res/wm.png",
3 WATERMARK_WIDTH = 325,
4 WATERMARK_HEIGHT = 98,
5 IG_TEMPLATE_SRC = "/static/res/ig.png",
6 IG_MIN_OFFSET_X = 0,
7 IG_MIN_OFFSET_Y = 342,
8 IG_MAX_WIDTH = 1080,
9 IG_MAX_HEIGHT = 988,
10
11 WATERMARK = new Image(),
12 IG_TEMPLATE = new Image(),
13 fanarts = new Array(),
14 months = new Set(),
15 last_updated_link = document.getElementById("last-updated-link"),
16 month_input = document.getElementById("month_input"),
17 month_div = document.getElementById("month_div"),
18 get_button = document.getElementById("get_button"),
19 selectall_button = document.getElementById("selectall_button"),
20 selectnone_button = document.getElementById("selectnone_button"),
21 controls_div = document.getElementById("controls"),
22 opacity_range = document.getElementById("opacity_range"),
23 content_div = document.getElementById("content"),
24 canvas_link = document.getElementById("canvas-download"),
25 canvas_ig = document.getElementById("instagram-canvas"),
26 fanart_template = document.getElementById("fanart-template").innerHTML,
27 radians_factor = 90 * Math.PI / 180;
28
29let new_entries = 0;
30
31WATERMARK.src = WATERMARK_SRC;
32IG_TEMPLATE.src = IG_TEMPLATE_SRC;
33month_input.addEventListener("keyup", (e) => { if (e.key === "Enter") getArtworks(); });
34
35function addCanvasEvents(element, ctx) {
36 function abc(e) {
37 if (element.enabled == 0) return;
38 if (e.button != 0) return;
39 element.watermark.opacity = opacity_range.value;
40 addWatermark(e, element, ctx);
41 }
42 if (element.clicked) {
43 element.canvas.addEventListener('mousedown', abc);
44 return;
45 }
46 element.canvas.addEventListener('mousemove', abc);
47 element.canvas.addEventListener('mouseleave', () => {
48 if (!element.clicked && element.enabled)
49 setBaseImage(element);
50 });
51 element.canvas.addEventListener('mousedown', (e) => {
52 element.clicked = true;
53 element.canvas.removeEventListener('mousemove', abc);
54 element.canvas.addEventListener('mousedown', abc);
55 });
56}
57
58function createElementFromHTML(htmlString) {
59 const div = document.createElement('div');
60 div.innerHTML = htmlString.trim();
61 return div.firstChild;
62}
63
64function getNewCardHtml(element) {
65 const id = element.id,
66 index = element.index,
67 name = element.name,
68 content = element.content,
69 filename = `${('0' + element.index).slice(-2)} - ${element.name}.png`,
70 disabled = element.enabled == 0 ? " entry-disabled" : "";
71 const html_string = Mustache.render("{{={| |}=}}" + fanart_template, { id, index, name, content, filename, disabled });
72 element.div = createElementFromHTML(html_string);
73 element.canvas = element.div.getElementsByTagName("canvas")[0];
74 const ctx = element.canvas.getContext("2d");
75 element.image = new Image();
76 element.image.addEventListener("load", () => {
77 const wm_info = element.watermark;
78 setBaseImage(element);
79 if (element.clicked)
80 drawWatermark(wm_info, ctx);
81 addCanvasEvents(element, ctx);
82 });
83 element.image.src = element.content;
84 return element.div;
85}
86
87async function updateFanartList() {
88 content_div.innerHTML = "";
89 get_button.disabled = false;
90 get_button.innerText = emoji_get;
91
92 let i = 0;
93 for (fanart of fanarts) {
94 if (fanart.enabled) {
95 i++;
96 fanart.index = i;
97 content_div.appendChild(getNewCardHtml(fanart));
98 }
99 }
100 for (fanart of fanarts) {
101 if (!fanart.enabled) {
102 fanart.index = 0;
103 content_div.appendChild(getNewCardHtml(fanart));
104 }
105 }
106}
107
108function getFanart(id) {
109 return fanarts.find(element => element.id == id);
110}
111
112function toggleEntry(id) {
113 entry = getFanart(id);
114 if (!entry) return;
115
116 entry.enabled = !entry.enabled;
117 entry.clicked = false;
118 updateFanartList();
119}
120
121function saveCanvas(my_canvas, filename) {
122 canvas_link.href = my_canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
123 canvas_link.setAttribute("download", filename);
124 canvas_link.click();
125}
126
127function reloadEntry(id) {
128 const fanart = getFanart(id);
129 if (!fanart) return;
130
131 const old_div = fanart.div;
132 content_div.replaceChild(getNewCardHtml(fanart), old_div);
133 old_div.remove();
134}
135
136function selectAllNone(toggle) {
137 fanarts.map(x => x.enabled = toggle)
138 updateFanartList();
139}
140
141function clickCoordsToCanvas(clickX, clickY, c) {
142 const rect = c.getBoundingClientRect();
143 const x = (clickX - rect.left) * c.width / c.clientWidth;
144 const y = (clickY - rect.top) * c.height / c.clientHeight;
145 return [x, y];
146}
147
148function drawWatermark(wm_info, ctx) {
149 ctx.globalAlpha = wm_info.opacity;
150 ctx.filter = wm_info.invert;
151 ctx.drawImage(WATERMARK, wm_info.x - (WATERMARK_WIDTH / 2), wm_info.y - (WATERMARK_HEIGHT / 2), WATERMARK_WIDTH, WATERMARK_HEIGHT);
152}
153
154function addWatermark(event, element, ctx) {
155 setBaseImage(element);
156 let [x, y] = clickCoordsToCanvas(event.clientX, event.clientY, element.canvas);
157 element.watermark.x = x;
158 element.watermark.y = y;
159 drawWatermark(element.watermark, ctx);
160}
161
162function updateOpacity() {
163 opacity_label.innerHTML = Math.round(opacity_range.value * 100) + '%';
164}
165
166function getFactor(img_width, img_height, max_width, max_height) {
167 return Math.min(max_width / img_width, max_height / img_height);
168}
169
170function setBaseImage(element) {
171 const img = element.image;
172 const c = element.canvas;
173 const ctx = c.getContext("2d");
174 const f = getFactor(img.width, img.height, MAX_WIDTH, MAX_HEIGHT);
175 ctx.imageSmoothingEnabled = (f < 1);
176
177 const pre_width = Math.ceil(img.width * f);
178 const pre_height = Math.ceil(img.height * f);
179
180 if (element.rotated % 2) [c.width, c.height] = [pre_height, pre_width];
181 else [c.width, c.height] = [pre_width, pre_height];
182
183 const rotation_radians = radians_factor * element.rotated;
184 ctx.rotate(rotation_radians);
185
186 offset_x = (element.rotated == 2 || element.rotated == 3) ? -pre_width : 0;
187 offset_y = (element.rotated == 1 || element.rotated == 2) ? -pre_height : 0;
188 ctx.drawImage(img, offset_x, offset_y, pre_width, pre_height);
189 ctx.rotate(-rotation_radians);
190}
191
192function rotateEntry(id) {
193 const entry = fanarts.find(element => element.id == id);
194 if (!entry) return;
195 if (!entry.enabled) return;
196
197 entry.rotated++;
198 if (entry.rotated >= 4 || entry.rotated < 0) entry.rotated = 0;
199 entry.clicked = false;
200 updateFanartList();
201}
202
203function moveUpDown(id, amount) {
204 const entry = fanarts.find(element => element.id == id);
205 if (!entry) return;
206 if (!entry.enabled) return;
207 const pos = fanarts.indexOf(entry);
208 const new_pos = pos + amount;
209
210 if (new_pos <= -1 || new_pos >= fanarts.length) return;
211 [fanarts[pos], fanarts[new_pos]] = [fanarts[new_pos], fanarts[pos]];
212 updateFanartList();
213}
214
215function toggleInvert(id, button) {
216 const entry = fanarts.find(element => element.id == id);
217 if (!entry) return;
218 if (!entry.enabled) return;
219 entry.watermark.invert = entry.watermark.invert == '' ? 'invert(1)' : '';
220 button.innerText = entry.watermark.invert ? emoji_color_black : emoji_color;
221 const ctx = entry.canvas.getContext('2d');
222 setBaseImage(entry);
223 drawWatermark(entry.watermark, ctx);
224}
225
226async function postData(url = "", data = {}, contentType = "application/json") {
227 const response = await fetch(url, { method: "POST", headers: { "Content-Type": contentType }, body: JSON.stringify(data) });
228 return response.json();
229}
230
231function saveEntry(id) {
232 const entry = getFanart(id);
233 if (!entry) return;
234 if (!entry.enabled) return;
235 saveCanvas(entry.canvas, entry.canvas.getAttribute("data-filename"));
236}
237
238function saveCanvasIG(my_canvas) {
239 const f = getFactor(my_canvas.width, my_canvas.height, IG_MAX_WIDTH, IG_MAX_HEIGHT);
240 const width = Math.ceil(my_canvas.width * f);
241 const height = Math.ceil(my_canvas.height * f);
242 const offset_x = Math.round((IG_MAX_WIDTH - width) / 2);
243 const offset_y = Math.round((IG_MAX_HEIGHT - height) / 2);
244 destCtx = canvas_ig.getContext('2d');
245 destCtx.drawImage(IG_TEMPLATE, 0, 0);
246 destCtx.drawImage(my_canvas, IG_MIN_OFFSET_X + offset_x, IG_MIN_OFFSET_Y + offset_y, width, height);
247 saveCanvas(canvas_ig, "IG - " + my_canvas.getAttribute("data-filename"));
248}
249
250function saveEntryIG(id) {
251 entry = getFanart(id);
252 if (!entry) return;
253 if (!entry.enabled) return;
254 saveCanvasIG(entry.canvas);
255}
256
257function getArtworks() {
258 const month_value = month_input.value;
259 if (months.has(month_value)) return;
260 get_button.disabled = true;
261 get_button.innerText = "🔄"
262 postData("/", { month: month_value }).then((data) => {
263 fanarts.push(...data.map((element) => {
264 return {
265 ...element,
266 "enabled": 1,
267 "rotated": 0,
268 "watermark": { invert: "" },
269 };
270 }));
271 controls_div.hidden = false;
272 updateOpacity();
273 updateFanartList();
274 months.add(month_value);
275 });
276}
277
278function saveAll() {
279 const response = confirm("Vuoi davvero scaricare tutte le fanart?");
280 if (response == false) return;
281 fanarts.forEach((fanart) => {
282 if (fanart.enabled)
283 saveCanvas(fanart.canvas, fanart.canvas.getAttribute("data-filename"));
284 })
285}
286
287function saveAllIG() {
288 const response = confirm("Vuoi davvero scaricare tutte le storie per le fanart?");
289 if (response == false) return;
290 fanarts.forEach((fanart) => {
291 if (fanart.enabled)
292 saveCanvasIG(fanart.canvas);
293 });
294}
295
296function updateDb() {
297 postData("/update").then((data) => {
298 last_updated_link.innerText = data.timestamp;
299 if (data.new == 0) return;
300 new_entries += data.new;
301 last_updated_link.innerText += ` (+${ new_entries })`;
302 });
303}