// Configuration and Messages
const debugEnabled = false
const mouseDebugEnabled = false
const zsrCheckEnabled = false
const interactionThreshold = 5 // Time in seconds
const interactionCountThreshold = 5 // Number of interactions
let botDetected = false
const messages = {
de: {
required: 'Bitte füllen Sie dieses Feld aus',
email: 'Bitte geben Sie eine gültige E-Mail-Adresse ein',
success: 'Die Bestellung wurde erfolgreich übermittelt!',
thankyou: 'Vielen Dank für Ihre Bestellung!',
zsrTooltip: 'Bitte geben Sie eine gültige ZSR-Nummer, oder "beantragt" ein.',
captcha: 'Geben Sie den angezeigten Captcha-Code ein',
captchaButton: 'Überprüfen',
},
fr: {
required: 'Veuillez remplir ce champ',
email: 'Veuillez saisir une adresse email valide',
success: 'La commande a bien été envoyée!',
thankyou: 'Merci de votre commande!',
zsrTooltip: 'Veuillez saisir une ZSR-Nummer valide, ou indiquer "demandé".',
captcha: 'Entrez le code Captcha affiché',
captchaButton: 'Vérifier',
},
}
// DOM Selectors
const debugLabel = document.createElement('div')
const submitButton = document.getElementById('bestellformular-btn')
const form = document.querySelector('form#bestellformular')
const notification = document.getElementById('notification')
const zsrTooltip = document.getElementById('zsr-tooltip')
const honeypotInput1 = document.getElementById('age')
const honeypotInput2 = document.getElementById('hobbies')
const verifyEmailInput = document.getElementById('verify_email')
const emailInput = document.getElementById('email')
const textInputs = document.querySelectorAll('input[type="text"]')
const captcha = document.querySelectorAll('.captcha')
const captchaInput = document.querySelectorAll('.captcha-input')
const captchaVerifyButton = document.querySelectorAll('.captcha-verify')
const botBadge = document.createElement('div')
if (debugEnabled) {
botBadge.className = 'bot-badge'
document.body.appendChild(botBadge)
botBadge.setAttribute(
'style',
'position: fixed; top: 0; right: 0; z-index: 9999; background-color: red; color: white; font-weight: bold; height:20px; width:20px'
)
}
// Utility variables
let startTime = Date.now()
let interactionCount = 0
let userInteracted = false
let lastInteractionTime = null
const mousePositions = []
const interactionTimes = []
let isStraightLine = true
// Utility functions
function log(thing) {
console.log(thing)
}
function getCurrentLangMessages() {
log(messages[document.documentElement.lang.split('-')[0]])
return messages[document.documentElement.lang.split('-')[0]]
}
function setUserInteracted() {
const currentTime = Date.now()
if (lastInteractionTime) {
const timeDiff = (currentTime - lastInteractionTime) / 1000 // time in seconds
interactionTimes.push(timeDiff)
log('Time since last interaction: ' + timeDiff + ' seconds')
}
lastInteractionTime = currentTime
userInteracted = true
interactionCount++
checkForBotBehavior()
}
function handleMouseMove(event) {
mousePositions.push({ x: event.clientX, y: event.clientY })
if (debugEnabled && mouseDebugEnabled) log('Mouse Position:', { x: event.clientX, y: event.clientY })
if (mousePositions.length > 2) {
const len = mousePositions.length
const { x: x1, y: y1 } = mousePositions[len - 3]
const { x: x2, y: y2 } = mousePositions[len - 2]
const { x: x3, y: y3 } = mousePositions[len - 1]
// Calculate the area of the triangle formed by three consecutive points
const area = 0.5 * Math.abs(x1 * y2 + x2 * y3 + x3 * y1 - y1 * x2 - y2 * x3 - y3 * x1)
if (debugEnabled && mouseDebugEnabled) log('Triangle Area:', area)
if (area > 0.5) {
// Threshold for detecting non-straight line, adjust as needed
isStraightLine = false
if (debugEnabled && mouseDebugEnabled) log('Detected non-straight line movement.')
}
}
}
function validateZSRNumber(form) {
const zsrNumber = form.elements['zsr_nummer'].value
return /^\d+$|^beantragt$/i.test(zsrNumber)
}
function checkForBotBehavior() {
let timeSpent = (Date.now() - startTime) / 1000
botDetected =
!userInteracted ||
interactionCount === 0 ||
timeSpent < interactionThreshold ||
(isStraightLine && mousePositions.length > 0) ||
honeypotInput1.value !== '' ||
honeypotInput2.value !== '' ||
verifyEmailInput.value !== ''
if (debugEnabled)
if (!botDetected) {
botBadge.style.backgroundColor = 'green'
} else {
botBadge.style.backgroundColor = 'red'
}
console.log(
'Bot Detected: ' +
botDetected +
' userInteracted:' +
userInteracted +
' interactionCount:' +
interactionCount +
' timeSpent:' +
timeSpent +
' isStraightLine:' +
isStraightLine +
' mousePositions:' +
mousePositions.length +
' honeypotInput1:' +
honeypotInput1.value +
' honeypotInput2:' +
honeypotInput2.value +
' verifyEmailInput:' +
verifyEmailInput.value
)
}
function handleSubmit(e) {
e.preventDefault()
const currentMessages = getCurrentLangMessages()
if (zsrCheckEnabled && !validateZSRNumber(form)) {
zsrTooltip.className = 'input-tooltip visible'
zsrTooltip.setAttribute('data-tooltip', currentMessages.zsrTooltip)
zsrTooltip.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'nearest',
})
return
}
checkForBotBehavior()
const data = new FormData(form)
data.append('tra', botDetected)
fetch(form.action, {
method: 'POST',
mode: 'cors',
body: data,
})
.then((response) => {
if (!response.ok) throw new Error('Network response was not ok')
return response.json()
})
.then((data) => {
submitButton.disabled = true
submitButton.innerHTML = `
`
setTimeout(() => {
// if data.success is true, show a success message
if (data.success) {
submitButton.style.display = 'none'
notification.innerHTML = `${currentMessages.thankyou}`
notification.className = 'bg-green-500 text-white px-4 py-2 rounded block'
} else {
submitButton.style.display = 'none'
notification.textContent = data.message
notification.className = 'bg-blue-500 text-white px-4 py-2 rounded block'
}
}, 3000)
})
.catch((error) => {
submitButton.style.display = 'none'
notification.textContent = 'Fehler beim Senden der Nachricht.'
notification.className = 'bg-blue-500 text-white px-4 py-2 rounded block'
console.error(error)
})
}
function init() {
// Event Listeners
document.addEventListener('mousedown', setUserInteracted)
document.addEventListener('touchstart', setUserInteracted)
document.addEventListener('keydown', setUserInteracted)
document.addEventListener('mousemove', handleMouseMove)
form.addEventListener('submit', handleSubmit)
emailInput.addEventListener('invalid', () => {
const currentMessages = getCurrentLangMessages()
emailInput.setCustomValidity(currentMessages.email)
})
textInputs.forEach((input) => {
input.addEventListener('invalid', () => {
const currentMessages = getCurrentLangMessages()
input.setCustomValidity(currentMessages.required)
})
})
log('captchaInput', captchaInput)
captchaInput?.forEach((input) => {
input.setAttribute('placeholder', getCurrentLangMessages().captcha)
})
captchaVerifyButton?.forEach((button) => {
button.textContent = getCurrentLangMessages().captchaButton
})
}
document.addEventListener('DOMContentLoaded', function () {
init()
})