PageSourceSearch

https://www.paidsurveyupdate.com/wp-content/plugins/psu-suite-subs…cribe_v3_ContactIdHandler.js?ver=1743921639

js paidsurveyupdate.com collected 2026-09-27 08:34:50 UTC 9,540 bytes, 298 lines download raw bytes

1/**
2 * @typedef {Object} PSU_Subscribe_v3
3 * @property {Object} classes
4 * @property {typeof ContactIdHandler} classes.ContactIdHandler
5 */
6
7/**
8 * ContactIdHandler
9 * Handles contact ID operations including fetch, set, and analytics integration
10 */
11
12/**
13 * @typedef {Object} jQuery
14 * @property {(settings: jQueryAjaxSettings) => Promise<any>} ajax
15 */
16
17/**
18 * @typedef {Object} jQueryAjaxSettings
19 * @property {string} url
20 * @property {string} method
21 * @property {Object} data
22 */
23
24/**
25 * @external PSU_Subscribe_v3_ContactIdHandler_JSConfig
26 * @global
27 * @property {string} ajaxUrl WordPress AJAX URL
28 * @property {string} nonce Security nonce
29 */
30class PSU_Subscribe_v3_ContactIdHandler {
31    constructor(contactIdHandlerConfig) {
32
33        if (!contactIdHandlerConfig?.ajaxUrl || !contactIdHandlerConfig?.nonce) {
34            throw new Error('[PSU_Subscribe_v3_ContactIdHandler] Missing required configuration');
35        }
36
37        // Load configuration
38        this.contactIdHandlerConfig = contactIdHandlerConfig;
39
40        // Core state
41        this.initializationComplete = false;
42        this.cookieHandler = window.psuCoreCookieHandler;
43
44        // Contact ID state
45        this.contactIdNeededEmailEncoded = null;
46        this.currentAttempt = 0;
47        this.maxAttempts = 5;
48        this.retryDelay = 3000;
49
50        // Initialize immediately but don't block
51        this.initialize().catch(error => {
52            console.error('[PSU_Subscribe_v3_ContactIdHandler] Initialization failed:', error);
53        });
54    }
55
56    /**
57     * Initialize handler operations
58     * @returns {Promise<void>}
59     */
60    async initialize() {
61        if (this.initializationComplete) return;
62
63        try {
64            // Wait for tracker to be ready using shared function
65            const isReady = await psuAnalyticsV1getTrackerWhenReady('PSU_Subscribe_v3_ContactIdHandler');
66            if (!isReady) {
67                console.warn('[PSU_Subscribe_v3_ContactIdHandler] Analytics tracker not available');
68                return;
69            }
70
71            await this.maybeHandleContactID();
72            this.initializationComplete = true;
73        } catch (error) {
74            console.error('[PSU_Subscribe_v3_ContactIdHandler] Initialization error:', error);
75            this.initializationComplete = false;
76        }
77    }
78
79    /**
80     * Process contact ID operations
81     * @returns {Promise<void>}
82     */
83    async maybeHandleContactID() {
84        try {
85            // First check URL parameters
86            if (await this.maybeSetContactIdFromUrl()) {
87                return;
88            }
89
90            // Then check for existing contact ID
91            const existingId = this.getStoredContactId();
92            if (existingId) {
93                console.log('[PSU_Subscribe_v3_ContactIdHandler] Existing contact ID found:', existingId);
94                this.sendContactIdToGoogleAnalytics(existingId);
95                return;
96            }
97
98            // Finally check if we need to fetch a contact
99            const contactIdNeededData = this.cookieHandler.getCookie('CONTACT_ID_NEEDED');
100            if (contactIdNeededData?.email_encoded) {
101                this.contactIdNeededEmailEncoded = contactIdNeededData.email_encoded;
102                await this.fetchContactId();
103            }
104
105        } catch (error) {
106            console.error('[PSU_Subscribe_v3_ContactIdHandler] Error handling contact ID:', error);
107            throw error;
108        }
109    }
110
111    /**
112     * Check URL for contact ID parameter
113     * @returns {Promise<boolean>}
114     */
115    async maybeSetContactIdFromUrl() {
116        const urlParams = new URLSearchParams(window.location.search);
117        const contactId = urlParams.get('psu_contact_id');
118
119        if (contactId) {
120            await this.handleSetContactIdViaUrl(contactId);
121            return true;
122        }
123
124        return false;
125    }
126
127    /**
128     * Set contact ID and emit tracking event
129     * @param {string} contactId GetResponse contact ID to store and track
130     * @returns {Promise<void>}
131     */
132    async handleSetContactIdViaUrl(contactId) {
133        if (!contactId) return;
134
135        this.dispatchSetContactIdViaUrlEvent(contactId);
136        this.saveContactIdToCookie(contactId);
137        this.sendContactIdToGoogleAnalytics(contactId);
138    }
139
140    /**
141     * Emit contact tracking event
142     */
143    dispatchSetContactIdViaUrlEvent(contactId) {
144        if (!contactId) return;
145
146        const event = new CustomEvent('psu_subscribe_v3_contact_id_set_via_url', {
147            detail: {
148                contactId,
149                timestamp: new Date().toISOString()
150            },
151            bubbles: true
152        });
153
154        document.dispatchEvent(event);
155    }
156
157    /**
158     * Fetch contact ID from server with retry logic
159     * @returns {Promise<void>}
160     */
161    async fetchContactId() {
162        if (this.currentAttempt >= this.maxAttempts) {
163            console.warn('[PSU_Subscribe_v3_ContactIdHandler] Max contact ID fetch attempts reached');
164            return;
165        }
166
167        console.log('[PSU_Subscribe_v3_ContactIdHandler] Fetching contact ID...');
168
169        try {
170            const response = await jQuery.ajax({
171                url: this.contactIdHandlerConfig.ajaxUrl,
172                method: 'POST',
173                data: {
174                    action: 'psu_fetch_contact_id',
175                    nonce: this.contactIdHandlerConfig.nonce,
176                    email_encoded: this.contactIdNeededEmailEncoded
177                }
178            });
179
180            if (response.success && response.data.contact_id) {
181                await this.handleContactIdFetched(response.data.contact_id);
182                this.clearContactIdNeeded();
183            } else {
184                await this.scheduleRetry();
185            }
186        } catch (error) {
187            console.error('[PSU_Subscribe_v3_ContactIdHandler] Error fetching contact ID:', error);
188            await this.scheduleRetry();
189        }
190    }
191
192    /**
193     * Handle fetched contact ID
194     * @param {string} contactId GetResponse contact ID to store and track
195     * @returns {Promise<void>}
196     */
197    async handleContactIdFetched(contactId) {
198        if (!contactId) return;
199
200        console.log('[PSU_Subscribe_v3_ContactIdHandler] Contact ID fetched:', contactId);
201
202        this.dispatchContactIdFetchedEvent(contactId);
203        this.saveContactIdToCookie(contactId);
204        this.sendContactIdToGoogleAnalytics(contactId);
205    }
206
207    /**
208     * Emit contact tracking event
209     */
210    dispatchContactIdFetchedEvent(contactId) {
211        if (!contactId) return;
212
213        const event = new CustomEvent('psu_subscribe_v3_contact_id_fetched', {
214            detail: {
215                contactId,
216                email_encoded: this.contactIdNeededEmailEncoded,
217                timestamp: new Date().toISOString()
218            },
219            bubbles: true
220        });
221
222        document.dispatchEvent(event);
223    }
224
225    /**
226     * Store contact ID in cookie
227     * @param {string} contactId
228     */
229    saveContactIdToCookie(contactId) {
230        if (!contactId) return;
231        this.cookieHandler.setCookie('CONTACT_ID', {contact_id: contactId});
232    }
233
234
235    /**
236     * Set user ID in GA4
237     * @param {string} contactId GetResponse contact ID to set as user ID
238     */
239    sendContactIdToGoogleAnalytics(contactId) {
240        if (!contactId) return;
241
242        try {
243            if (typeof gtag === 'function') {
244                gtag('set', 'user_id', contactId);
245                console.log('[PSU_Subscribe_v3_ContactIdHandler] GA4 user_id set:', contactId);
246            } else {
247                console.warn('[PSU_Subscribe_v3_ContactIdHandler] gtag not available');
248            }
249        } catch (error) {
250            console.error('[PSU_Subscribe_v3_ContactIdHandler] Failed to set user ID:', error);
251        }
252    }
253
254    /**
255     * Clear contact ID needed cookie
256     */
257    clearContactIdNeeded() {
258        this.cookieHandler.deleteCookie('CONTACT_ID_NEEDED');
259    }
260
261    /**
262     * Get stored contact ID from cookie
263     * @returns {string|null}
264     */
265    getStoredContactId() {
266        const data = this.cookieHandler.getCookie('CONTACT_ID');
267        return data?.contact_id || null;
268    }
269
270    /**
271     * Schedule retry attempt with exponential backoff
272     * @returns {Promise<void>}
273     */
274    async scheduleRetry() {
275        this.currentAttempt++;
276        const backoffDelay = this.retryDelay * Math.pow(1.5, this.currentAttempt - 1);
277
278        return new Promise(resolve => {
279            setTimeout(() => {
280                this.fetchContactId().then(resolve);
281            }, backoffDelay);
282        });
283    }
284}
285
286/** @type {PSU_Subscribe_v3_ContactIdHandler_JSConfig} */
287window.PSU_Subscribe_v3_ContactIdHandler_JSConfig = window.PSU_Subscribe_v3_ContactIdHandler_JSConfig??{};
288
289// Initialize when document is ready
290jQuery(document).ready(() => {
291    if (typeof window.PSU_Subscribe_v3_ContactIdHandler_JSConfig === 'undefined') {
292        console.error('[PSU_Subscribe_v3_ContactIdHandler] Missing PSU_Subscribe_v3_ContactIdHandler_JSConfig');
293    } else {
294        PSU_Subscribe_v3.contactIdHandler = new PSU_Subscribe_v3_ContactIdHandler(
295            window.PSU_Subscribe_v3_ContactIdHandler_JSConfig
296        );
297    }
298});

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.