diff --git a/assets/js/bestellformular.js b/assets/js/bestellformular.js
index 973810b..3566f48 100644
--- a/assets/js/bestellformular.js
+++ b/assets/js/bestellformular.js
@@ -1,160 +1,216 @@
-document.addEventListener('DOMContentLoaded', function () {
- console.log('bestellformular.js loaded')
- // Rest of the code goes here
- const FORMDEBUG = false
- const btn = document.getElementById('bestellformular-btn')
- const bestellformular = document.getElementById('bestellformular')
+// Configuration and Messages
+const debugEnabled = true
+const mouseDebugEnabled = false
+const zsrCheckEnabled = false
+const interactionThreshold = 15 // Time in seconds
+const interactionCountThreshold = 5 // Number of interactions
+let botDetected = false
- // custom Validation messages
- const messagesGerman = {
+const messages = {
+ de: {
required: 'Bitte füllen Sie dieses Feld aus',
email: 'Bitte geben Sie eine gültige E-Mail-Adresse ein',
- minlength: 'Bitte geben Sie mindestens {0} Zeichen ein',
- maxlength: 'Bitte geben Sie maximal {0} Zeichen ein',
- min: 'Bitte geben Sie mindestens {0} ein',
- max: 'Bitte geben Sie maximal {0} ein',
- range: 'Bitte geben Sie zwischen {0} und {1} ein',
- }
- const messagesFrench = {
+ 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',
- minlength: 'Veuillez saisir au moins {0} caractères',
- maxlength: 'Veuillez saisir au plus {0} caractères',
- min: 'Veuillez saisir au moins {0} caractères',
- max: 'Veuillez saisir au plus {0} caractères',
- range: 'Veuillez saisir au moins {0} et au plus {1} caractères',
+ 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')
+
+// 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++
+}
- // custom Validation rules
- // determine which language depending on html lang attribute
- const lang = document.documentElement.lang
- console.log('lang', lang)
- const messages = lang === 'de-DE' ? messagesGerman : messagesFrench
- // set custom validation messages for each validator
- console.log('messages', messages)
+function handleMouseMove(event) {
+ mousePositions.push({ x: event.clientX, y: event.clientY })
+ if (debugEnabled && mouseDebugEnabled) log('Mouse Position:', { x: event.clientX, y: event.clientY })
- let textInputs = document.querySelectorAll('input[type="text"]')
- const emailInput = document.getElementById('email')
+ 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]
- Array.from(textInputs).forEach(function (input) {
- input.addEventListener('invalid', function () {
- this.setCustomValidity(messages['required'])
+ // 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)
+ 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',
})
- })
-
- emailInput.addEventListener('invalid', function () {
- this.setCustomValidity(messages['email'])
- })
-
- // initieiere Zeitmessung zur Botprevention
- var startTime = Date.now()
-
- // Messe ob mit der Seite agiert wird
- var userInteracted = false
-
- function setUserInteracted() {
- var timeSpent = (Date.now() - startTime) / 1000 // Zeit in Sekunden
- if (timeSpent > 5) {
- btn.disabled = false
- }
- userInteracted = true
+ return
}
- setTimeout(function () {
- if (userInteracted) {
- btn.disabled = false
- }
- }, 5000)
- // Eventlistener für Interaktionen - setzt userInteracted auf true bei Interaktion
+
+ 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)
-
- bestellformular.addEventListener('submit', function (e) {
- e.preventDefault()
-
- const form = e.target
- const notification = document.getElementById('notification')
- const zsrTooltip = document.getElementById('zsr-tooltip')
-
- // Spinner und button disabled anzeigen
-
- var endTime = Date.now()
- var timeSpent = (endTime - startTime) / 1000 // Zeit in Sekunden
-
- // Setze die Werte für die Botvalidierung zum Auswerten in PHP
- document.getElementById('age').value = timeSpent
- document.getElementById('hobbies').value = userInteracted ? 'true' : 'false'
-
- // Validierung der ZSR-Nummer
- const zsrNummer = form.elements['zsr_nummer'].value
- const isNumberOrBeantragt = /^\d+$|^beantragt$/i.test(zsrNummer)
- // TODO REGEX für ZSR-Nummer
- if (!isNumberOrBeantragt) {
- // Display error message for invalid zsr_nummer
- zsrTooltip.className = 'input-tooltip'
- // Scroll to the tooltip element
- zsrTooltip.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' })
- return
- }
-
- btn.innerHTML = `
-
- `
-
- btn.disabled = true
-
- if (FORMDEBUG) {
- console.log('userInteracted: ' + userInteracted)
- console.log('timeSpent: ' + timeSpent)
- console.log('hobbies: ' + document.getElementById('hobbies').value)
- console.log('age: ' + document.getElementById('age').value)
- console.log('verify_email(honeypot): ' + document.getElementById('verify_email').value)
- }
-
- // Formulardaten an den Server senden
- const data = new FormData(form)
- 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) => {
- // Erfolgsnachricht anzeigen
- // TODO Nachricht anpassen wenn französische Version
- notification.textContent = 'Bestellformular erfolgreich gesendet!'
- btn.className = 'submitbutton text-white mx-auto submit-after-valid-captchaaaa fadeOut'
- setTimeout(() => {
- btn.style.visibility = 'hidden'
- btn.style.display = 'none'
- notification.style.visibility = 'visible'
- notification.style.display = 'block'
- notification.classList.remove('fadeIn') // Remove fadeIn class
- void notification.offsetWidth
- notification.className = 'bg-green-500 text-white px-4 py-2 rounded block fadeIn'
- }, 1000)
- // setTimeout(() => notification.className = 'bg-green-500 text-white px-4 py-2 rounded hidden', 5000); // Benachrichtigung nach 5 Sekunden ausblenden
- })
- .catch((error) => {
- // Fehlermeldung anzeigen
- notification.textContent = 'Fehler beim Senden der Nachricht.'
- console.log(error)
- notification.className = 'bg-red-500 text-white px-4 py-2 rounded block'
- // setTimeout(() => notification.className = 'bg-red-500 text-white px-4 py-2 rounded hidden', 5000); // Benachrichtigung nach 5 Sekunden ausblenden
- })
+ 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()
})