diff --git a/assets/js/kontaktformular.js b/assets/js/kontaktformular.js
index 5176287..8e29dcf 100644
--- a/assets/js/kontaktformular.js
+++ b/assets/js/kontaktformular.js
@@ -1,152 +1,229 @@
-window.onload = function () {
- const FORMDEBUG = false
- const btn = document.getElementById('kontaktformular-btn')
- const kontaktformular = document.getElementById('kontaktformular')
+// 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
+let currentMessages = []
- // 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 Anfrage.',
+ 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 demande.',
+ 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('kontaktformular-btn')
+const form = document.querySelector('form#kontaktformular')
+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('mail')
+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() {
+ 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 checkForBotBehavior() {
+ let timeSpent = (Date.now() - startTime) / 1000
+ botDetected =
+ !userInteracted ||
+ interactionCount === 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()
+
+ currentMessages = getCurrentLangMessages()
+
+ checkForBotBehavior()
+ if (botDetected) {
+ fakeOut()
+ return false
}
- // 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)
+ const formData = new FormData(form)
+ const formDataEncoded = new URLSearchParams(formData).toString()
+ const formURL = form.action + '.json'
- let textInputs = document.querySelectorAll('input[type="text"]')
- const emailInput = document.getElementById('email')
-
- Array.from(textInputs).forEach(function (input) {
- input.addEventListener('invalid', function () {
- this.setCustomValidity(messages['required'])
+ fetch(formURL, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded', // Wichtig für die Vermeidung von CORS-Problemen
+ },
+ body: formDataEncoded,
+ })
+ .then((response) => {
+ if (!response.ok) {
+ throw new Error('Network response was not ok')
+ }
+ return response.json()
})
- })
-
- 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
- }
- setTimeout(function () {
- if (userInteracted) {
- btn.disabled = false
- }
- }, 5000)
- // Eventlistener für Interaktionen - setzt userInteracted auf true bei Interaktion
+ .then((data) => {
+ submitButton.disabled = true
+ submitButton.innerHTML = `
+
+ `
+ setTimeout(() => {
+ // if data.success is true, show a success message
+ if (data) {
+ 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 = 'Es gab ein Problem mit Ihrer Anfrage'
+ 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 fakeOut() {
+ submitButton.disabled = true
+ submitButton.innerHTML = `
+
+ `
+ setTimeout(() => {
+ submitButton.style.display = 'none'
+ notification.innerHTML = `${currentMessages.thankyou}`
+ notification.className = 'bg-green-500 text-white px-4 py-2 rounded block'
+ }, 3000)
+}
+function init() {
+ // Event Listeners
document.addEventListener('mousedown', setUserInteracted)
document.addEventListener('touchstart', setUserInteracted)
document.addEventListener('keydown', setUserInteracted)
-
- kontaktformular.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'
-
- 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)
- }
-
- // Konvertiere das JSON-Objekt in einen String, um es zu senden
- const formData = new FormData(form)
- const formDataEncoded = new URLSearchParams(formData).toString()
- const formURL = form.action + '.json'
-
- fetch(formURL, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded', // Wichtig für die Vermeidung von CORS-Problemen
- },
- body: formDataEncoded,
+ 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)
})
- .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 = 'Anfrage erfolgreich versendet.'
- 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
- })
+ })
+ log('captchaInput', captchaInput)
+ captchaInput?.forEach((input) => {
+ input.setAttribute('placeholder', getCurrentLangMessages().captcha)
+ })
+ captchaVerifyButton?.forEach((button) => {
+ button.textContent = getCurrentLangMessages().captchaButton
})
}
+
+document.addEventListener('DOMContentLoaded', function () {
+ init()
+})