all repos — artbound-python @ 5ef1788dfade02080034166b4fd13fe3818ef069

A client-server reimplementation of the administration panel for ArtBound.

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