all repos — artbound-go @ e93f6facc7a759881a3f2e093b267a13c8626cae

The official administration panel for ArtBound, by EarthBound Café.

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 = {}) {
227	const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams(data).toString() });
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	fetch("/get?month=" + month_value).then((res) => {
263		res.json().then((data) => {
264			fanarts.push(...data.map((element) => {
265				return {
266					...element,
267					"enabled": 1,
268					"rotated": 0,
269					"watermark": { invert: "" },
270				};
271			}));
272			controls_div.hidden = false;
273			updateOpacity();
274			updateFanartList();
275			months.add(month_value);
276		})
277	})
278}
279
280function saveAll() {
281	const response = confirm("Vuoi davvero scaricare tutte le fanart?");
282	if (response == false) return;
283	fanarts.forEach((fanart) => {
284		if (fanart.enabled)
285			saveCanvas(fanart.canvas, fanart.canvas.getAttribute("data-filename"));
286	})
287}
288
289function saveAllIG() {
290	const response = confirm("Vuoi davvero scaricare tutte le storie per le fanart?");
291	if (response == false) return;
292	fanarts.forEach((fanart) => {
293		if (fanart.enabled)
294			saveCanvasIG(fanart.canvas);
295	});
296}
297
298function updateDb() {
299	postData("/update").then((data) => {
300		last_updated_link.innerText = data.timestamp;
301		if (data.new == 0) return;
302		new_entries += data.new;
303		last_updated_link.innerText += ` (+${ new_entries })`;
304	});
305}