all repos — honk @ 0f6eb7e7022106c849333b0bb16a420a2aab3c10

my fork of honk

views/honkpage.js (view raw)

  1var csrftoken = ""
  2var honksforpage = { }
  3var curpagestate = { name: "", arg : "" }
  4var tophid = { }
  5var servermsgs = { }
  6
  7function encode(hash) {
  8	var s = []
  9	for (var key in hash) {
 10		var val = hash[key]
 11		s.push(encodeURIComponent(key) + "=" + encodeURIComponent(val))
 12	}
 13	return s.join("&")
 14}
 15function post(url, data) {
 16	var x = new XMLHttpRequest()
 17	x.open("POST", url)
 18	x.timeout = 30 * 1000
 19	x.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
 20	x.send(data)
 21}
 22function get(url, whendone, errfunction) {
 23	var x = new XMLHttpRequest()
 24	x.open("GET", url)
 25	x.timeout = 15 * 1000
 26	x.responseType = "json"
 27	x.onload = function() { whendone(x) }
 28	if (errfunction) {
 29		x.ontimeout = function(e) { errfunction(" timed out") }
 30		x.onerror = function(e) { errfunction(" error") }
 31	}
 32	x.send()
 33}
 34function bonk(el, xid) {
 35	el.innerHTML = "bonked"
 36	el.disabled = true
 37	post("/bonk", encode({"js": "2", "CSRF": csrftoken, "xid": xid}))
 38	return false
 39}
 40function unbonk(el, xid) {
 41	el.innerHTML = "unbonked"
 42	el.disabled = true
 43	post("/zonkit", encode({"CSRF": csrftoken, "wherefore": "unbonk", "what": xid}))
 44}
 45function muteit(el, convoy) {
 46	el.innerHTML = "muted"
 47	el.disabled = true
 48	post("/zonkit", encode({"CSRF": csrftoken, "wherefore": "zonvoy", "what": convoy}))
 49	var els = document.querySelectorAll('article.honk')
 50	for (var i = 0; i < els.length; i++) {
 51		var e = els[i]
 52		if (e.getAttribute("data-convoy") == convoy) {
 53			e.remove()
 54		}
 55	}
 56}
 57function zonkit(el, xid) {
 58	el.innerHTML = "zonked"
 59	el.disabled = true
 60	post("/zonkit", encode({"CSRF": csrftoken, "wherefore": "zonk", "what": xid}))
 61	var p = el
 62	while (p && p.tagName != "ARTICLE") {
 63		p = p.parentElement
 64	}
 65	if (p) {
 66		p.remove()
 67	}
 68}
 69function flogit(el, how, xid) {
 70	var s = how
 71	if (s[s.length-1] != "e") { s += "e" }
 72	s += "d"
 73	if (s == "untaged") s = "untagged"
 74	if (s == "reacted") s = "badonked"
 75	el.innerHTML = s
 76	el.disabled = true
 77	post("/zonkit", encode({"CSRF": csrftoken, "wherefore": how, "what": xid}))
 78}
 79
 80var lehonkform = document.getElementById("honkform")
 81var lehonkbutton = document.getElementById("honkingtime")
 82
 83function oldestnewest(btn) {
 84	var els = document.getElementsByClassName("glow")
 85	if (els.length) {
 86		els[els.length-1].scrollIntoView({ behavior: "smooth" })
 87	}
 88}
 89function removeglow() {
 90	var els = document.getElementsByClassName("glow")
 91	while (els.length) {
 92		els[0].classList.remove("glow")
 93	}
 94}
 95
 96function fillinhonks(xhr, glowit) {
 97	var resp = xhr.response
 98	var stash = curpagestate.name + ":" + curpagestate.arg
 99	tophid[stash] = resp.Tophid
100	var doc = document.createElement( 'div' );
101	doc.innerHTML = resp.Srvmsg
102	var srvmsg = doc
103	doc = document.createElement( 'div' );
104	doc.innerHTML = resp.Honks
105	var honks = doc.children
106
107	var mecount = document.getElementById("mecount")
108	if (resp.MeCount) {
109		mecount.innerHTML = resp.MeCount
110	} else {
111		mecount.innerHTML = ""
112	}
113	var chatcount = document.getElementById("chatcount")
114	if (resp.ChatCount) {
115		chatcount.innerHTML = resp.ChatCount
116	} else {
117		chatcount.innerHTML = ""
118	}
119
120	var srvel = document.getElementById("srvmsg")
121	while (srvel.children[0]) {
122		srvel.children[0].remove()
123	}
124	srvel.prepend(srvmsg)
125
126	var frontload = true
127	if (curpagestate.name == "convoy") {
128		frontload = false
129	}
130
131	var honksonpage = document.getElementById("honksonpage")
132	var holder = honksonpage.children[0]
133	var lenhonks = honks.length
134	for (var i = honks.length; i > 0; i--) {
135		var h = honks[frontload ? i-1 : 0]
136		if (glowit)
137			h.classList.add("glow")
138		if (frontload) {
139			holder.prepend(h)
140		} else {
141			holder.append(h)
142		}
143	}
144	relinklinks()
145	return lenhonks
146}
147function hydrargs() {
148	var name = curpagestate.name
149	var arg = curpagestate.arg
150	var args = { "page" : name }
151	if (name == "convoy") {
152		args["c"] = arg
153	} else if (name == "combo") {
154		args["c"] = arg
155	} else if (name == "honker") {
156		args["xid"] = arg
157	} else if (name == "user") {
158		args["uname"] = arg
159	}
160	return args
161}
162function refreshupdate(msg) {
163	var el = document.querySelector("#refreshbox p span")
164	if (el) {
165		el.innerHTML = msg
166	}
167}
168function refreshhonks(btn) {
169	removeglow()
170	btn.innerHTML = "refreshing"
171	btn.disabled = true
172	var args = hydrargs()
173	var stash = curpagestate.name + ":" + curpagestate.arg
174	args["tophid"] = tophid[stash]
175	get("/hydra?" + encode(args), function(xhr) {
176		btn.innerHTML = "refresh"
177		btn.disabled = false
178		if (xhr.status == 200) {
179			var lenhonks = fillinhonks(xhr, true)
180			refreshupdate(" " + lenhonks + " new")
181		} else {
182			refreshupdate(" status: " + xhr.status)
183		}
184	}, function(err) {
185		btn.innerHTML = "refresh"
186		btn.disabled = false
187		refreshupdate(err)
188	})
189}
190function statechanger(evt) {
191	var data = evt.state
192	if (!data) {
193		return
194	}
195	switchtopage(data.name, data.arg)
196}
197function switchtopage(name, arg) {
198	var stash = curpagestate.name + ":" + curpagestate.arg
199	var honksonpage = document.getElementById("honksonpage")
200	var holder = honksonpage.children[0]
201	holder.remove()
202	var srvel = document.getElementById("srvmsg")
203	var msg = srvel.children[0]
204	if (msg) {
205		msg.remove()
206		servermsgs[stash] = msg
207	}
208	showelement("refreshbox")
209
210	honksforpage[stash] = holder
211
212	curpagestate.name = name
213	curpagestate.arg = arg
214	// get the holder for the target page
215	stash = name + ":" + arg
216	holder = honksforpage[stash]
217	if (holder) {
218		honksonpage.prepend(holder)
219		msg = servermsgs[stash]
220		if (msg) {
221			srvel.prepend(msg)
222		}
223	} else {
224		// or create one and fill it
225		honksonpage.prepend(document.createElement("div"))
226		var args = hydrargs()
227		get("/hydra?" + encode(args), function(xhr) {
228			if (xhr.status == 200) {
229				fillinhonks(xhr, false)
230			} else {
231				refreshupdate(" status: " + xhr.status)
232			}
233		}, function(err) {
234			refreshupdate(err)
235		})
236	}
237	refreshupdate("")
238}
239function newpagestate(name, arg) {
240	return { "name": name, "arg": arg }
241}
242function pageswitcher(name, arg) {
243	return function(evt) {
244		var topmenu = document.getElementById("topmenu")
245		topmenu.open = false
246		if (name == curpagestate.name && arg == curpagestate.arg) {
247			return false
248		}
249		switchtopage(name, arg)
250		var url = evt.srcElement.href
251		if (!url) {
252			url = evt.srcElement.parentElement.href
253		}
254		history.pushState(newpagestate(name, arg), "some title", url)
255		window.scrollTo(0, 0)
256		return false
257	}
258}
259function relinklinks() {
260	var els = document.getElementsByClassName("convoylink")
261	while (els.length) {
262    var s = (new URL(els[0].href)).search
263    var c = new URLSearchParams(s).get('c')
264		els[0].onclick = pageswitcher("convoy", c)
265		els[0].classList.remove("convoylink")
266	}
267	els = document.getElementsByClassName("combolink")
268	while (els.length) {
269		els[0].onclick = pageswitcher("combo", els[0].text)
270		els[0].classList.remove("combolink")
271	}
272	els = document.getElementsByClassName("honkerlink")
273	while (els.length) {
274		var el = els[0]
275		var xid = el.getAttribute("data-xid")
276		el.onclick = pageswitcher("honker", xid)
277		el.classList.remove("honkerlink")
278	}
279	els = document.getElementsByClassName("donklink")
280	while (els.length) {
281		let el = els[0]
282		el.onclick = function() {
283			el.classList.remove("donk")
284			el.onclick = null
285			return false
286		}
287		el.classList.remove("donklink")
288	}
289
290	els = document.querySelectorAll("#honksonpage article button")
291	els.forEach(function(el) {
292		var honk = el.closest("article")
293		var convoy = honk.dataset.convoy
294		var hname = honk.dataset.hname
295		var xid = honk.dataset.xid
296		var id = Number(honk.dataset.id)
297
298		if (!(id > 0)) {
299			console.error("could not determine honk id")
300			return
301		}
302
303		if (el.classList.contains("unbonk")) {
304			el.onclick = function() {
305				unbonk(el, xid);
306			}
307		} else if (el.classList.contains("bonk")) {
308			el.onclick = function() {
309				bonk(el, xid)
310			}
311		} else if (el.classList.contains("honkback")) {
312			el.onclick = function() {
313				return showhonkform(el, xid, hname)
314			}
315		} else if (el.classList.contains("mute")) {
316			el.onclick = function() {
317				muteit(el, convoy);
318			}
319		} else if (el.classList.contains("evenmore")) {
320			var more = document.querySelector("#evenmore"+id);
321			el.onclick = function() {
322				more.classList.toggle("hide");
323			}
324		} else if (el.classList.contains("zonk")) {
325			el.onclick = function() {
326				zonkit(el, xid);
327			}
328		} else if (el.classList.contains("flogit-deack")) {
329			el.onclick = function() {
330				flogit(el, "deack", xid);
331			}
332		} else if (el.classList.contains("flogit-ack")) {
333			el.onclick = function() {
334				flogit(el, "ack", xid);
335			}
336		} else if (el.classList.contains("flogit-unsave")) {
337			el.onclick = function() {
338				flogit(el, "unsave", xid);
339			}
340		} else if (el.classList.contains("flogit-save")) {
341			el.onclick = function() {
342				flogit(el, "save", xid);
343			}
344		} else if (el.classList.contains("flogit-untag")) {
345			el.onclick = function() {
346				flogit(el, "untag", xid);
347			}
348		} else if (el.classList.contains("flogit-react")) {
349			el.onclick = function() {
350				flogit(el, "react", xid);
351			}
352		}
353	})
354}
355function showhonkform(elem, rid, hname) {
356	var form = lehonkform
357	var donker = document.getElementById("donker")
358	var honknoise = document.getElementById("honknoise")
359	var forminput = document.getElementById("donkinput")
360	forminput.addEventListener('change', () => {
361	  form.submit();
362	});
363	
364	honknoise.addEventListener('paste', e => {
365	  forminput.files = e.clipboardData.files
366	  donker.children[1].textContent = e.clipboardData.files[0].name
367	});
368	
369	form.style = "display: block"
370	form.reset()
371	if (elem) {
372		form.remove()
373		elem.parentElement.parentElement.parentElement.insertAdjacentElement('beforebegin', form)
374	} else {
375		hideelement(lehonkbutton)
376		elem = document.getElementById("honkformhost")
377		elem.insertAdjacentElement('afterend', form)
378	}
379	donker.children[1].textContent = ""
380	var ridinput = document.getElementById("ridinput")
381	if (rid) {
382		ridinput.value = rid
383		if (hname) {
384			honknoise.value = hname + " "
385		} else {
386			honknoise.value = ""
387		}
388	} else {
389		ridinput.value = ""
390		honknoise.value = ""
391	}
392	var updateinput = document.getElementById("updatexidinput")
393	updateinput.value = ""
394	var savedfile = document.getElementById("saveddonkxid")
395	savedfile.value = ""
396	honknoise.focus()
397	return false
398}
399function cancelhonking() {
400	hideelement(lehonkform)
401	showelement(lehonkbutton)
402}
403function showelement(el) {
404	if (typeof(el) == "string")
405		el = document.getElementById(el)
406	if (!el) return
407	el.style.display = "block"
408}
409function hideelement(el) {
410	if (typeof(el) == "string")
411		el = document.getElementById(el)
412	if (!el) return
413	el.style.display = "none"
414}
415function updatedonker(ev) {
416	var el = ev.target.parentElement
417	el.children[1].textContent = el.children[0].value.slice(-20)
418	el = el.nextSibling
419	el.value = ""
420	el = el.parentElement.nextSibling
421	el.style.display = ""
422}
423var checkinprec = 100.0
424var gpsoptions = {
425	enableHighAccuracy: false,
426	timeout: 1000,
427	maximumAge: 0
428};
429function fillcheckin() {
430	if (navigator.geolocation) {
431		navigator.geolocation.getCurrentPosition(function(pos) {
432			showelement("placedescriptor")
433			var el = document.getElementById("placelatinput")
434			el.value = Math.round(pos.coords.latitude * checkinprec) / checkinprec
435			el = document.getElementById("placelonginput")
436			el.value = Math.round(pos.coords.longitude * checkinprec) / checkinprec
437			checkinprec = 10000.0
438			gpsoptions.enableHighAccuracy = true
439			gpsoptions.timeout = 2000
440		}, function(err) {
441			showelement("placedescriptor")
442			var el = document.getElementById("placenameinput")
443			el.value = err.message
444		}, gpsoptions)
445	}
446}
447
448function scrollnexthonk() {
449	var honks = document.getElementsByClassName("honk");
450	for (var i = 0; i < honks.length; i++) {
451		var h = honks[i];
452		var b = h.getBoundingClientRect();
453		if (b.top > 1.0) {
454			h.scrollIntoView()
455			var a = h.querySelector(".actions summary")
456			if (a) a.focus({ preventScroll: true })
457			break
458		}
459	}
460}
461
462function scrollprevioushonk() {
463	var honks = document.getElementsByClassName("honk");
464	for (var i = 1; i < honks.length; i++) {
465		var b = honks[i].getBoundingClientRect();
466		if (b.top > -1.0) {
467			honks[i-1].scrollIntoView()
468			var a = honks[i-1].querySelector(".actions summary")
469			if (a) a.focus({ preventScroll: true })
470			break
471		}
472	}
473}
474
475function hotkey(e) {
476	if (e.ctrlKey || e.altKey)
477		return
478	if (e.code == "Escape") {
479		var menu = document.getElementById("topmenu")
480		menu.open = false
481		return
482	}
483	if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement)
484		return
485
486	switch (e.code) {
487	case "KeyR":
488		refreshhonks(document.getElementById("honkrefresher"));
489		break;
490	case "KeyS":
491		oldestnewest(document.getElementById("newerscroller"));
492		break;
493	case "KeyJ":
494		scrollnexthonk();
495		break;
496	case "KeyK":
497		scrollprevioushonk();
498		break;
499	case "KeyM":
500		var menu = document.getElementById("topmenu")
501		if (!menu.open) {
502			menu.open = true
503			menu.querySelector("a").focus()
504		} else {
505			menu.open = false
506		}
507		break
508	case "Slash":
509		document.getElementById("topmenu").open = true
510		document.getElementById("searchbox").focus()
511		e.preventDefault()
512		break
513	}
514}
515
516document.addEventListener("keydown", hotkey)
517
518function addemu(elem) {
519	const data = elem.alt
520	const box = document.getElementById("honknoise");
521	box.value += data;
522}
523function loademus() {
524	var div = document.getElementById("emupicker")
525	var request = new XMLHttpRequest()
526	request.open('GET', '/emus')
527	request.onload = function() {
528		div.innerHTML = request.responseText
529		div.querySelectorAll(".emu").forEach(function(el) {
530			el.onclick = function() {
531				addemu(el)
532			}
533		})
534	}
535	if (div.style.display === "none") {
536		div.style.display = "block";
537	} else {
538		div.style.display = "none";
539	}
540	request.send()
541}
542
543// init
544(function() {
545	var me = document.currentScript;
546	csrftoken = me.dataset.csrf
547	curpagestate.name = me.dataset.pagename
548	curpagestate.arg = me.dataset.pagearg
549	tophid[curpagestate.name + ":" + curpagestate.arg] = me.dataset.tophid
550	servermsgs[curpagestate.name + ":" + curpagestate.arg] = me.dataset.srvmsg
551
552	var mecountobserver = new MutationObserver(function(mutations) {
553		mutations.forEach(function(mutation) {
554			if (mutation.type == "childList") {
555				var m = mutation.target
556				if (m.innerHTML !== "") {
557					m.style.width = "18px"
558					if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
559						m.style.backgroundColor = "var(--bg-page)"
560						m.style.color = "var(--fg-subtle)"
561					}
562				} else {
563					m.style.width = "0px"
564				}
565			}
566		})
567	})
568
569	mecountobserver.observe(document.getElementById("mecount"), { childList: true })
570
571	var el = document.getElementById("homelink")
572	el.onclick = pageswitcher("home", "")
573	el = document.getElementById("atmelink")
574	el.onclick = pageswitcher("atme", "")
575	el = document.getElementById("firstlink")
576	el.onclick = pageswitcher("first", "")
577	el = document.getElementById("savedlink")
578	el.onclick = pageswitcher("saved", "")
579	el = document.getElementById("longagolink")
580	el.onclick = pageswitcher("longago", "")
581
582	var totop = document.querySelector(".nophone")
583	if (totop) {
584		totop.onclick = function() {
585			window.scrollTo(0,0)
586		}
587	}
588
589	var refreshbox = document.getElementById("refreshbox")
590	if (refreshbox) {
591		refreshbox.querySelectorAll("button").forEach(function(el) {
592			if (el.classList.contains("refresh")) {
593				el.onclick = function() {
594					refreshhonks(el)
595				}
596			} else if (el.classList.contains("scrolldown")) {
597				el.onclick = function() {
598					oldestnewest(el)
599				}
600			}
601		})
602
603		if (me.dataset.srvmsg == "one honk maybe more") {
604			hideelement(refreshbox)
605		}
606	}
607
608	var td = document.getElementById("timedescriptor")
609	document.getElementById("addtimebutton").onclick = function() {
610		td.classList.toggle("hide")
611	}
612	document.getElementById("honkingtime").onclick = function() {
613		return showhonkform()
614	}
615	document.getElementById("checkinbutton").onclick = fillcheckin
616	document.getElementById("emuload").onclick = loademus
617	document.querySelector("#donker input").onchange = updatedonker
618	document.querySelector("button[name=cancel]").onclick = cancelhonking
619
620	relinklinks()
621	window.onpopstate = statechanger
622	history.replaceState(curpagestate, "some title", "")
623
624	hideelement("donkdescriptor")
625})();