jschan - Anonymous imageboard software. Classic look, modern features and feel. Works without JavaScript and supports Tor, I2P, Lokinet, etc.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

141 lines
4.4 KiB

/* globals __ captchaOptions captchaformsection */
const captchaCookieRegex = /captchaid=(.[^;]*)/ig;
class CaptchaController {
constructor() {
this.captchaFields = [];
this.refreshing = false;
}
init() {
this.captchaFields = document.getElementsByClassName('captchafield');
this.refreshing = false;
for (let captcha of this.captchaFields) {
this.setupCaptchaField(captcha);
}
}
captchaAge() {
const captchaCookieMatch = document.cookie.match(captchaCookieRegex);
if (!captchaCookieMatch) { return; }
const cookieParams = new URLSearchParams(captchaCookieMatch[0]);
const captchaIdCookie = cookieParams.get('captchaid');
if (captchaIdCookie) {
const captchaExpiry = new Date(parseInt(captchaIdCookie.slice(0,8),16)*1000);
return (Date.now() - captchaExpiry);
}
}
startRefreshTimer() {
clearTimeout(this.refreshTimer); //this wont throw an error if its null, so no need to check
const captchaAge = this.captchaAge();
if (captchaAge != null) {
console.log('Refreshing captcha in ', 300000-captchaAge);
this.refreshTimer = setTimeout(() => {
this.refreshCaptchas();
}, 300000-captchaAge);
}
}
setupCaptchaField(captcha) {
if (captcha.closest('form').dataset.captchaPreload == 'true') {
return this.loadCaptcha(captcha);
}
if (captchaOptions.type === 'grid' || captchaOptions.type === 'grid2') {
let hoverListener = captcha.closest('details') || captcha;
//captcha.parentElement.previousSibling.previousSibling.tagName === 'SUMMARY' ? captcha.parentElement.previousSibling.previousSibling : captcha.parentElement;
hoverListener.addEventListener('mouseover', () => this.loadCaptcha(captcha), { once: true });
} else { //captchaOptions.type === 'text'
captcha.placeholder = __('focus to load captcha');
captcha.addEventListener('focus', () => this.loadCaptcha(captcha), { once: true });
}
}
refreshCaptchas(e) {
if (this.refreshing) {
return null;
}
this.refreshing = true;
e && e.target.classList.add('spin');
for (let captchacheck of document.querySelectorAll('input[name="captcha"]')) {
captchacheck.checked = false;
}
document.cookie = 'captchaid=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
const xhr = new XMLHttpRequest();
xhr.onload = () => {
this.startRefreshTimer();
for (let captcha of this.captchaFields) {
const existingImage = captcha.previousSibling.children[0];
if (existingImage) {
captcha.previousSibling.children[0].src = xhr.responseURL;
} else {
this.loadCaptcha(captcha, xhr.responseURL);
}
}
this.refreshing = false;
e && e.target.classList.remove('spin');
};
xhr.onerror = () => {
this.refreshing = false;
e && e.target.classList.remove('spin');
};
xhr.open('GET', '/captcha', true);
xhr.send(null);
}
removeCaptcha() {
const postForm = document.getElementById('postform');
const captchaField = postForm.querySelector('.captcha');
if (captchaField) {
//delete the whole row
const captchaRow = captchaField.closest('.row');
captchaRow.remove();
}
}
addMissingCaptcha() {
const postSubmitButton = document.getElementById('submitpost');
const captchaFormSectionHtml = captchaformsection({ captchaOptions });
postSubmitButton.insertAdjacentHTML('beforebegin', captchaFormSectionHtml);
const captchaFormSection = postSubmitButton.previousSibling;
const captchaField = captchaFormSection.querySelector('.captchafield');
this.loadCaptcha(captchaField);
}
loadCaptcha(field, imgSrc = '/captcha') {
const captchaDiv = field.previousSibling;
if (captchaDiv.children.length > 0) {
return;
}
const captchaImg = document.createElement('img');
const refreshDiv = document.createElement('div');
captchaDiv.style.display = '';
captchaImg.style.margin = '0 auto';
captchaImg.style.display = 'flex';
//captchaImg.style.width = '100%';
refreshDiv.classList.add('captcharefresh', 'noselect');
refreshDiv.addEventListener('click', (e) => this.refreshCaptchas(e), true);
refreshDiv.textContent = '↻';
if (captchaOptions.type === 'text') {
field.placeholder = __('loading');
}
captchaImg.src = imgSrc;
captchaImg.onload = () => {
if (captchaOptions.type === 'text') {
field.placeholder = __('Captcha text');
}
captchaDiv.appendChild(captchaImg);
captchaDiv.appendChild(refreshDiv);
this.startRefreshTimer();
};
}
}
const captchaController = new CaptchaController();
window.addEventListener('DOMContentLoaded', () => {
captchaController.init();
});