function getMessageFromJsonData(success, json) { let message = success ? "Success!" : "Error, please try again later"; let key = success ? "message" : "error"; if (!json || !json[key]) return message; message = json[key]; if (!success && json["details"]) { message = json["details"]; } return message; } function showToast(success, message, isToastTwo=false) { let element = success ? "toast-post-success" : "toast-post-error"; let textElement = element + "-text"; if (isToastTwo) { element = element + "2"; textElement = textElement + "2"; } if (!message) { message = success ? "Success" : "Error, please try again later"; } document.getElementById(textElement).innerText = message; bootstrap.Toast.getOrCreateInstance(document.getElementById(element)).show(); } function createXhrWithFormKey(url, method="POST", form=new FormData()) { const xhr = new XMLHttpRequest(); xhr.open(method, url); xhr.setRequestHeader('xhr', 'xhr'); if (!form) form = new FormData(); form.append("formkey", formkey()); return [xhr, form]; // hacky but less stupid than what we were doing before } function postToast(t, url, data, extraActionsOnSuccess, method="POST") { const isShopConfirm = t.id.startsWith('buy1-') || t.id.startsWith('buy2-') || t.id.startsWith('giveaward') if (!isShopConfirm) { t.disabled = true; t.classList.add("disabled"); } let form = new FormData(); if(typeof data === 'object' && data !== null) { for(let k of Object.keys(data)) { form.append(k, data[k]); } } const xhr = createXhrWithFormKey(url, method, form); xhr[0].onload = function() { let result let message; let success = xhr[0].status >= 200 && xhr[0].status < 300; if (typeof result == "string") { message = result; } else { message = getMessageFromJsonData(success, JSON.parse(xhr[0].response)); } let oldToast = bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-' + (success ? 'error': 'success'))); // intentionally reversed here: this is the old toast oldToast.hide(); showToast(success, message); if (!isShopConfirm) { t.disabled = false; t.classList.remove("disabled"); } if (success && extraActionsOnSuccess) result = extraActionsOnSuccess(xhr[0]); return success; }; xhr[0].send(xhr[1]); } function postToastReload(t, url, method="POST") { postToast(t, url, { }, () => { location.reload() } , method); } function postToastSwitch(t, url, button1, button2, cls, extraActionsOnSuccess, method="POST") { postToast(t, url, { }, (xhr) => { if (button1) { if (typeof(button1) == 'boolean') { location.reload() } else { try { document.getElementById(button1).classList.toggle(cls); } catch (e) {} try { document.getElementById(button2).classList.toggle(cls); } catch (e) {} } } if (typeof extraActionsOnSuccess == 'function') extraActionsOnSuccess(xhr); } , method); } if (!location.pathname.endsWith('/submit')) { document.addEventListener('keydown', (e) => { if(!((e.ctrlKey || e.metaKey) && e.key === "Enter")) return; const targetDOM = document.activeElement; if(!(targetDOM instanceof HTMLTextAreaElement || targetDOM instanceof HTMLInputElement)) return; const formDOM = targetDOM.parentElement; const submitButtonDOMs = formDOM.querySelectorAll('input[type=submit], .btn-primary'); if(submitButtonDOMs.length === 0) throw new TypeError("I am unable to find the submit button :(. Contact the head custodian immediately.") const btn = submitButtonDOMs[0] btn.click(); }); } function autoExpand(field) { xpos=window.scrollX; ypos=window.scrollY; field.style.height = 'inherit'; let computed = window.getComputedStyle(field); let height = parseInt(computed.getPropertyValue('border-top-width'), 10) + parseInt(computed.getPropertyValue('padding-top'), 10) + field.scrollHeight + parseInt(computed.getPropertyValue('padding-bottom'), 10) + parseInt(computed.getPropertyValue('border-bottom-width'), 10); field.style.height = height + 'px'; if (Math.abs(window.scrollX - xpos) < 1 && Math.abs(window.scrollY - ypos) < 1) return; window.scrollTo(xpos,ypos); }; function smoothScrollTop() { window.scrollTo({ top: 0, behavior: 'smooth' }); } // Click navbar to scroll back to top const nav = document.getElementsByTagName('nav') if (nav.length) { nav[0].addEventListener('click', (e) => { if (e.target.id === "navbar" || e.target.classList.contains("container-fluid") || e.target.id == "navbarResponsive" || e.target.id == "logo-container" || e.target.classList.contains("srd")) smoothScrollTop(); }, false); } function formkey() { let formkey = document.getElementById("formkey") if (formkey) return formkey.innerHTML; else return null; } const expandImageModal = document.getElementById('expandImageModal') function expandImage(url) { const e = this.event if(e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) return; e.preventDefault(); if (!url) { url = e.target.dataset.src if (!url) url = e.target.src } document.getElementById("desktop-expanded-image").src = url.replace("200w.webp", "giphy.webp"); document.getElementById("desktop-expanded-image-wrap-link").href = url.replace("200w.webp", "giphy.webp"); bootstrap.Modal.getOrCreateInstance(expandImageModal).show(); }; function bs_trigger(e) { let tooltipTriggerList = [].slice.call(e.querySelectorAll('[data-bs-toggle="tooltip"]')); tooltipTriggerList.map(function(element){ return bootstrap.Tooltip.getOrCreateInstance(element); }); const popoverTriggerList = [].slice.call(e.querySelectorAll('[data-bs-toggle="popover"]')); popoverTriggerList.map(function(popoverTriggerEl) { const popoverId = popoverTriggerEl.getAttribute('data-content-id'); let contentEl; try {contentEl = e.getElementById(popoverId);} catch(t) {contentEl = document.getElementById(popoverId);} if (contentEl) { return bootstrap.Popover.getOrCreateInstance(popoverTriggerEl, { content: contentEl.innerHTML, html: true, }); } }) if (typeof update_speed_emoji_modal == 'function') { let forms = e.querySelectorAll("textarea, .allow-emojis"); forms.forEach(i => { let pseudo_div = document.createElement("div"); pseudo_div.className = "ghostdiv"; pseudo_div.style.display = "none"; i.after(pseudo_div); i.addEventListener('input', update_speed_emoji_modal, false); i.addEventListener('keydown', speed_carot_navigate, false); }); } } function escapeHTML(unsafe) { return unsafe.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); } function showmore(t) { div = t.parentElement.parentElement.parentElement let text = div.getElementsByTagName('d')[0] if (!text) text = div.getElementsByClassName('showmore-text')[0] if (!text) text = div.querySelector('div.d-none') text.classList.add('showmore-text') text.classList.toggle('d-none') if (text.classList.contains('d-none')) t.innerHTML = 'SHOW MORE' else t.innerHTML = 'SHOW LESS' } function formatDate(d) { let year = d.getFullYear(); let monthAbbr = d.toLocaleDateString('en-us', {month: 'short'}); let day = d.getDate(); let hour = ("0" + d.getHours()).slice(-2); let minute = ("0" + d.getMinutes()).slice(-2); let second = ("0" + d.getSeconds()).slice(-2); let tzAbbr = d.toLocaleTimeString('en-us', {timeZoneName: 'short'}).split(' ')[2]; return (day + " " + monthAbbr + " " + year + " " + hour + ":" + minute + ":" + second + " " + tzAbbr); } const timestamps = document.querySelectorAll('[data-time]'); for (const e of timestamps) { e.innerHTML = formatDate(new Date(e.dataset.time*1000)); }; function timestamp(t, ti) { const date = formatDate(new Date(ti*1000)); t.setAttribute("data-bs-original-title", date); }; function areyousure(t) { if (t.value) t.value = 'Are you sure?' else t.innerHTML = t.innerHTML.replace(t.textContent, 'Are you sure?') t.setAttribute("data-onclick", t.dataset.areyousure); if (t.dataset.dismiss) t.setAttribute("data-bs-dismiss", t.dataset.dismiss); } function prepare_to_pause(audio) { for (const e of document.querySelectorAll('video,audio')) { e.addEventListener('play', () => { if (!audio.paused) audio.pause(); }); } document.addEventListener('click', (e) => { if ((e.target.tagName.toLowerCase() == "lite-youtube" || e.target.classList.contains('lty-playbtn')) && !audio.paused) { audio.pause(); } }); } function sendFormXHR(form, extraActionsOnSuccess) { const xhr = new XMLHttpRequest(); formData = new FormData(form); formData.append("formkey", formkey()); actionPath = form.getAttribute("action"); xhr.open("POST", actionPath); xhr.setRequestHeader('xhr', 'xhr'); xhr.onload = function() { if (xhr.status >= 200 && xhr.status < 300) { let data = JSON.parse(xhr.response); showToast(true, getMessageFromJsonData(true, data)); if (extraActionsOnSuccess) extraActionsOnSuccess(xhr); } else { document.getElementById('toast-post-error-text').innerText = "Error, please try again later." try { let data=JSON.parse(xhr.response); bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-error')).show(); document.getElementById('toast-post-error-text').innerText = data["error"]; if (data && data["details"]) document.getElementById('toast-post-error-text').innerText = data["details"]; } catch(e) { bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-success')).hide(); bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-error')).show(); } } }; xhr.send(formData); } function sendFormXHRSwitch(form) { sendFormXHR(form, () => { form.previousElementSibling.classList.remove('d-none'); const days = form.querySelector("input[name=days]") if (!days || !days.value) form.classList.add('d-none'); } ) } let sortAscending = {}; function sort_table(t) { const n = Array.prototype.indexOf.call(t.parentElement.children, t); const table = this.event.target.parentElement.parentElement.parentElement const rows = table.rows; let items = []; for (let i = 1; i < rows.length; i++) { const ele = rows[i]; let x = rows[i].getElementsByTagName("TD")[n]; if (!('sortKey' in x.dataset)) { x = x.getElementsByTagName('a')[0] || x; } let attr; if ('sortKey' in x.dataset) { attr = x.dataset.sortKey; if (/^[\d-,]+$/.test(attr)) { attr = parseInt(attr.replace(/,/g, '')) } } else if ('time' in x.dataset) { attr = parseInt(x.dataset.time); } else { attr = x.innerText if (/^[\d-,]+$/.test(x.innerHTML)) { attr = parseInt(attr.replace(/,/g, '')) } } items.push({ele, attr}); } if (sortAscending[n]) { items.sort((a, b) => a.attr > b.attr ? 1 : -1); sortAscending[n] = false; } else { items.sort((a, b) => a.attr < b.attr ? 1 : -1); sortAscending[n] = true; } for (let i = items.length - 1; i--;) { items[i].ele.parentNode.insertBefore(items[i].ele, items[i + 1].ele); } } if (window.matchMedia('(display-mode: minimal-ui)')['matches']) { const links = document.querySelectorAll('a[data-target="t"]'); for (const link of links) { link.removeAttribute("target"); } } if (document.getElementById('gbrowser').value == 'apple') { const videos = document.querySelectorAll('video') for (const video of videos) { const link = video.src const htmlString = `
${link}
` const div = document.createElement('div'); div.innerHTML = htmlString; video.after(div) } } function logout(t) { postToast(t, '/logout', { }, () => { location.href = '/' }); } const width = (window.innerWidth > 0) ? window.innerWidth : screen.width; function focusSearchBar(element) { if (width >= 768) { element.focus(); } } //FILE SHIT let oldfiles = {}; function handle_files(input, newfiles) { if (!newfiles) return; const ta = input.parentElement.parentElement.parentElement.parentElement.querySelector('textarea.file-ta'); if (oldfiles[ta.id]) { let list = new DataTransfer(); for (const file of oldfiles[ta.id]) { list.items.add(file); } for (const file of newfiles) { list.items.add(file); } input.files = list.files; } else { input.files = newfiles; oldfiles[ta.id] = [] } const span = input.previousElementSibling if (input.files.length > 20) { alert("You can't upload more than 20 files at one time!") input.value = null input.parentElement.nextElementSibling.classList.add('d-none'); span.innerHTML = '' oldfiles[ta.id] = [] return } if (!span.textContent) span.textContent = ' ' for (const file of newfiles) { oldfiles[ta.id].push(file) if (span.innerHTML != ' ') span.innerHTML += ', ' span.innerHTML += file.name.substr(0, 30); if (location.pathname != '/chat') ta.setRangeText(`[${file.name}]`); } autoExpand(ta) input.parentElement.nextElementSibling.classList.remove('d-none') if (typeof checkForRequired === "function") checkForRequired(); } file_upload = document.getElementById('file-upload'); if (file_upload) { function process_url_image() { if (file_upload.files) { const filename = file_upload.files[0].name file_upload.previousElementSibling.textContent = filename.substr(0, 50); for (const s of document.getElementById('IMAGE_FORMATS').value.split(',')) { if (filename.toLowerCase().endsWith(s)) { const fileReader = new FileReader(); fileReader.readAsDataURL(file_upload.files[0]); fileReader.addEventListener("load", function () { document.getElementById('image-preview').setAttribute('src', this.result); document.getElementById('image-preview').classList.remove('d-none'); }); break; } } if (typeof checkForRequired === "function") { document.getElementById('urlblock').classList.add('d-none'); checkForRequired(); } else { document.getElementById('submit-btn').disabled = false; } } } file_upload.onchange = process_url_image } document.onpaste = function(event) { const files = structuredClone(event.clipboardData.files); if (!files.length) return const focused = document.activeElement; let input; if (file_upload) { if (location.pathname.endsWith('/submit') && focused && focused.id == 'post-text') { input = document.getElementById('file-upload-submit') } else { file_upload.files = files; process_url_image(); return; } } else if (focused) { input = focused.parentElement.querySelector('input[type="file"]') } else { input = document.querySelector('input[type="file"]') } event.preventDefault(); handle_files(input, files); } function cancel_files(element) { const input = element.previousElementSibling.querySelector('input[type="file"]'); const span = input.previousElementSibling; const ta = input.parentElement.parentElement.parentElement.parentElement.querySelector('textarea.file-ta'); for (const file of input.files) { ta.value = ta.value.replaceAll(`[${file.name}]`, ""); } ta.value = ta.value.trim(); span.innerHTML = ''; input.value = null; input.parentElement.nextElementSibling.classList.add('d-none'); oldfiles[ta.id] = []; element.classList.add('d-none'); ta.focus(); if (typeof checkForRequired === "function") checkForRequired(); } function handleUploadProgress(e, upload_prog) { const bar = upload_prog.firstElementChild; const percentIndicator = upload_prog.lastElementChild; upload_prog.classList.remove("d-none") if (e.lengthComputable) { const progressPercent = Math.floor((e.loaded / e.total) * 100); bar.value = progressPercent; percentIndicator.textContent = progressPercent + '%'; } } if (width <= 768) { expandImageModal.addEventListener('show.bs.modal', function () { setTimeout(() => { location.hash = "modal"; }, 200); }); expandImageModal.addEventListener('hide.bs.modal', function () { if(location.hash == "#modal") { history.back(); } }); window.addEventListener('hashchange', function () { if(location.hash != "#modal") { const curr_modal = bootstrap.Modal.getInstance(document.getElementsByClassName('show')[0]) if (curr_modal) curr_modal.hide() } }); }