@ -1203,6 +1203,40 @@ input.invalid, select.invalid, textarea.invalid {
reader.readAsDataURL(file);
}
// Compress image for email (much smaller)
function compressImageForEmail(dataUrl, callback) {
const img = new Image();
img.onload = function() {
const canvas = document.createElement('canvas');
const maxSize = 300; // Max 300x300 for email
let width = img.width;
let height = img.height;
// Scale down to max 300px
if (width > height) {
if (width > maxSize) {
height = height * (maxSize / width);
width = maxSize;
}
} else {
if (height > maxSize) {
width = width * (maxSize / height);
height = maxSize;
}
}
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, width, height);
// Very low quality for email
callback(canvas.toDataURL('image/jpeg', 0.3));
};
img.src = dataUrl;
}
// Remove photo
window.removePhoto = function(fieldId) {
const preview = document.getElementById(fieldId);
@ -1262,7 +1296,7 @@ input.invalid, select.invalid, textarea.invalid {
}
// Use setTimeout to allow UI to update before heavy processing
setTimeout(function() {
setTimeout(async function() {
const data = getFormData();
// Build subject
@ -1283,8 +1317,9 @@ input.invalid, select.invalid, textarea.invalid {
body += 'Versie: ' + CONFIG.version + '\n';
body += 'Datum: ' + formatDateTime(new Date()) + '\n\n';
// Collect photos separately
// Collect photos separately and compress them
const photos = [];
const photoPromises = [];
Object.entries(data).forEach(([key, value]) => {
if (value & & value !== '') {
@ -1292,14 +1327,18 @@ input.invalid, select.invalid, textarea.invalid {
if (typeof value === 'string' & & value.startsWith('data:image/')) {
const label = document.querySelector(`label[for="${key}"]`);
const labelText = label ? label.textContent.replace('*', '').trim() : key;
// Extract extension from data URL (e.g., data:image/jpeg;base64,...)
const mimeMatch = value.match(/data:image\/([a-z]+);/);
const ext = mimeMatch ? mimeMatch[1] : 'jpg';
photos.push({
name: labelText,
extension: ext,
data: value
// Compress photo for email
const promise = new Promise((resolve) => {
compressImageForEmail(value, function(compressedData) {
resolve({
name: labelText,
extension: 'jpg',
data: compressedData
});
});
});
photoPromises.push(promise);
} else {
const label = document.querySelector(`label[for="${key}"]`);
const labelText = label ? label.textContent.replace('*', '').trim() : key;
@ -1309,17 +1348,19 @@ input.invalid, select.invalid, textarea.invalid {
}
});
// Wait for all photos to be compressed
const compressedPhotos = await Promise.all(photoPromises);
body += '\n========================\n';
// Add photos section only if there are photos
if (photos.length > 0) {
body += '\nFOTO BIJLAGEN (BASE64)\n';
body += '========================\n';
body += 'Onderstaande foto\'s zijn gecodeerd in base64 formaat.\n';
body += 'Kopieer de tekst tussen START en EINDE naar een base64 decoder\n';
body += 'of gebruik een online tool zoals base64-image.de\n\n';
if (compressedPhotos.length > 0) {
body += '\nFOTO BIJLAGEN (BASE64 - verkleind voor email)\n';
body += '==============================================\n';
body += 'Foto\'s zijn verkleind naar 300x300px voor email.\n';
body += 'Voor originele kwaliteit, gebruik CSV export.\n\n';
photos.forEach((photo, index) => {
com pressedP hotos.forEach((photo, index) => {
const filename = photo.name.replace(/[^a-zA-Z0-9]/g, '_') + '.' + photo.extension;
body += '--- FOTO ' + (index + 1) + ': ' + filename + ' ---\n';
body += '>>> START BASE64 >>>\n';
@ -1338,9 +1379,9 @@ input.invalid, select.invalid, textarea.invalid {
button.disabled = false;
}
// Check if mailto URL is too long (most clients support ~2000 chars)
if (mailto.length > 100 000) {
showToast('Email te groot door foto. Gebruik CSV export .', 'error');
// Check if mailto URL is too long
if (mailto.length > 64 000) {
showToast('Email nog steeds te groot. Verklein foto of gebruik CSV .', 'error');
return;
}