PageSourceSearch

https://hermanbroodacademie.nl/wp-content/plugins/stuurlui-basel/assets/js/script.js?ver=1790943973

js hermanbroodacademie.nl collected 2026-10-02 12:48:55 UTC 15,325 bytes, 488 lines download raw bytes

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.