1document.addEventListener("DOMContentLoaded", function () { 2 // Initialize key DOM elements and helpers for the feedback popup. 3 const { __ } = wp.i18n; 4 const feedbackButton = document.getElementById("basel-feedback-button"); 5 const instructionsButton = document.getElementById("basel-help-button"); 6 const instructionsCloseButton = document.getElementById( 7 "basel-close-help-button" 8 ); 9 const instructionsOverlay = document.getElementById( 10 "basel-instruction-overlay" 11 ); 12 const feedbackPopup = document.getElementById("basel-feedback-popup"); 13 const feedbackBackdrop = document.getElementById("basel-backdrop"); 14 const feedbackNotification = document.getElementById( 15 "basel-feedback-notification" 16 ); 17 const closeButton = document.getElementById("basel-close-button"); 18 19 // Populate the metadata information table from available data attributes. 20 const metaContainer = document.querySelector(".basel-meta-info"); 21 if (metaContainer) { 22 document.getElementById("basel-meta-page-url").textContent = 23 metaContainer.dataset.pageUrl || window.location.href; 24 25 document.getElementById("basel-meta-datetime").textContent = 26 new Date().toLocaleString(); 27 28 document.getElementById("basel-meta-window-size").textContent = 29 window.innerWidth + " x " + window.innerHeight; 30 31 document.getElementById("basel-meta-user").textContent = 32 metaContainer.dataset.user || "Unknown"; 33 34 const sendTicketsAsEl = document.getElementById("basel-meta-send-tickets-as"); 35 if (sendTicketsAsEl) { 36 sendTicketsAsEl.textContent = 37 metaContainer.dataset.sendTicketsAs || ""; 38 } 39 40 document.getElementById("basel-meta-user-agent").textContent = 41 metaContainer.dataset.userAgent || navigator.userAgent; 42 } 43 44 // Update window size metadata on browser resize. 45 window.addEventListener("resize", () => { 46 const windowSizeEl = document.getElementById("basel-meta-window-size"); 47 if (windowSizeEl) { 48 windowSizeEl.textContent = window.innerWidth + " x " + window.innerHeight; 49 } 50 }); 51 52 // Close the feedback popup when the close button is clicked. 53 if (closeButton) { 54 closeButton.addEventListener("click", () => { 55 feedbackPopup.hidden = true; 56 feedbackBackdrop.hidden = true; 57 document.body.style.overflow = ""; 58 }); 59 } 60 61 const toggleInstructionOverlay = () => { 62 const isVisible = !instructionsOverlay.hidden; 63 instructionsOverlay.hidden = isVisible; 64 instructionsOverlay.style.display = isVisible ? "none" : "block"; 65 }; 66 67 if (instructionsButton) { 68 instructionsButton.addEventListener("click", toggleInstructionOverlay); 69 } 70 71 if (instructionsCloseButton) { 72 instructionsCloseButton.addEventListener("click", toggleInstructionOverlay); 73 } 74 75 // Toggle popup and initialize drawing tools when feedback button is clicked. 76 if (feedbackButton && feedbackPopup) { 77 feedbackButton.addEventListener("click", () => { 78 const firstTimeKey = "basel_feedback_popup_opened"; 79 const isFirstTime = !localStorage.getItem(firstTimeKey); 80 81 if (isFirstTime && instructionsButton) { 82 localStorage.setItem(firstTimeKey, "true"); 83 instructionsButton.click(); 84 } 85 86 feedbackPopup.hidden = !feedbackPopup.hidden; 87 feedbackBackdrop.hidden = feedbackPopup.hidden; 88 document.body.style.overflow = feedbackPopup.hidden ? "" : "hidden"; 89 90 // Set currentColor from localized marker color setting 91 let currentColor = basel.marker_color || "#000"; 92 93 // Marker button logic: assign color and handle click for marker 1 94 const markerButton = document.getElementById("basel-marker-1"); 95 if (markerButton) { 96 const markerColor = markerButton.getAttribute("data-color"); 97 markerButton.style.backgroundColor = markerColor; 98 markerButton.addEventListener("click", () => { 99 currentColor = markerColor; 100 }); 101 } 102 // Marker button logic: assign color and handle click for marker 2 103 const markerButton2 = document.getElementById("basel-marker-2"); 104 if (markerButton2) { 105 const markerColor2 = markerButton2.getAttribute("data-color"); 106 markerButton2.style.backgroundColor = markerColor2; 107 markerButton2.addEventListener("click", () => { 108 currentColor = markerColor2; 109 }); 110 } 111 112 let drawing = false;
113 114 const canvas = document.getElementById("basel-canvas"); 115 const context = canvas?.getContext("2d"); 116 117 canvas.addEventListener("mousedown", (e) => { 118 drawing = true; 119 const rect = canvas.getBoundingClientRect(); 120 const scaleX = canvas.width / rect.width; 121 const scaleY = canvas.height / rect.height; 122 123 context.strokeStyle = currentColor; 124 context.lineWidth = 6 * (canvas.width / window.innerWidth); 125 context.lineCap = "round"; 126 context.beginPath(); 127 context.moveTo( 128 (e.clientX - rect.left) * scaleX, 129 (e.clientY - rect.top) * scaleY 130 ); 131 }); 132 133 canvas.addEventListener("mousemove", (e) => { 134 if (!drawing) return; 135 const rect = canvas.getBoundingClientRect(); 136 const scaleX = canvas.width / rect.width; 137 const scaleY = canvas.height / rect.height; 138 context.lineTo( 139 (e.clientX - rect.left) * scaleX, 140 (e.clientY - rect.top) * scaleY 141 ); 142 context.stroke(); 143 }); 144 145 ["mouseup", "mouseleave"].forEach((event) => { 146 canvas.addEventListener(event, () => { 147 drawing = false; 148 }); 149 }); 150 }); 151 } 152 153 // Allow image pasting from the clipboard to the canvas. 154 let uploadedImageDataUrl = ""; 155 const screenshotUploadInput = document.getElementById( 156 "basel-screenshot-upload" 157 ); 158 const uploadDropzone = document.getElementById("basel-upload-dropzone"); 159 const uploadTriggerButton = document.getElementById("basel-upload-trigger"); 160 const uploadWrapper = document.querySelector(".upload-wrapper"); 161 const canvas = document.getElementById("basel-canvas"); 162 const context = canvas?.getContext("2d"); 163 const removeImageButton = document.getElementById( 164 "basel-remove-image-button" 165 ); 166 const removeAnnotationsButton = document.getElementById( 167 "basel-remove-annotations-button" 168 ); 169 170 function showMarkerButtons() { 171 const markerButton = document.getElementById("basel-marker-1"); 172 if (markerButton) { 173 markerButton.hidden = false; 174 } 175 const markerButton2 = document.getElementById("basel-marker-2"); 176 if (markerButton2) { 177 markerButton2.hidden = false; 178 } 179 } 180 181 function loadScreenshotFromDataUrl(dataUrl) { 182 if (!canvas || !context) { 183 return; 184 } 185 186 const img = new Image(); 187 img.onload = () => { 188 canvas.width = img.width; 189 canvas.height = img.height; 190 context.clearRect(0, 0, canvas.width, canvas.height); 191 context.drawImage(img, 0, 0); 192 uploadedImageDataUrl = canvas.toDataURL("image/png"); 193 canvas.hidden = false; 194 if (uploadWrapper) { 195 uploadWrapper.hidden = true; 196 } 197 if (removeImageButton) { 198 removeImageButton.hidden = false; 199 } 200 if (removeAnnotationsButton) { 201 removeAnnotationsButton.hidden = false; 202 } 203 showMarkerButtons(); 204 }; 205 img.src = dataUrl; 206 } 207 208 function loadScreenshotFromFile(file) { 209 if (!file || !file.type.startsWith("image/")) { 210 return; 211 } 212 213 const reader = new FileReader(); 214 reader.onload = (loadEvent) => { 215 loadScreenshotFromDataUrl(loadEvent.target.result); 216 }; 217 reader.readAsDataURL(file); 218 } 219 220 function resetScreenshotUpload() { 221 if (screenshotUploadInput) { 222 screenshotUploadInput.value = ""; 223 } 224 if (uploadDropzone) { 225 uploadDropzone.classList.remove("is-dragover"); 226 } 227 } 228 229 if (removeImageButton && canvas && context) { 230 removeImageButton.addEventListener("click", () => { 231 context.clearRect(0, 0, canvas.width, canvas.height); 232 canvas.width = 400; 233 canvas.height = 300; 234 canvas.hidden = true; 235 if (uploadWrapper) { 236 uploadWrapper.hidden = false; 237 } 238 resetScreenshotUpload(); 239 removeImageButton.hidden = true; 240 if (removeAnnotationsButton) { 241 removeAnnotationsButton.hidden = true; 242 } 243 const markerButton = document.getElementById("basel-marker-1"); 244 if (markerButton) { 245 markerButton.hidden = true; 246 } 247 const markerButton2 = document.getElementById("basel-marker-2"); 248 if (markerButton2) { 249 markerButton2.hidden = true; 250 } 251 }); 252 } 253 254 // Remove annotations button logic 255 if (removeAnnotationsButton && canvas && context) { 256 removeAnnotationsButton.addEventListener("click", () => { 257 if (uploadedImageDataUrl) { 258 const img = new Image(); 259 img.onload = () => { 260 context.clearRect(0, 0, canvas.width, canvas.height); 261 context.drawImage(img, 0, 0); 262 }; 263 img.src = uploadedImageDataUrl; 264 } 265 }); 266 } 267 268 document.addEventListener("paste", (event) => { 269 const items = event.clipboardData?.items; 270 if (!items || !canvas || !context) { 271 return; 272 } 273 274 for (let i = 0; i < items.length; i++) { 275 const item = items[i]; 276 if (item.type.indexOf("image") === 0) { 277 const file = item.getAsFile(); 278 if (!file) { 279 return; 280 } 281 282 loadScreenshotFromFile(file); 283 event.preventDefault(); 284 break; 285 } 286 } 287 }); 288 289 if (screenshotUploadInput && canvas && context) { 290 if (uploadTriggerButton) { 291 uploadTriggerButton.addEventListener("click", (event) => { 292 event.stopPropagation(); 293 screenshotUploadInput.click(); 294 }); 295 } 296 297 if (uploadDropzone) { 298 uploadDropzone.addEventListener("click", (event) => { 299 if (event.target === uploadTriggerButton) { 300 return; 301 } 302 screenshotUploadInput.click(); 303 }); 304 305 uploadDropzone.addEventListener("dragover", (event) => { 306 event.preventDefault(); 307 uploadDropzone.classList.add("is-dragover"); 308 }); 309 310 uploadDropzone.addEventListener("dragleave", () => { 311 uploadDropzone.classList.remove("is-dragover"); 312 }); 313 314 uploadDropzone.addEventListener("drop", (event) => { 315 event.preventDefault(); 316 uploadDropzone.classList.remove("is-dragover"); 317 318 const file = event.dataTransfer?.files?.[0]; 319 if (file) { 320 loadScreenshotFromFile(file); 321 } 322 }); 323 } 324 325 screenshotUploadInput.addEventListener("change", (event) => { 326 const file = event.target.files[0]; 327 loadScreenshotFromFile(file); 328 }); 329 } 330 331 // Submit the feedback form including canvas data and metadata. 332 const submitButton = document.getElementById("basel-feedback-submit-button"); 333 const feedbackTitleField = document.getElementById("feedback-title"); 334 const feedbackField = document.getElementById("feedback"); 335 336 // Enable/disable submit button based on feedback title field content 337 if (feedbackTitleField && submitButton) { 338 feedbackTitleField.addEventListener("input", () => { 339 submitButton.disabled = feedbackTitleField.value.trim().length === 0; 340 }); 341 // Initial state on load 342 submitButton.disabled = feedbackTitleField.value.trim().length === 0; 343 } 344 345 // Remove old generate with AI button logic. 346 347 // Enhance button logic: after enhancement, generate a title and unhide the title field. 348 const enhanceAIButton = document.getElementById("enhance-ai-button"); 349 const feedbackTitleWrapper = document.querySelector( 350 ".feedback-title-wrapper" 351 ); 352 353 if ( 354 enhanceAIButton && 355 feedbackField && 356 feedbackTitleField && 357 feedbackTitleWrapper 358 ) { 359 feedbackField.addEventListener("input", () => { 360 const hasValue = feedbackField.value.trim().length > 0; 361 // feedbackTitleWrapper.hidden = !hasValue; 362 }); 363 } 364 365 if (submitButton && feedbackField) { 366 submitButton.addEventListener("click", function () { 367 // Disable the button and show "Sending..." to prevent multiple submissions. 368 submitButton.disabled = true; 369 submitButton.textContent = __("Verzenden...", "stuurlui-basel"); 370 371 // Retrieve user input values. 372 const feedbackTitle = feedbackTitleField.value; 373 const feedback = feedbackField.value; 374 375 // Capture the current canvas content (including any drawing).
376 const screenshot = canvas 377 ? canvas.toDataURL("image/png") 378 : uploadedImageDataUrl; 379 380 // Show loading spinner 381 const baselFeedbackSpinner = document.getElementById("basel-spinner"); 382 383 // Validate that both title and feedback fields are filled. 384 if (!feedbackTitle) { 385 alert(__("Een titel is vereist om het ticket te verzenden.", "stuurlui-basel")); 386 submitButton.disabled = false; 387 submitButton.textContent = __("Send ticket", "stuurlui-basel"); 388 return; 389 } 390 391 // Display the spinner. 392 baselFeedbackSpinner.hidden = false; 393 394 // Collect meta info from DOM and browser context. 395 const metaContainer = document.querySelector(".basel-meta-info"); 396 const meta = { 397 page_url: metaContainer?.dataset.pageUrl || window.location.href, 398 window_size: 399 document.documentElement.clientWidth + 400 " x " + 401 document.documentElement.clientHeight, 402 datetime: new Date().toLocaleString(), 403 user: metaContainer?.dataset.user || "Unknown", 404 user_agent: metaContainer?.dataset.userAgent || navigator.userAgent, 405 }; 406 407 // Send feedback using AJAX to the backend. 408 fetch(basel.basel_ajax_url, { 409 method: "POST", 410 headers: { 411 "Content-Type": "application/x-www-form-urlencoded", 412 }, 413 body: new URLSearchParams({ 414 action: "basel_send_feedback", 415 feedback, 416 feedback_title: feedbackTitle, 417 screenshot, 418 _ajax_nonce: basel.basel_feedback_nonce, 419 ...meta, 420 }), 421 }) 422 .then(() => { 423 // Reset form and UI after successful submission. 424 submitButton.disabled = false; 425 submitButton.textContent = __("Send feedback", "stuurlui-basel"); 426 baselFeedbackSpinner.hidden = true; 427 feedbackTitleField.value = ""; 428 feedbackField.value = ""; 429 feedbackPopup.hidden = true; 430 feedbackBackdrop.hidden = true; 431 document.body.style.overflow = ""; 432 433 // Reset canvas and annotation tools. 434 context.clearRect(0, 0, canvas.width, canvas.height); 435 canvas.width = 400; 436 canvas.height = 300; 437 canvas.hidden = true; 438 removeImageButton.hidden = true; 439 if (removeAnnotationsButton) removeAnnotationsButton.hidden = true; 440 const markerButton = document.getElementById("basel-marker-1"); 441 if (markerButton) markerButton.hidden = true; 442 if (uploadWrapper) { 443 uploadWrapper.hidden = false; 444 } 445 resetScreenshotUpload(); 446 const markerButton2 = document.getElementById("basel-marker-2"); 447 if (markerButton2) markerButton2.hidden = true; 448 449 // Show temporary confirmation message. 450 if (feedbackNotification) { 451 feedbackNotification.textContent = ""; 452 const supportPortalLink = document.createElement("a"); 453 supportPortalLink.href = 454 "https://support.stuurlui.nl/mijntickets?hsLang=nl"; 455 supportPortalLink.target = "_blank"; 456 supportPortalLink.rel = "noopener noreferrer"; 457 supportPortalLink.textContent = __( 458 "support portaal", 459 "stuurlui-basel" 460 ); 461 feedbackNotification.appendChild( 462 document.createTextNode( 463 __( 464 "Ahoy! We hebben je ticket ontvangen, en gaan ermee aan de slag. Je kunt je ticket inzien in ons ", 465 "stuurlui-basel" 466 ) 467 ) 468 ); 469 feedbackNotification.appendChild(supportPortalLink); 470 feedbackNotification.style.opacity = 1; 471 feedbackNotification.style.pointerEvents = "auto"; 472 setTimeout(() => { 473 feedbackNotification.style.opacity = 0; 474 feedbackNotification.style.pointerEvents = "none"; 475 }, 6000); 476 setTimeout(() => { 477 feedbackNotification.textContent = ""; 478 }, 6500); 479 } 480 }) 481 .catch(() => { 482 // Show error if submission fails. 483 baselFeedbackSpinner.remove(); 484 alert(__("Failed to send feedback.", "stuurlui-basel")); 485 }); 486 }); 487 } 488});
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.