all repos — ArtBoundPanel @ 9d2568092d58d9e40e1353fc7ab3328a2cf3aa94

An administration panel for the "ArtBound" competition, that works entirely via client-side Javascript and HTML5.

script.js (view raw)

  1const DISCOVERY_DOC = "https://sheets.googleapis.com/$discovery/rest?version=v4",
  2	DISCOVERY_DOC_DRIVE = 'https://www.googleapis.com/discovery/v1/apis/drive/v3/rest',
  3	SCOPES = "https://www.googleapis.com/auth/spreadsheets.readonly https://www.googleapis.com/auth/drive.metadata.readonly",
  4	RANGE = `${SHEET_NAME}!A2:${String.fromCharCode(65 + Math.max(DATE_COL, NAME_COL, URL_COL))}`,
  5	WATERMARK = new Image(),
  6	date = new Date(),
  7	month_input = document.getElementById("month_input"),
  8	month_div = document.getElementById("month_div"),
  9	authorize_button = document.getElementById("authorize_button"),
 10	selectall_button = document.getElementById("selectall_button"),
 11	selectnone_button = document.getElementById("selectnone_button"),
 12	togglecolor_button = document.getElementById("togglecolor_button"),
 13	controls_div = document.getElementById("controls"),
 14	opacity_range = document.getElementById("opacity_range"),
 15	main_container_div = document.getElementById("main_container"),
 16	content_div = document.getElementById("content");
 17
 18let tokenClient,
 19	sheetResult,
 20	gapiInited = !1,
 21	gisInited = !1,
 22	fanarts = new Array(),
 23	watermark_invert = '';
 24
 25WATERMARK.src = WATERMARK_SRC;
 26month_input.value = `${date.getFullYear()}-${("0" + (date.getMonth() + 1)).slice(-2)}`;
 27
 28function gapiLoaded() {
 29	gapi.load("client", intializeGapiClient);
 30}
 31async function intializeGapiClient() {
 32	await gapi.client.init({
 33		apiKey: API_KEY,
 34		discoveryDocs: [DISCOVERY_DOC, DISCOVERY_DOC_DRIVE]
 35	})
 36	gapiInited = true;
 37	maybeEnableButtons();
 38}
 39
 40function gisLoaded() {
 41	tokenClient = google.accounts.oauth2.initTokenClient({
 42		client_id: CLIENT_ID,
 43		scope: SCOPES,
 44		callback: ""
 45	});
 46	gisInited = true;
 47	maybeEnableButtons();
 48}
 49
 50function maybeEnableButtons() {
 51	gapiInited && gisInited && (authorize_button.hidden = false);
 52}
 53
 54function handleAuthClick() {
 55	if(!sheetResult){
 56		tokenClient.callback = async (resp) => {
 57			if (resp.error !== undefined) {
 58				throw (resp);
 59			}
 60			authorize_button.innerText = "Aggiungi";
 61	
 62			await getSheet();
 63			getFanarts();
 64		};
 65	
 66		if (gapi.client.getToken() === null) {
 67			tokenClient.requestAccessToken({
 68				prompt: 'consent'
 69			});
 70		} else {
 71			tokenClient.requestAccessToken({
 72				prompt: ''
 73			});
 74		}
 75	} else getFanarts();
 76}
 77
 78async function getLink(id) {
 79	let response;
 80	try {
 81		response = await gapi.client.drive.files.get({
 82			'fileId': id,
 83			'fields': 'webContentLink'
 84		});
 85	} catch (err) {
 86		console.log(err.message);
 87		return;
 88	}
 89	const files = response.result;
 90	if (!files || files.length == 0) {
 91		console.log("nessuna risposta");
 92		return;
 93	}
 94	return files['webContentLink'].split("&")[0];
 95}
 96
 97
 98async function getSheet() {
 99	let e;
100	try {
101		e = await gapi.client.sheets.spreadsheets.values.get({
102			spreadsheetId: SPREADSHEET_ID,
103			range: RANGE
104		})
105	} catch (e) {
106		return void(content_div.innerText = e.message);
107	}
108	const t = e.result;
109	if (!t || !t.values || 0 == t.values.length) return void(content_div.innerText = "No values found.");
110	sheetResult = t.values;
111}
112
113async function getFanarts(){
114	let date, date_str, id;
115	for (x of sheetResult) {
116		date = x[DATE_COL].split(" ")[0].split("/"); // dd/mm/yy hh.mm.ss
117		date_str = `${date[2]}-${date[1]}`;
118
119		if (date_str == month_input.value) { // yyyy-mm
120			id = x[URL_COL].split("=")[1];
121			fanarts.push({
122				'id': id,
123				'date': date,
124				'name': x[NAME_COL],
125				'content': await getLink(id),
126				'enabled': 1
127			})
128		}
129	}
130	if (fanarts) {
131		controls_div.hidden = false;
132		updateOpacity();
133		updateFanartList();
134		//console.log(JSON.stringify(fanarts));
135	}
136}
137
138function addCanvasEvents(img, canvas, ctx){
139	function abc(e) {
140		if (e.button == 0) addWatermark(e, img, canvas, ctx);
141	}
142
143	canvas.addEventListener('mousemove', abc);
144	canvas.addEventListener('mousedown', function(e) {
145		canvas.removeEventListener('mousemove', abc);
146		canvas.addEventListener('mousedown', abc);
147	});
148}
149
150function getNewCardHtml(element) {
151	element.div = document.createElement("div");
152	element.canvas = document.createElement("canvas");
153	const div1 = document.createElement("div"),
154		div2 = document.createElement("div"),
155		a = document.createElement("a"),
156		div3 = document.createElement("div"),
157		div4 = document.createElement("div"),
158		button1 = document.createElement("button"),
159		button2 = document.createElement("button"),
160		button3 = document.createElement("button"),
161		button4 = document.createElement("button");
162	
163	element.div.className = `col-md-${BS_COL_WIDTH} entry${element.enabled == 0 ? " entry-disabled" : ""}`;
164	element.div.id = `div-${element.id}`;
165	element.div.setAttribute("data-index", element.index);
166	div1.className = `card mb-${BS_COL_WIDTH} box-shadow my-card`;
167	element.canvas.className = "card-img-top entry-img";
168	element.canvas.id = element.id;
169	element.canvas.setAttribute("data-name", element.name);
170	element.canvas.setAttribute("data-content", element.content);
171	div2.className = "card-body";
172	a.className = "card-text";
173	a.innerText = `${('0' + element.index).slice(-2)} - ${element.name}.png`;
174	a.title = "Clicca per copiare."
175	a.addEventListener("click", function() { navigator.clipboard.writeText(a.innerText); }, false)
176	div3.className = "d-flex justify-content-between align-items-center card-controls";
177	div4.className = "btn-group";
178
179	button1.className = button2.className = button3.className = button4.className = "btn btn-sm btn-outline-secondary";
180	button1.innerText = "⬅️";
181	button2.innerText = "*️⃣";
182	button3.innerText = "🔄";
183	button4.innerText = "➡️";
184	button1.addEventListener("click", function() { moveUpDown(element.id, -1); }, false);
185	button2.addEventListener("click", function() { toggleEntry(element.id); }, false);
186	button3.addEventListener("click", function() { reloadEntry(element.id); }, false);
187	button4.addEventListener("click", function() { moveUpDown(element.id, 1); }, false);
188
189	div4.appendChild(button1);
190	div4.appendChild(button2);
191	div4.appendChild(button3);
192	div4.appendChild(button4);
193	div3.appendChild(div4);
194	div2.appendChild(a);
195	div2.appendChild(div3);
196	div1.appendChild(element.canvas);
197	div1.appendChild(div2);
198	element.div.appendChild(div1);
199
200	const ctx = element.canvas.getContext("2d");
201
202	element.image = new Image();
203	element.image.addEventListener("load", function(){
204		setBaseImage(element.image, element.canvas, ctx);
205		addCanvasEvents(element.image, element.canvas, ctx);
206	}, false);
207	element.image.src = element.content;
208
209	return element.div;
210}
211
212async function updateFanartList() {
213	main_container_div.hidden = false;
214	content_div.innerHTML = ""
215	
216	let i = 0;
217	for (fanart of fanarts) {
218		if(fanart.enabled){
219			i++;
220			fanart.index = i;
221			content_div.appendChild(getNewCardHtml(fanart));
222		}
223	}
224
225	for (fanart of fanarts) {
226		if(!fanart.enabled){
227			fanart.index = 0;
228			content_div.appendChild(getNewCardHtml(fanart));
229		}
230	}
231}
232
233function getFanart(id){
234	return fanarts.find(element => element.id == id)
235}
236
237function toggleEntry(id) {
238	entry = getFanart(id);
239	if (!entry) return;
240
241	entry.enabled = !entry.enabled;
242	updateFanartList()
243}
244
245function reloadEntry(id){
246	const fanart = getFanart(id);
247	if (!fanart) return;
248
249	const old_div = fanart.div;
250	content_div.replaceChild(getNewCardHtml(fanart), old_div);
251	old_div.remove();
252}
253
254function selectAllNone(toggle) {
255	fanarts.map(x => x.enabled = toggle)
256	updateFanartList()
257}
258
259function debugFn(){
260	fanarts = JSON.parse('[{"id":"1dE8L7w2DuOfQSJwf5oRjAeJ-VZfy5o-6","date":["03","08","2022"],"name":"Saro","content":"https://drive.google.com/uc?id=1dE8L7w2DuOfQSJwf5oRjAeJ-VZfy5o-6","enabled":1,"index":1,"div":{}},{"id":"1ZO2poqaylmh7_FkEjRthozVXFpcP1Edx","date":["18","08","2022"],"name":"suchetto","content":"https://drive.google.com/uc?id=1ZO2poqaylmh7_FkEjRthozVXFpcP1Edx","enabled":1,"index":2,"div":{}},{"id":"1jkpZzqnQUdXv7QiW1khuwnSsdnjudBt-","date":["18","08","2022"],"name":"suca","content":"https://drive.google.com/uc?id=1clZbi1QzJQo_c7PGWx-nmLgfPhXqHdZR","enabled":1,"index":3,"div":{}}]');
261	controls_div.hidden = false;
262	updateOpacity();
263	updateFanartList();
264}
265
266function toggleColor() {
267	watermark_invert = watermark_invert == '' ? 'invert(1)' : '';
268	updateColorDisplay();
269}
270
271function updateColorDisplay() {
272	togglecolor_button.innerText = watermark_invert ? "⚫" : "⚪";
273}
274
275function addWatermark(event, img, c, ctx) {
276	setBaseImage(img, c, ctx);
277
278	const rect = c.getBoundingClientRect();
279	const x = (event.clientX - rect.left) * c.width / c.clientWidth;
280	const y = (event.clientY - rect.top) * c.height / c.clientHeight;
281
282	ctx.globalAlpha = opacity_range.value;
283	ctx.filter = watermark_invert;
284	ctx.drawImage(WATERMARK, x - (WATERMARK_WIDTH / 2), y - (WATERMARK_HEIGHT / 2), WATERMARK_WIDTH, WATERMARK_HEIGHT);
285}
286
287function updateOpacity() {
288	opacity_label.innerHTML = Math.round(opacity_range.value * 100) + '%';
289}
290
291function setBaseImage(img, c, ctx) {
292	const f = Math.min(MAX_WIDTH / img.width, MAX_HEIGHT / img.height);
293
294	const new_width = c.width = Math.ceil(img.width * f);
295	const new_height = c.height = Math.ceil(img.height * f)
296
297	ctx.imageSmoothingEnabled = (f < 1);
298	ctx.drawImage(img, 0, 0, new_width, new_height);
299}
300
301function moveUpDown(id, amount) {
302	const pos = fanarts.indexOf(fanarts.find(element => element.id == id));
303	const new_pos = pos + amount;
304
305	if (new_pos <= -1 || new_pos >= fanarts.length) {
306		return;
307	}
308
309	[fanarts[pos], fanarts[new_pos]] = [fanarts[new_pos], fanarts[pos]];
310
311	updateFanartList();
312}