1jQuery(document).ready(function ($) { 2 3 const $fileInput = $('#files'); 4 const $fileListDisplay = $('#fileList'); 5 const $fileLabel = $('#fileLabel'); 6 7 let filesArray = []; 8 9 $fileInput.on('change', function () { 10 const newFiles = Array.from(this.files); 11 12 // Only add files that aren't already in the array (based on name + size) 13 newFiles.forEach(file => { 14 if (!filesArray.some(f => f.name === file.name && f.size === file.size)) { 15 filesArray.push(file); 16 addFileToList(file, filesArray.length - 1); // pass index 17 } 18 }); 19 20 updateInputFiles(); 21 }); 22 23 function addFileToList(file, index) { 24 const $li = $('<li>').addClass('file-list-item'); 25 26 const $wrapperDiv = $('<div>') 27 .addClass('file-item') 28 .text(file.name + ' ') 29 .append( 30 $('<button>') 31 .html(` 32 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg> 33 `) 34 .attr('type', 'button') 35 .on('click', function () { 36 filesArray.splice(index, 1); 37 $li.remove(); 38 updateInputFiles(); 39 }) 40 ); 41 42 $li.append($wrapperDiv); 43 $fileListDisplay.append($li); 44 45 // Trigger grow effect 46 setTimeout(() => { 47 $li.addClass('open'); 48 }, 10); 49 } 50 51 function updateInputFiles() { 52 const dataTransfer = new DataTransfer(); 53 filesArray.forEach(file => dataTransfer.items.add(file)); 54 $fileInput[0].files = dataTransfer.files; 55 } 56 57 58 const allowedTypes = ['.jpg', '.png', '.pdf', '.eps', '.ai', '.psd']; 59 60 $fileInput.on('change', function () { 61 const newFiles = Array.from(this.files).filter(file => { 62 const ext = '.' + file.name.split('.').pop().toLowerCase(); 63 return allowedTypes.includes(ext); 64 }); 65 66 67 if (newFiles.length < this.files.length) { 68 alert('Sommige bestanden zijn niet toegestaan.'); 69 return; 70 } 71 72 73 newFiles.forEach(file => { 74 if (!filesArray.some(f => f.name === file.name && f.size === file.size)) { 75 filesArray.push(file); 76 addFileToList(file, filesArray.length - 1); 77 } 78 }); 79 80 updateInputFiles(); 81 }); 82 83 84 $('[data-action="form__submit"]').on('click', function (e) { 85 e.preventDefault(); 86 87 const $form = $(this).closest('form'); 88 const $submitBtn = $(this); 89 90 91 if (!$form[0].checkValidity()) { 92 $form[0].reportValidity(); // triggers native browser messages 93 return; 94 } 95 96 // Disable submit button to prevent double submission 97 $submitBtn.prop('disabled', true).text('Versturen...'); 98 99 grecaptcha.ready(function () { 100 grecaptcha.execute('6Ldie38rAAAAANzQOmQ6DxY0Pc9xPQw_vy_BATP4', { action: 'submit' }).then(function (token) { 101 102 // Set the recaptcha token in the hidden field 103 $('#recaptcha_token').val(token); 104 105 // Create FormData object 106 const formData = new FormData(); 107 108 // Add WordPress specific fields 109 formData.append('action', 'submit_custom_form'); 110 formData.append('nonce', formulier.nonce); 111 112 // Serialize form data and append to FormData 113 const serializedData = $form.serializeArray(); 114 serializedData.forEach(function(field) { 115 formData.append(field.name, field.value); 116 }); 117 118 // Handle file uploads separately 119 const files = $('#files')[0].files; 120 if (files.length > 0) { 121 for (let i = 0; i < files.length; i++) { 122 formData.append('files[]', files[i]); 123 } 124 } 125 126 // Send AJAX request 127 $.ajax({ 128 url: formulier.ajaxurl, 129 method: 'POST', 130 data: formData, 131 processData: false, 132 contentType: false, 133 success: function (response) { 134 console.log('Success:', response); 135 136 // Show success message 137 showMessage('Bericht succesvol verstuurd!', 'success'); 138 139 // Reset form 140 $form[0].reset(); 141 $('#fileList').empty(); 142 $('#fileLabel').text('Drag & Drop je bestanden of Bladeren'); 143 }, 144 error: function (xhr) { 145 console.error('Error:', xhr.responseText); 146 147 // Parse error response 148 let errorMessage = 'Er ging iets mis. Probeer het opnieuw.'; 149 try { 150 const errorData = JSON.parse(xhr.responseText); 151 if (errorData.data) { 152 errorMessage = errorData.data; 153 } 154 } catch (e) { 155 // Use default error message 156 showMessage(errorMessage, 'error'); 157 } 158 159 }, 160 complete: function() { 161 // Re-enable submit button 162 $submitBtn.prop('disabled', false).text('Versturen'); 163 } 164 }); 165 }).catch(function(error) { 166 console.error('reCAPTCHA error:', error); 167 showMessage('reCAPTCHA verificatie mislukt. Probeer het opnieuw.', 'error'); 168 $submitBtn.prop('disabled', false).text('Versturen'); 169 }); 170 }); 171 }); 172 173 174 // Helper function to show messages 175 function showMessage(message, type) { 176 // Remove existing messages 177 $('.form-message').remove(); 178 179 // Create message element 180 const messageClass = type === 'success' ? 'form-message-success' : 'form-message-error'; 181 const messageHtml = `<div class="form-message ${messageClass}">${message}</div>`; 182 183 // Insert message before form 184 $('form').before(messageHtml); 185 186 // Auto-remove success messages after 5 seconds 187 if (type === 'success') { 188 setTimeout(function() { 189 $('.form-message-success').fadeOut(300, function() { 190 $(this).remove(); 191 }); 192 }, 5000); 193 } 194 } 195 196});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.