PageSourceSearch

https://www.insidehook.com/wp-content/client-mu-plugins/ih-tracking/inc/facebook/assets/facebook.js?ver=798

js insidehook.com collected 2026-10-01 08:52:53 UTC 9,200 bytes, 344 lines download raw bytes

1/**
2 * Facebook Tracking Functionality
3 *
4 * Supports both common events (handled in mu-plugins) and site-specific custom events.
5 * Implements deduplication with Facebook Pixel via event_id parameter.
6 */
7
8(function(window) {
9	'use strict';
10
11	const IHFacebookTracking = {
12		config: {},
13		events: {
14			PageView: false,
15			ViewContent: false
16		},
17
18		init: function() {
19			this.config = window.ihFacebookConfig || {};
20			this.handlePageEvents();
21
22			// Handle newsletter signup event.
23			this.handleNewsletterSignup();
24
25			// Listen for pixel events with event IDs for deduplication
26			this.handlePixelEventDeduplication();
27
28			// Handle outbound link tracking
29			this.handleOutboundLinkTracking();
30		},
31
32		handlePageEvents: function() {
33			// Always track page view
34			this.trackPageView();
35
36			// Handle ViewContent based on page type
37			this.handleViewContent();
38		},
39
40		/**
41		 * Generic event tracking method
42		 * @param {string} eventName - Name of the event to track
43		 * @param {Object} eventData - Additional data for the event
44		 * @returns {Promise} - Promise that resolves when tracking is complete
45		 */
46		trackEvent: async function(eventName, eventData = {}) {
47			if ( ! eventName ) {
48				return;
49			}
50
51			const requestData = {
52				event_name: eventName,
53				event_url: window.location.href,
54				...eventData
55			};
56
57			return this.sendToServer('/events', requestData);
58		},
59
60		/**
61		 * Track page view event.
62		 * @returns {void}
63		 */
64		trackPageView: function() {
65			if ( this.events.PageView ) {
66				return; // Prevent duplicate
67			}
68
69			const eventData = {
70				title: document.title,
71				referrer: document.referrer
72			};
73
74			this.trackEvent( 'PageView', eventData );
75			this.events.PageView = true;
76		},
77
78		/**
79		 * Handle ViewContent event.
80		 * @returns {void}
81		 */
82		handleViewContent: function() {
83			if ( ! this.config.isSingle || ! this.config.articleData ) {
84				return;
85			}
86
87			if ( this.events.ViewContent ) {
88				return; // Prevent duplicate
89			}
90
91			this.trackEvent('ViewContent', this.config.articleData);
92			this.events.ViewContent = true;
93		},
94
95		/**
96		 * Handle pixel events with event IDs for deduplication
97		 * Listens for custom events dispatched from analytics.js
98		 * @returns {void}
99		 */
100		handlePixelEventDeduplication: function() {
101			document.addEventListener('fb-conversion-api-event', (event) => {
102				const eventDetail = event.detail;
103
104				// Extract event_id for deduplication
105				const eventId = eventDetail.event_id;
106				const eventName = eventDetail.event_name;
107
108				// Remove event_id from data to avoid sending it as custom property
109				const { event_id, event_name: _, ...eventData } = eventDetail;
110
111				// Send to Conversion API with event_id for deduplication
112				const requestData = {
113					event_name: eventName,
114					event_id: eventId, // Critical for deduplication
115					event_url: window.location.href,
116					...eventData
117				};
118
119				this.sendToServer('/events', requestData);
120			});
121		},
122
123		/**
124		 * Send data to server
125		 * @param {string} endpoint - API endpoint
126		 * @param {Object} data - Data to send
127		 * @returns {Promise} - Promise that resolves with the response
128		 */
129		sendToServer: async function(endpoint, data) {
130			const url = this.config.apiEndpoint + endpoint;
131
132			const headers = {
133				'Accept': 'application/json',
134				'Content-Type': 'application/json',
135			};
136
137			// Present for logged-in users only; without it the endpoint rejects
138			// their requests as a failed cookie check.
139			if ( this.config.nonce ) {
140				headers['X-WP-Nonce'] = this.config.nonce;
141			}
142
143			try {
144				const response = await fetch( url, {
145					method: 'POST',
146					headers,
147					body: JSON.stringify(data)
148				});
149
150				if ( ! response.ok ) {
151					throw new Error(`Failed to track event: ${response.statusText}`);
152				}
153
154				return await response.json();
155			} catch ( error ) {
156				throw error;
157			}
158		},
159
160		/**
161		 * Helper method to attach event listeners for tracking
162		 * @param {string} selector - CSS selector for elements to track
163		 * @param {string} eventType - Type of event to listen for (e.g., 'click')
164		 * @param {string} trackingEventName - Name of the event to track
165		 * @param {Function} dataCallback - Optional callback to get additional event data
166		 */
167		attachTrackingListener: function(selector, eventType, trackingEventName, dataCallback) {
168			document.querySelectorAll(selector).forEach(element => {
169				element.addEventListener(eventType, (event) => {
170					const baseData = {
171						element_id: element.id,
172						element_class: element.className,
173						element_type: element.tagName.toLowerCase()
174					};
175
176					const additionalData = dataCallback ? dataCallback(element, event) : {};
177					this.trackEvent(trackingEventName, { ...baseData, ...additionalData });
178				});
179			});
180		},
181
182		/**
183		 * Handle outbound link tracking
184		 * Tracks clicks on external links and sends events to both Facebook Pixel and CAPI
185		 * @returns {void}
186		 */
187		handleOutboundLinkTracking: function() {
188			// Use event delegation for better performance
189			document.addEventListener('click', (event) => {
190				const link = event.target.closest('a');
191
192				// Only process actual link clicks
193				if (!link || !link.href) {
194					return;
195				}
196
197				// Check if link is external
198				if (!this.isExternalLink(link.href)) {
199					return;
200				}
201
202				// Generate unique event ID for deduplication
203				const eventId = this.generateEventId();
204
205				// Prepare event data
206				const eventData = this.prepareOutboundLinkEventData(link, eventId);
207
208				// Fire Facebook Pixel event
209				if (window.fbq) {
210					window.fbq('trackCustom', 'OutboundLinkClick', {
211						destination_domain: eventData.destination_domain,
212						destination_url: eventData.destination_url,
213						source_page: eventData.source_page,
214						utm_source: eventData.utm_source,
215						utm_medium: eventData.utm_medium,
216						utm_campaign: eventData.utm_campaign,
217						utm_content: eventData.utm_content,
218						referrer: eventData.referrer
219					}, {
220						eventID: eventId
221					});
222				}
223
224				// Send to Conversion API
225				this.sendToServer('/events', eventData).catch(error => {
226					// Silently handle API errors to avoid blocking user experience
227				});
228			});
229		},
230
231		/**
232		 * Check if a URL is external to our current domain
233		 * @param {string} url - URL to check
234		 * @returns {boolean} - True if external, false if internal
235		 */
236		isExternalLink: function(url) {
237			try {
238				const linkHost = new URL(url, window.location.origin).hostname;
239				const currentHost = window.location.hostname;
240
241				return linkHost !== currentHost;
242			} catch (error) {
243				// If URL parsing fails, assume it's internal to be safe
244				return false;
245			}
246		},
247
248		/**
249		 * Prepare event data for outbound link tracking
250		 * @param {HTMLAnchorElement} link - The clicked link element
251		 * @param {string} eventId - Unique event ID for deduplication
252		 * @returns {Object} - Event data object
253		 */
254		prepareOutboundLinkEventData: function(link, eventId) {
255			const destinationUrl = link.href;
256			const destinationDomain = new URL(destinationUrl).hostname;
257			const currentPageUrl = window.location.href;
258			const utmParams = this.extractUtmParameters(currentPageUrl);
259
260			return {
261				event_name: 'OutboundLinkClick',
262				event_id: eventId,
263				event_url: currentPageUrl,
264				destination_domain: destinationDomain,
265				destination_url: destinationUrl,
266				source_page: currentPageUrl,
267				referrer: document.referrer || '',
268				...utmParams
269			};
270		},
271
272		/**
273		 * Extract UTM parameters from current page URL
274		 * @param {string} url - URL to extract UTM parameters from
275		 * @returns {Object} - Object containing UTM parameters
276		 */
277		extractUtmParameters: function(url) {
278			const utmParams = {
279				utm_source: '',
280				utm_medium: '',
281				utm_campaign: '',
282				utm_content: ''
283			};
284
285			try {
286				const urlParams = new URLSearchParams(new URL(url).search);
287
288				Object.keys(utmParams).forEach(param => {
289					const value = urlParams.get(param);
290					if (value) {
291						utmParams[param] = value;
292					}
293				});
294			} catch (error) {
295				// Silently handle UTM parameter extraction errors
296			}
297
298			return utmParams;
299		},
300
301		/**
302		 * Generate a unique event ID for deduplication
303		 * @returns {string} - Unique event ID
304		 */
305		generateEventId: function() {
306			return 'fb_' + Date.now() + '_' + Math.random().toString(36).substring(2, 9);
307		},
308
309		/**
310		 * Handle newsletter signup event.
311		 * @returns {void}
312		 */
313		handleNewsletterSignup: function() {
314			document.addEventListener('ih-newsletter-signup', (event) => {
315				// Get Braze external ID (email hash) from the event
316				const brazeExternalId = event.detail.hash || null;
317
318				const eventData = {
319					source: 'newsletter',
320				};
321
322				// Add Braze external ID if available (this is the key integration)
323				if (brazeExternalId) {
324					eventData.external_id = brazeExternalId;
325				}
326
327				this.trackEvent('CompleteRegistration', eventData);
328			});
329		},
330	};
331
332	// Initialize when DOM is ready.
333	if (document.readyState === 'loading') {
334		document.addEventListener('DOMContentLoaded', () => {
335			IHFacebookTracking.init();
336		});
337	} else {
338		IHFacebookTracking.init();
339	}
340
341	// Expose to global scope.
342	window.IHFacebookTracking = IHFacebookTracking;
343
344})(window);

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.