PageSourceSearch

https://wpforms.com/wp-content/plugins/am-announce-kit/assets/frontend/js/main.js?ver=1.0.0

js wpforms.com collected 2026-09-24 06:54:09 UTC 21,841 bytes, 694 lines download raw bytes

1"use strict";
2
3(function ($) {
4	'use strict';
5
6	/**
7	 * AM Announce Kit Frontend JavaScript
8	 * Handles drawer functionality and user interactions.
9	 */
10	const AnnouncementDrawer = {
11		// Store timer references to clear them if needed
12		tooltipTimers: [],
13
14		/**
15		 * Initialize the drawer functionality.
16		 */
17		init: function () {
18			this.bindEvents();
19			this.setupDrawer();
20			this.addAnnouncementCountBadge();
21			this.initializeDismissableTooltip();
22		},
23
24		/**
25		 * Cookie management utilities.
26		 */
27		cookies: {
28			set: function(name, value, days) {
29				const date = new Date();
30				date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
31				const expires = "expires=" + date.toUTCString();
32				document.cookie = name + "=" + value + ";" + expires + ";path=/";
33			},
34
35			get: function(name) {
36				const nameEQ = name + "=";
37				const ca = document.cookie.split(';');
38				for(let i = 0; i < ca.length; i++) {
39					let c = ca[i];
40					while (c.charAt(0) === ' ') c = c.substring(1, c.length);
41					if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
42				}
43				return null;
44			},
45
46			remove: function(name) {
47				document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;";
48			}
49		},
50
51		/**
52		 * Add announcement count badge to drawer triggers.
53		 */
54		addAnnouncementCountBadge: function() {
55			// Only add badge if there are announcements
56			if (!amAnnounceKit.hasAnnouncements) {
57				return;
58			}
59
60			// Check if the announcement has been dismissed
61			const latestAnnouncement = amAnnounceKit.latestAnnouncement || null;
62			if (latestAnnouncement && latestAnnouncement.id) {
63				const cookieName = 'am_tooltip_dismissed_' + latestAnnouncement.id;
64				const isDismissed = this.cookies.get(cookieName);
65
66				// Don't show badge if tooltip has been dismissed
67				if (isDismissed) {
68					return;
69				}
70			}
71
72			// Find all drawer trigger elements
73			const triggers = document.querySelectorAll('.am-announce-kit-drawer-trigger, .am-announce-kit-drawer-trigger a');
74
75			triggers.forEach(trigger => {
76				// Skip if this is a container and not the actual clickable element
77				if (trigger.tagName === 'LI' || trigger.tagName === 'DIV') {
78					return;
79				}
80
81				// For links within drawer trigger containers
82				if (!trigger.classList.contains('am-announce-kit-drawer-trigger')) {
83					const parent = trigger.closest('.am-announce-kit-drawer-trigger');
84					if (parent) {
85						trigger.classList.add('am-announce-kit-drawer-trigger');
86					}
87				}
88
89				// Check if badge already exists
90				if (trigger.querySelector('.am-announcement-count-badge')) {
91					return;
92				}
93
94				// Create and add the count badge
95				const badge = document.createElement('span');
96				badge.className = 'am-announcement-count-badge';
97				badge.textContent = '1';
98				trigger.style.position = 'relative';
99				trigger.appendChild(badge);
100			});
101		},
102
103		/**
104		 * Initialize dismissable tooltip for drawer triggers.
105		 */
106		initializeDismissableTooltip: function() {
107			// First, get the latest announcement data from the localized script data
108			const latestAnnouncement = amAnnounceKit.latestAnnouncement || null;
109
110			if (!latestAnnouncement || !latestAnnouncement.title) {
111				return; // No announcement to show
112			}
113
114			// Find all drawer trigger elements (including links within trigger containers)
115			const triggers = document.querySelectorAll('.am-announce-kit-drawer-trigger, .am-announce-kit-drawer-trigger a');
116
117			triggers.forEach(trigger => {
118				// Skip if this is a container and not the actual clickable element
119				if (trigger.tagName === 'LI' || trigger.tagName === 'DIV') {
120					return;
121				}
122
123				// For links within drawer trigger containers
124				if (!trigger.classList.contains('am-announce-kit-drawer-trigger')) {
125					const parent = trigger.closest('.am-announce-kit-drawer-trigger');
126					if (parent) {
127						trigger.classList.add('am-announce-kit-drawer-trigger');
128					}
129				}
130
131				// Check if this specific announcement tooltip has been dismissed
132				const cookieName = 'am_tooltip_dismissed_' + latestAnnouncement.id;
133				const isDismissed = this.cookies.get(cookieName);
134
135				if (!isDismissed) {
136					// Add 3-second delay before showing tooltip
137					const timer = setTimeout(() => {
138						// Double-check cookie wasn't set while we were waiting
139						if (!this.cookies.get(cookieName)) {
140							this.createDismissableTooltip(trigger, latestAnnouncement.title, latestAnnouncement.id);
141						}
142					}, 3000);
143
144					// Store timer reference so we can clear it if needed
145					this.tooltipTimers.push(timer);
146				}
147			});
148		},
149
150		/**
151		 * Create a dismissable tooltip element.
152		 */
153		createDismissableTooltip: function(trigger, title, announcementId) {
154			// Remove any existing tooltip
155			const existingTooltip = trigger.querySelector('.am-dismissable-tooltip');
156			if (existingTooltip) {
157				existingTooltip.remove();
158			}
159
160			// Create tooltip container
161			const tooltip = document.createElement('div');
162			tooltip.className = 'am-dismissable-tooltip';
163			tooltip.setAttribute('role', 'tooltip');
164
165			// Create tooltip content
166			const tooltipInner = document.createElement('div');
167			tooltipInner.className = 'am-dismissable-tooltip__inner';
168
169			// Add NEW badge
170			const newBadge = document.createElement('span');
171			newBadge.className = 'am-dismissable-tooltip__new-badge';
172			newBadge.textContent = 'NEW';
173			tooltipInner.appendChild(newBadge);
174
175			// Add title
176			const tooltipTitle = document.createElement('span');
177			tooltipTitle.className = 'am-dismissable-tooltip__title';
178			tooltipTitle.textContent = title;
179			tooltipInner.appendChild(tooltipTitle);
180
181			// Add dismiss button
182			const dismissBtn = document.createElement('button');
183			dismissBtn.className = 'am-dismissable-tooltip__dismiss';
184			dismissBtn.setAttribute('aria-label', 'Dismiss tooltip');
185			dismissBtn.innerHTML = '<span aria-hidden="true">&times;</span>';
186
187			// Add arrow
188			const arrow = document.createElement('div');
189			arrow.className = 'am-dismissable-tooltip__arrow';
190
191			// Assemble tooltip
192			tooltip.appendChild(tooltipInner);
193			tooltip.appendChild(dismissBtn);
194			tooltip.appendChild(arrow);
195
196			// Position tooltip relative to trigger
197			trigger.style.position = 'relative';
198			trigger.appendChild(tooltip);
199
200			// Show tooltip with animation
201			setTimeout(() => {
202				tooltip.classList.add('am-dismissable-tooltip--visible');
203			}, 100);
204
205			// Auto-hide after 20 seconds (no cookie)
206			const autoHideTimer = setTimeout(() => {
207				tooltip.classList.remove('am-dismissable-tooltip--visible');
208				setTimeout(() => {
209					tooltip.remove();
210				}, 300);
211			}, 20000);
212
213			// Handle dismiss
214			dismissBtn.addEventListener('click', (e) => {
215				e.preventDefault();
216				e.stopPropagation();
217
218				// Clear auto-hide timer
219				clearTimeout(autoHideTimer);
220
221				// Set cookie to remember dismissal
222				const cookieName = 'am_tooltip_dismissed_' + (announcementId || 'default');
223				this.cookies.set(cookieName, '1', 30); // Dismiss for 30 days
224
225				// Hide and remove tooltip
226				tooltip.classList.remove('am-dismissable-tooltip--visible');
227				setTimeout(() => {
228					tooltip.remove();
229				}, 300);
230
231				// Remove count badges from all triggers
232				const badges = document.querySelectorAll('.am-announcement-count-badge');
233				badges.forEach(badge => badge.remove());
234
235				// Track dismissal
236				this.trackEvent('tooltip_dismissed', {
237					announcement_id: announcementId
238				});
239			});
240		},
241
242		/**
243		 * Bind event handlers.
244		 */
245		bindEvents: function () {
246			// Open drawer when trigger is clicked
247			$(document).on('click', '.am-announce-kit-drawer-trigger', this.openDrawer.bind(this));
248
249			// Close drawer when close button is clicked
250			$(document).on('click', '.am-announcement-drawer-widget__close', this.closeDrawer.bind(this));
251
252			// Close drawer when clicking outside
253			$(document).on('click', function (e) {
254				if (!$(e.target).closest('.am-announcement-drawer-widget').length &&
255					!$(e.target).closest('.am-announce-kit-drawer-trigger').length) {
256					AnnouncementDrawer.closeDrawer();
257				}
258
259				// Close feedback form when clicking outside the feedback section
260				if (!$(e.target).closest('.am-announcement-item__feedback').length) {
261					$('.am-announcement-item__feedback').each(function() {
262						const $feedbackSection = $(this);
263						const $selectedReaction = $feedbackSection.find('.am-feedback-reaction--selected');
264
265						if ($selectedReaction.length) {
266							// Remove selected state from all reactions
267							$feedbackSection.find('.am-feedback-reaction').removeClass('am-feedback-reaction--selected');
268
269							// Hide the feedback form
270							$feedbackSection.find('.am-feedback-form').slideUp(300);
271
272							// Clear the reaction value
273							$feedbackSection.find('.am-feedback-form__reaction').val('');
274						}
275					});
276				}
277			});
278
279			// Close drawer on escape key
280			$(document).on('keydown', function (e) {
281				if (e.key === 'Escape') {
282					AnnouncementDrawer.closeDrawer();
283				}
284			});
285
286			// Handle CTA button clicks
287			$(document).on('click', '.am-announcement-item__cta-button', this.handleCTAClick.bind(this));
288
289			// Handle load more button clicks
290			$(document).on('click', '.am-announcement-drawer-widget__load-more-button', this.handleLoadMoreClick.bind(this));
291
292			// Handle feedback reactions
293			$(document).on('click', '.am-feedback-reaction', this.handleFeedbackReaction.bind(this));
294
295			// Handle feedback form submission
296			$(document).on('submit', '.am-feedback-form__container', this.handleFeedbackSubmission.bind(this));
297
298			// Handle subscription component interactions
299			$(document).on('click', '#am-subscription-trigger', this.handleSubscriptionTriggerClick.bind(this));
300			$(document).on('click', '#am-email-subscription-option', this.handleEmailSubscriptionClick.bind(this));
301			$(document).on('click', '#am-push-subscription-option', this.handlePushSub
301scriptionClick.bind(this));
302			$(document).on('click', '#am-email-form-close', this.handleEmailFormClose.bind(this));
303		},
304
305		/**
306		 * Setup the drawer element.
307		 */
308		setupDrawer: function () {
309			const $drawer = $('#am-announcement-drawer');
310			const $overlay = $('#am-announcement-drawer-overlay');
311
312			if ($drawer.length && $overlay.length) {
313				// Initialize load more button with next page
314				const $loadMoreButton = $('#am-announcement-load-more-button');
315				if ($loadMoreButton.length) {
316					$loadMoreButton.data('next-page', 2);
317				}
318			}
319		},
320
321		/**
322		 * Open the announcement drawer.
323		 */
324		openDrawer: function (e) {
325			e.preventDefault();
326
327			const $drawer = $('#am-announcement-drawer');
328			const $overlay = $('#am-announcement-drawer-overlay');
329
330			if ($drawer.length) {
331				// Clear any pending tooltip timers to prevent them from showing
332				this.tooltipTimers.forEach(timer => clearTimeout(timer));
333				this.tooltipTimers = [];
334
335				// Set cookie to dismiss tooltip when drawer is opened
336				if (amAnnounceKit.latestAnnouncement && amAnnounceKit.latestAnnouncement.id) {
337					const cookieName = 'am_tooltip_dismissed_' + amAnnounceKit.latestAnnouncement.id;
338					this.cookies.set(cookieName, '1', 30); // Dismiss for 30 days
339
340					// Remove any visible tooltips
341					const tooltips = document.querySelectorAll('.am-dismissable-tooltip');
342					tooltips.forEach(tooltip => {
343						tooltip.classList.remove('am-dismissable-tooltip--visible');
344						setTimeout(() => {
345							tooltip.remove();
346						}, 300);
347					});
348
349					// Remove count badges from all triggers
350					const badges = document.querySelectorAll('.am-announcement-count-badge');
351					badges.forEach(badge => badge.remove());
352				}
353
354				// Add loading state
355				$drawer.addClass('am-announcement-drawer-widget--loading');
356
357				// Show overlay
358				$overlay.addClass('am-announcement-drawer-overlay--visible');
359
360				// Open drawer
361				$drawer.addClass('am-announcement-drawer-widget--open');
362
363				// Remove loading state after animation
364				setTimeout(function () {
365					$drawer.removeClass('am-announcement-drawer-widget--loading');
366				}, 300);
367
368				// Prevent body scroll
369				$('body').addClass('am-announcement-drawer-open');
370
371				// Track analytics if available
372				this.trackEvent('drawer_opened');
373			}
374		},
375
376		/**
377		 * Close the announcement drawer.
378		 */
379		closeDrawer: function (e) {
380			if (e) {
381				e.preventDefault();
382			}
383
384			const $drawer = $('#am-announcement-drawer');
385			const $overlay = $('#am-announcement-drawer-overlay');
386
387			if ($drawer.length) {
388				// Add closing animation class
389				$drawer.addClass('am-announcement-drawer-widget--closing');
390
391				// Hide overlay
392				$overlay.removeClass('am-announcement-drawer-overlay--visible');
393
394				// Close drawer after animation
395				$drawer.removeClass('am-announcement-drawer-widget--open am-announcement-drawer-widget--closing');
396
397				// Re-enable body scroll
398				$('body').removeClass('am-announcement-drawer-open');
399
400				// Track analytics if available
401				AnnouncementDrawer.trackEvent('drawer_closed');
402			}
403		},
404
405		/**
406		 * Handle call-to-action button clicks.
407		 */
408		handleCTAClick: function (e) {
409			const $button = $(e.currentTarget);
410			const href = $button.attr('href');
411			const announcementId = $button.closest('.am-announcement-item').data('announcement-id');
412
413			// Track CTA click
414			this.trackEvent('cta_clicked', {
415				announcement_id: announcementId,
416				cta_url: href
417			});
418		},
419
420		/**
421		 * Handle load more button clicks.
422		 */
423		handleLoadMoreClick: function (e) {
424			const $button = $(e.currentTarget);
425			const nextPage = $button.data('next-page') || 2;
426
427			this.loadMoreAnnouncements(nextPage);
428		},
429
430		/**
431		 * Load more announcements via AJAX.
432		 */
433		loadMoreAnnouncements: function (page) {
434			const $button = $('.am-announcement-drawer-widget__load-more-button');
435			const $container = $('.am-announcement-drawer-widget__content');
436
437			// Disable button and show loading
438			$button.prop('disabled', true).text(amAnnounceKit.strings.loading || 'Loading...');
439
440			$.ajax({
441				url: amAnnounceKit.ajaxUrl,
442				type: 'POST',
443				data: {
444					action: 'am_announce_kit_load_more',
445					page: page,
446					per_page: 5,
447					nonce: amAnnounceKit.nonce
448				},
449				success: function (response) {
450					if (response.success) {
451						// Append new announcements if there are any
452						if (response.data.html) {
453							$container.append(response.data.html);
454						}
455
456						// Update button for next page
457						const nextPage = response.data.pagination.current_page + 1;
458						$button.data('next-page', nextPage);
459
460						// Disable button if no more pages but keep it visible
461						if (!response.data.pagination.has_more) {
462							$button.prop('disabled', true).text(amAnnounceKit.strings.noM
462oreAnnouncements || 'No More Announcements');
463						} else {
464							$button.prop('disabled', false).text(amAnnounceKit.strings.loadMore || 'Load More');
465						}
466					} else {
467						alert(amAnnounceKit.strings.error || 'An error occurred. Please try again.');
468						$button.prop('disabled', false).text(amAnnounceKit.strings.loadMore || 'Load More');
469					}
470				},
471				error: function () {
472					alert(amAnnounceKit.strings.error || 'An error occurred. Please try again.');
473					$button.prop('disabled', false).text(amAnnounceKit.strings.loadMore || 'Load More');
474				}
475			});
476		},
477
478		/**
479		 * Track events for analytics.
480		 */
481		trackEvent: function (eventName, data = {}) {
482			// Google Analytics 4
483			if (typeof gtag !== 'undefined') {
484				gtag('event', eventName, {
485					event_category: 'announcement_drawer',
486					...data
487				});
488			}
489
490			// Google Analytics Universal
491			if (typeof ga !== 'undefined') {
492				ga('send', 'event', 'announcement_drawer', eventName, data);
493			}
494
495			// Custom event for other tracking
496			$(document).trigger('am_announce_kit_event', {
497				event: eventName,
498				data: data
499			});
500		},
501
502		/**
503		 * Handle feedback reaction clicks.
504		 */
505		handleFeedbackReaction: function (e) {
506			const $button = $(e.currentTarget);
507			const $feedbackSection = $button.closest('.am-announcement-item__feedback');
508			const reaction = $button.data('reaction');
509			const announcementId = $button.closest('.am-announcement-item').data('announcement-id');
510			const wasSelected = $button.hasClass('am-feedback-reaction--selected');
511
512			// If clicking the same emoji that's already selected, close everything
513			if (wasSelected) {
514				// Remove selected state from all reactions
515				$feedbackSection.find('.am-feedback-reaction').removeClass('am-feedback-reaction--selected');
516
517				// Hide the feedback form
518				$feedbackSection.find('.am-feedback-form').slideUp(300);
519
520				// Clear the reaction value
521				$feedbackSection.find('.am-feedback-form__reaction').val('');
522
523				// Track deselection
524				this.trackEvent('feedback_reaction_deselected', {
525					announcement_id: announcementId,
526					reaction: reaction
527				});
528
529				return;
530			}
531
532			// Remove selected state from all reactions
533			$feedbackSection.find('.am-feedback-reaction').removeClass('am-feedback-reaction--selected');
534
535			// Add selected state to clicked reaction
536			$button.addClass('am-feedback-reaction--selected');
537
538			// Set the reaction value in the form
539			$feedbackSection.find('.am-feedback-form__reaction').val(reaction);
540
541			// Show the feedback form
542			$feedbackSection.find('.am-feedback-form').slideDown(300);
543
544			// Focus on the text input
545			setTimeout(() => {
546				$feedbackSection.find('.am-feedback-form__text-input').focus();
547			}, 350);
548
549			// Track reaction selection
550			this.trackEvent('feedback_reaction_selected', {
551				announcement_id: announcementId,
552				reaction: reaction
553			});
554		},
555
556		/**
557		 * Handle feedback form submission.
558		 */
559		handleFeedbackSubmission: function (e) {
560			e.preventDefault();
561
562			const $form = $(e.currentTarget);
563			const $feedbackSection = $form.closest('.am-announcement-item__feedback');
564			const announcementId = $form.data('announcement-id');
565			const feedbackText = $form.find('.am-feedback-form__text-input').val().trim();
566			const reaction = $form.find('.am-feedback-form__reaction').val();
567			const userEmail = $form.find('.am-feedback-form__email-input').val().trim();
568
569			// Validate required fields
570			if (!feedbackText) {
571				alert(amAnnounceKit.strings.feedbackRequired || 'Please enter your feedback.');
572				return;
573			}
574
575			// Check if user is logged in
576			const isLoggedIn = amAnnounceKit.isLoggedIn || false;
577
578			// If not logged in, show email field if not already shown
579			if (!isLoggedIn && !$form.find('.am-feedback-form__email-group').is(':visible')) {
580				$form.find('.am-feedback-form__email-group').slideDown(300);
581				$form.find('.am-feedback-form__email-input').focus();
582				return;
583			}
584
585			// If not logged in, validate email
586			if (!isLoggedIn && !userEmail) {
587				alert(amAnnounceKit.strings.emailRequired || 'Please enter your email address.');
588				return;
589			}
590
591			// Disable submit button
592			const $submitButton = $form.find('.am-feedback-form__submit');
593			$submitButton.prop('disabled', true);
594
595			// Prepare form data
596			const formData = {
597				action: 'am_announce_kit_submit_feedback',
598				announcement_id: announcementId,
599				feedback_text: feedbackText,
600				reaction: reaction,
601				user_email: userEmail,
602				nonce: amAnnounceKit.feedbackNonce
603			};
604
605			// Submit feedback via AJAX
606			$.ajax({
607				url: amAnnounceKit.ajaxUrl,
608				type: 'POST',
609				data: formData,
610				success: function (response) {
611					if (response.success) {
612						// Show success message
613						$feedbackSection.html('<div class="am-feedback-success"><p>' + (amAnnounceKit.strings.feedbackSuccess || 'Thank you for your feedback!') + '</p></div>');
614					} else {
615						alert(amAnnounceKit.strings.feedbackError || 'Failed to submit feedback. Please try again.');
616					}
617				},
618				error: function () {
619					alert(amAnnounceKit.strings.feedbackError || 'Failed to submit feedback. Please try again.');
620				},
621				complete: function () {
622					$submitButton.prop('disabled', false);
623				}
624			});
625		},
626
627		/**
628		 * Handle subscription trigger click.
629		 */
630		handleSubscriptionTriggerClick: function (e) {
631			e.preventDefault();
632			const $options = $('#am-subscription-options');
633			const $trigger = $('#am-subscription-trigger');
634
635			if ($options.hasClass('am-announcement-drawer-widget__subscription-options--visible')) {
636				// Hide options
637				$options.removeClass('am-announcement-drawer-widget__subscription-options--visible');
638				$trigger.removeClass('am-announcement-drawer-widget__subscription-trigger--active');
639			} else {
640				// Show options
641				$options.addClass('am-announcement-drawer-widget__subscription-options--visible');
642				$trigger.addClass('am-announcement-drawer-widget__subscription-trigger--active');
643			}
644		},
645
646		/**
647		 * Handle email subscription option click.
648		 */
649		handleEmailSubscriptionClick: function (e) {
650			e.preventDefault();
651			// Hide the buttons and show the form
652			const $buttons = $('#am-subscription-buttons');
653			const $form = $('#am-email-subscription-form');
654
655			if ($buttons.length && $form.length) {
656				$buttons.hide();
657				$form.show();
658			}
659		},
660
661		/**
662		 * Handle push subscription option click.
663		 */
664		handlePushSubscriptionClick: function (e) {
665			e.preventDefault();
666			// For now, just show a message that push notifications are not implemented yet
667			alert(amAnnounceKit.strings.pushNotificationsNotImplemented || 'Push notifications will be implemented in a future update.');
668		},
669
670		/**
671		 * Handle email form close button click.
672		 */
673		handleEmailFormClose: function (e) {
674			e.preventDefault();
675			// Show the buttons again and hide the form
676			const $buttons = $('#am-subscription-buttons');
677			const $form = $('#am-email-subscription-form');
678
679			if ($buttons.length && $form.length) {
680				$buttons.show();
681				$form.hide();
682			}
683		}
684	};
685
686	// Initialize when document is ready
687	$(document).ready(function () {
688		AnnouncementDrawer.init();
689	});
690
691	// Make available globally
692	window.AnnouncementDrawer = AnnouncementDrawer;
693
694})(jQuery);

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.