PageSourceSearch

https://www.commodore.ca/wp-content/plugins/zero-spam/modules/davidwalsh/assets/js/davidwalsh.js?ver=5.7.11

js commodore.ca collected 2026-10-02 04:30:41 UTC 5,051 bytes, 235 lines download raw bytes

1/**
2 * Zero Spam for WordPress David Walsh vanilla JavaScript implementation.
3 *
4 * Handles adding the required functionality for spam detections.
5 * Features:
6 * - No jQuery dependency
7 * - MutationObserver for dynamically loaded forms
8 * - AJAX key refresh for cached pages
9 * - Centralized selector management
10 *
11 * @package ZeroSpam
12 */
13( function() {
14	'use strict';
15
16	/**
17	 * Configuration from WordPress.
18	 *
19	 * @type {Object}
20	 */
21	const config = window.ZeroSpamDavidWalsh || {};
22
23	/**
24	 * Input field name for the David Walsh key.
25	 *
26	 * @type {string}
27	 */
28	const INPUT_NAME = 'zerospam_david_walsh_key';
29
30	/**
31	 * Data attribute to mark protected forms.
32	 *
33	 * @type {string}
34	 */
35	const DATA_ATTR = 'data-zerospam-davidwalsh';
36
37	/**
38	 * Threshold in seconds to trigger AJAX key refresh (12 hours).
39	 *
40	 * @type {number}
41	 */
42	const REFRESH_THRESHOLD = 43200;
43
44	/**
45	 * Current key value (may be updated via AJAX).
46	 *
47	 * @type {string}
48	 */
49	let currentKey = config.key || '';
50
51	/**
52	 * Flag to track if we've already attempted a key refresh.
53	 *
54	 * @type {boolean}
55	 */
56	let keyRefreshAttempted = false;
57
58	/**
59	 * Initialize protection on a single form element.
60	 *
61	 * @param {HTMLFormElement} form - The form element to protect.
62	 */
63	function initForm( form ) {
64		// Skip if already initialized.
65		if ( form.getAttribute( DATA_ATTR ) === 'protected' ) {
66			return;
67		}
68
69		// Skip if no key available.
70		if ( ! currentKey ) {
71			return;
72		}
73
74		// Mark as protected.
75		form.setAttribute( DATA_ATTR, 'protected' );
76
77		// Check if the hidden input already exists.
78		let input = form.querySelector( `input[name="${INPUT_NAME}"]` );
79
80		if ( input ) {
81			// Update existing input's value.
82			input.value = currentKey;
83		} else {
84			// Create and append new hidden input.
85			input = document.createElement( 'input' );
86			input.type = 'hidden';
87			input.name = INPUT_NAME;
88			input.value = currentKey;
89			form.appendChild( input );
90		}
91	}
92
93	/**
94	 * Initialize protection on all matching forms.
95	 */
96	function initAllForms() {
97		if ( ! config.selectors ) {
98			return;
99		}
100
101		try {
102			const forms = document.querySelectorAll( config.selectors );
103			forms.forEach( initForm );
104		} catch ( e ) {
105			// Invalid selector, fail silently.
106			if ( console && console.warn ) {
107				console.warn( 'Zero Spam: Invalid selector in davidwalsh.js', e );
108			}
109		}
110	}
111
112	/**
113	 * Refresh the key via AJAX if it's stale.
114	 *
115	 * @return {Promise<void>}
116	 */
117	async function maybeRefreshKey() {
118		// Only attempt refresh once per page load.
119		if ( keyRefreshAttempted ) {
120			return;
121		}
122
123		// Check if we have the necessary data.
124		if ( ! config.restUrl || ! config.generated ) {
125			return;
126		}
127
128		// Check if key is older than threshold.
129		const now = Math.floor( Date.now() / 1000 );
130		const keyAge = now - config.generated;
131
132		if ( keyAge < REFRESH_THRESHOLD ) {
133			return;
134		}
135
136		keyRefreshAttempted = true;
137
138		try {
139			const response = await fetch( config.restUrl, {
140				method: 'GET',
141			} );
142
143			if ( ! response.ok ) {
144				return;
145			}
146
147			const data = await response.json();
148
149			if ( data && data.key ) {
150				currentKey = data.key;
151				config.generated = data.generated;
152
153				// Update all already-initialized forms with the new key.
154				updateExistingForms();
155			}
156		} catch ( e ) {
157			// Fetch failed, continue with existing key.
158			if ( console && console.warn ) {
159				console.warn( 'Zero Spam: Failed to refresh David Walsh key', e );
160			}
161		}
162	}
163
164	/**
165	 * Update the key value in all already-protected forms.
166	 */
167	function updateExistingForms() {
168		const protectedForms = document.querySelectorAll( `form[${DATA_ATTR}="protected"]` );
169
170		protectedForms.forEach( function( form ) {
171			const input = form.querySelector( `input[name="${INPUT_NAME}"]` );
172			if ( input ) {
173				input.value = currentKey;
174			}
175		} );
176	}
177
178	/**
179	 * Set up MutationObserver to handle dynamically loaded forms.
180	 */
181	function setupMutationObserver() {
182		// Check for MutationObserver support.
183		if ( typeof MutationObserver === 'undefined' ) {
184			return;
185		}
186
187		const observer = new MutationObserver( function( mutations ) {
188			let shouldInit = false;
189
190			mutations.forEach( function( mutation ) {
191				if ( mutation.addedNodes.length > 0 ) {
192					shouldInit = true;
193				}
194			} );
195
196			if ( shouldInit ) {
197				// Debounce the initialization.
198				clearTimeout( observer.timeout );
199				observer.timeout = setTimeout( initAllForms, 100 );
200			}
201		} );
202
203		observer.observe( document.body, {
204			childList: true,
205			subtree: true,
206		} );
207	}
208
209	/**
210	 * Main initialization.
211	 */
212	function init() {
213		// Check for required config.
214		if ( typeof config.key === 'undefined' ) {
215			return;
216		}
217
218		// Initialize all forms on page load.
219		initAllForms();
220
221		// Set up observer for dynamic forms.
222		setupMutationObserver();
223
224		// Attempt key refresh if page might be cached.
225		maybeRefreshKey();
226	}
227
228	// Initialize when DOM is ready.
229	if ( document.readyState === 'loading' ) {
230		document.addEventListener( 'DOMContentLoaded', init );
231	} else {
232		// DOM is already ready.
233		init();
234	}
235} )();

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.