PageSourceSearch

https://www.baumaniere.com/wp-content/plugins/double-opt-in/core/assets/doi-error-notification.js?ver=5.6.0

js baumaniere.com collected 2026-09-25 14:15:26 UTC 8,610 bytes, 264 lines download raw bytes

1/**
2 * Double Opt-In — Universal Error Notification
3 *
4 * Listens for form submission events from any supported form plugin
5 * and checks the server-side AJAX endpoint for OptIn creation errors.
6 * Displays a toast notification if an error is found.
7 *
8 * @since 4.2.0
9 */
10(function () {
11	'use strict';
12
13	if ( typeof doiErrorNotification === 'undefined' ) {
14		return;
15	}
16
17	var config   = doiErrorNotification;
18	var checking = false;
19	var cooldown = false;
20
21	/**
22	 * Check the AJAX endpoint for a stored submission error.
23	 */
24	function checkForError() {
25		if ( checking || cooldown ) {
26			return;
27		}
28		checking = true;
29
30		var xhr = new XMLHttpRequest();
31		xhr._doiInternal = true; // Prevent the XHR interceptor from re-triggering scheduleCheck
32		xhr.open( 'POST', config.ajaxUrl, true );
33		xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' );
34
35		xhr.onreadystatechange = function () {
36			if ( xhr.readyState !== 4 ) {
37				return;
38			}
39			checking = false;
40
41			if ( xhr.status !== 200 ) {
42				return;
43			}
44
45			try {
46				var response = JSON.parse( xhr.responseText );
47			} catch ( e ) {
48				return;
49			}
50
51			if ( response.success && response.data && response.data.error ) {
52				// Hide form confirmation messages when validation error should be visible
53				if ( response.data.error.hide_confirmation ) {
54					hideFormConfirmations();
55				}
56
57				// Redirect to error page if configured, otherwise show toast
58				if ( response.data.redirect_url ) {
59					window.location.href = response.data.redirect_url;
60					return;
61				}
62
63				showNotification( response.data.error.message, 'error' );
64
65				// Prevent duplicate checks for 5 seconds
66				cooldown = true;
67				setTimeout( function () {
68					cooldown = false;
69				}, 5000 );
70			} else if ( response.success && response.data && response.data.success_message ) {
71				// A "sent — please confirm" message stashed server-side by an
72				// integration that die()s early (Avada) and so can't show the
73				// form plugin's own confirmation. Render it as a success toast.
74				showNotification( response.data.success_message, 'success' );
75
76				cooldown = true;
77				setTimeout( function () {
78					cooldown = false;
79				}, 5000 );
80			}
81		};
82
83		xhr.send(
84			'action=doi_check_submission_error&nonce=' + encodeURIComponent( config.nonce )
85		);
86	}
87
88	/**
89	 * Schedule an error check with a short delay to ensure
90	 * the server-side form processing has completed.
91	 */
92	function scheduleCheck() {
93		setTimeout( checkForError, 800 );
94	}
95
96	/**
97	 * Hide form plugin confirmation/success messages.
98	 *
99	 * Called when a validation error should be visible (block/redirect mode)
100	 * so the user does not see a contradictory success message.
101	 */
102	function hideFormConfirmations() {
103		// WPForms confirmation containers
104		var wpfSelectors = '.wpforms-confirmation-container-full, .wpforms-confirmation-container';
105		document.querySelectorAll( wpfSelectors ).forEach( function ( el ) {
106			el.style.display = 'none';
107		} );
108
109		// Gravity Forms confirmation wrapper
110		document.querySelectorAll( '.gform_confirmation_wrapper' ).forEach( function ( el ) {
111			el.style.display = 'none';
112		} );
113
114		// CF7 success response (only hide the success state, not error state)
115		document.querySelectorAll( '.wpcf7-response-output.wpcf7-mail-sent-ok' ).forEach( function ( el ) {
116			el.style.display = 'none';
117		} );
118
119		// Elementor Forms success message
120		document.querySelectorAll( '.elementor-message.elementor-message-success' ).forEach( function ( el ) {
121			el.style.display = 'none';
122		} );
123	}
124
125	/**
126	 * Show a toast notification.
127	 *
128	 * @param {string} message The message to display.
129	 * @param {string} [type]  'error' (default) or 'success' — controls colour + icon.
130	 */
131	function showNotification( message, type ) {
132		type = type === 'success' ? 'success' : 'error';
133
134		var existing = document.querySelector( '.doi-error-notification' );
135		if ( existing ) {
136			existing.remove();
137		}
138
139		var notification = document.createElement( 'div' );
140		notification.className = 'doi-error-notification';
141		if ( type === 'success' ) {
142			notification.className += ' doi-error-notification--success';
143		}
144		notification.setAttribute( 'role', type === 'success' ? 'status' : 'alert' );
145
146		var content = document.createElement( 'div' );
147		content.className = 'doi-error-notification__content';
148
149		var icon = document.createElement( 'span' );
150		icon.className = 'doi-error-notification__icon';
151		// Check mark for success, warning triangle for error.
152		icon.innerHTML = type === 'success' ? '✓' : '⚠';
153
154		var text = document.createElement( 'p' );
155		text.className = 'doi-error-notification__message';
156		text.textContent = message;
157
158		var closeBtn = document.createElement( 'button' );
159		closeBtn.className = 'doi-error-notification__close';
160		closeBtn.setAttribute( 'type', 'button' );
161		closeBtn.innerHTML = '×';
162		closeBtn.addEventListener( 'click', function () {
163			dismiss( notification );
164		} );
165
166		content.appendChild( icon );
167		content.appendChild( text );
168		content.appendChild( closeBtn );
169		notification.appendChild( content );
170		document.body.appendChild( notification );
171
172		// Trigger enter animation
173		requestAnimationFrame( function () {
174			notification.classList.add( 'doi-error-notification--visible' );
175		} );
176
177		// Auto-dismiss after 10 seconds
178		setTimeout( function () {
179			dismiss( notification );
180		}, 10000 );
181	}
182
183	/**
184	 * Dismiss a notification with exit animation.
185	 *
186	 * @param {HTMLElement} el The notification element.
187	 */
188	function dismiss( el ) {
189		if ( ! el || ! el.parentNode ) {
190			return;
191		}
192		el.classList.remove( 'doi-error-notification--visible' );
193		el.classList.add( 'doi-error-notification--exit' );
194		setTimeout( function () {
195			if ( el.parentNode ) {
196				el.parentNode.removeChild( el );
197			}
198		}, 300 );
199	}
200
201	// -------------------------------------------------------------------------
202	// Form plugin event listeners
203	// -------------------------------------------------------------------------
204
205	// Contact Form 7 (native DOM events)
206	document.addEventListener( 'wpcf7mailsent', scheduleCheck );
207	document.addEventListener( 'wpcf7invalid', scheduleCheck );
208	document.addEventListener( 'wpcf7spam', scheduleCheck );
209	document.addEventListener( 'wpcf7mailfailed', scheduleCheck );
210
211	// WPForms + Gravity Forms (jQuery events — not caught by native addEventListener)
212	if ( typeof jQuery !== 'undefined' ) {
213		jQuery( document ).on( 'wpformsAjaxSubmitSuccess', scheduleCheck );
214		jQuery( document ).on( 'wpformsAjaxSubmitError', scheduleCheck );
215		jQuery( document ).on( 'gform_confirmation_loaded', scheduleCheck );
216	}
217
218	// -------------------------------------------------------------------------
219	// Universal: catch ANY form submission on the page (capture phase)
220	// This is the most reliable way to detect form submissions regardless
221	// of which form plugin is used. It covers AJAX, standard, and custom flows.
222	// -------------------------------------------------------------------------
223	document.addEventListener( 'submit', function () {
224		// Use a longer delay for standard (non-AJAX) submissions
225		// because the server needs time to process the request.
226		setTimeout( checkForError, 1500 );
227	}, true );
228
229	// -------------------------------------------------------------------------
230	// Generic AJAX interception
231	// Catches all AJAX calls to detect form submissions from plugins
232	// that don't fire specific events (e.g., Avada, Elementor).
233	// -------------------------------------------------------------------------
234
235	// Intercept fetch()
236	if ( typeof window.fetch === 'function' ) {
237		var originalFetch = window.fetch;
238		window.fetch = function () {
239			// Capture URL before the async call — `arguments` inside .then()
240			// refers to the callback's own arguments, not the fetch arguments.
241			var fetchUrl = ( typeof arguments[0] === 'string' )
242				? arguments[0]
243				: ( arguments[0] && arguments[0].url ) || '';
244
245			return originalFetch.apply( this, arguments ).then( function ( response ) {
246				if ( fetchUrl && fetchUrl.indexOf( 'admin-ajax.php' ) !== -1 ) {
247					scheduleCheck();
248				}
249				return response;
250			} );
251		};
252	}
253
254	// Intercept XMLHttpRequest for jQuery.ajax based form plugins
255	var origOpen = XMLHttpRequest.prototype.open;
256	XMLHttpRequest.prototype.open = function ( method, url ) {
257		// Skip our own internal error-check requests to prevent infinite polling loops
258		if ( typeof url === 'string' && url.indexOf( 'admin-ajax.php' ) !== -1 && ! this._doiInternal ) {
259			this.addEventListener( 'load', scheduleCheck );
260		}
261		return origOpen.apply( this, arguments );
262	};
263
264})();

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.