PageSourceSearch

https://www.smithandwollensky.com/wp-content/themes/swus-2022/app/…/js/capi-tracking.js?debug=1790301425

js smithandwollensky.com collected 2026-09-25 02:40:56 UTC 11,230 bytes, 324 lines download raw bytes

1/**
2 * Retrieves CAPI configuration details
3 * Contains all necessary information to communicate with Facebook CAPI
4 * @returns {Object} - An object with pixel ID, access token, endpoint URL, and test code ID
5 */
6function getCapiDetails() {
7	return {
8		pixel: '1453176811645380', // Your Facebook Pixel ID
9		accessToken: 'EAAD6TNPETPkBAMxb1rZBKW6hMQMfeSrNDKntlIygZAnoQzrLFuTTsE2CIldJPb40tQOTGw64JppMcWMnyhkUjfEpS8VDv5fXQBezvZB99zRvemnc1SoV1bGwTwSmblmc0dgLCZAgppHcySr2kBA77cdzDcq1B0ZBKuDVLqqwDQv0i4H44wEr3', // Access token for CAPI
10		endpoint: `https://graph.facebook.com/v10.0/1453176811645380/events`, // Facebook CAPI endpoint
11		testCodeId: 'TEST95264' // Test event code ID
12	};
13}
14
15
16// Wait until the DOM is fully loaded
17document.addEventListener('DOMContentLoaded', () => {
18	// Initialize tracking based on Complianz status
19	if (shouldInitializeTracking()) {
20		console.log('FB CAPI active'); // Log to confirm CAPI is active
21		loadFacebookPixel(); // Load Facebook Pixel
22		initializeEventListeners(); // Start setting up event listeners
23	}
24});
25
26// Listen for Complianz consent changes
27document.addEventListener('cmplz_status_change', function () {
28	if (shouldInitializeTracking()) {
29		console.log('FB CAPI active - consent given'); // Log to confirm CAPI is active
30		loadFacebookPixel(); // Load Facebook Pixel
31		initializeEventListeners(); // Start setting up event listeners
32	} else {
33		// Remove Facebook pixel if consent is withdrawn
34		cleanupFacebookPixel();
35	}
36});
37
38/**
39 * Checks if tracking should be initialized based on Complianz consent
40 * @returns {boolean} Whether tracking should be initialized
41 */
42function shouldInitializeTracking() {
43
44	if (navigator.globalPrivacyControl) {
45		console.log('globalPrivacyControl detected - stopping CAPI');
46		return false;
47	}
48
49	// Check if Complianz functions exist and marketing cookies are accepted
50	if (typeof cmplz_accepted_categories === 'function') {
51		return cmplz_accepted_categories().includes('marketing');
52	}
53	// Alternative method using cmplz_is_bot and cmplz_has_consent
54	if (typeof cmplz_has_consent === 'function') {
55		return !cmplz_is_bot() && cmplz_has_consent('marketing');
56	}
57	return false; // Default to not tracking if Complianz not detected
58}
59
60/**
61 * Initializes all event listeners
62 * Sets up tracking for specific elements based on predefined selectors
63 */
64function initializeEventListeners() {
65	basicEventAjax('PageView');
66
67	// Book a table widget submit
68	const bookingSubmitButtons = document.querySelectorAll('.js__booking-submit');
69	if (bookingSubmitButtons) {
70		bookingSubmitButtons.forEach(button => {
71			button.addEventListener('click', function () {
72				basicEventAjax('Schedule');
73			});
74		});
75	}
76
77	// Private events enquiry initiated
78	// Fire event when a call is made on a private events page
79	if (window.location.href.indexOf('/private-events') != -1) {
80		const contactPhoneLinks = document.querySelectorAll('a[href*="tel:"]');
81		if (contactPhoneLinks) {
82			contactPhoneLinks.forEach(link => {
83				link.addEventListener('click', function () {
84					basicEventAjax('Contact - Phone');
85				});
86			});
87		}
88	}
89
90	// Private events enquiry initiated
91	// Fire event when link with tripleseat URL is clicked
92	const tripleseatLinks = document.querySelectorAll('a[href*="tripleseat.com"]');
93	if (tripleseatLinks) {
94		tripleseatLinks.forEach(link => {
95			link.addEventListener('click', function () {
96				basicEventAjax('PE Inquiry Link Clicked');
97			});
98		});
99	}
100
101
102	const newsletterForms = document.querySelectorAll('.m__newsletter--reduced .m__newsletter__form');
103	if (newsletterForms) {
104		newsletterForms.forEach(form => {
105			form.addEventListener('submit', function (e) {
106				e.preventDefault();
107				basicEventAjax('newsletter sign up');
108			});
109		});
110	}
111}
112
113/**
114 * Checks if the newsletter signup conditions are met
115 * Ensures that the privacy checkbox is checked and an email is provided
116 * @returns {boolean}
117 */
118function checkNewsletterSignup() {
119	const checked = document.querySelector('#mailingList')?.checked;
120	const emailValue = document.querySelector('#email')?.value;
121	return emailValue && checked; // Only return true if both email and checkbox are present
122}
123
124/**
125 * Sends a basic event to Facebook CAPI
126 * Handles both Pixel and CAPI event sending
127 * @param {string} eventName - The name of the event to send
128 */
129function basicEventAjax(eventName) {
130	const eventID = generateEventID(); // Create a unique ID for this event
131
132	// Send event to Facebook Pixel if fbq is available
133	if (typeof fbq === 'function') {
134		fbq('track', eventName, {}, { eventID });
135	}
136
137	// Send event to Facebook CAPI
138	sendBasicEvent({
139		event_name: eventName,
140		event_source: window.location.href,
141		event_id: eventID,
142		fbp: getCookie('_fbp'), // Facebook Pixel ID from cookies
143		fbc: getCookie('_fbc')  // Facebook Click ID from cookies
144	})
145		.then(() => console.log(`Event '${eventName}' sent.`)) // Log success
146		.catch(err => console.error(err)); // Log any errors
147}
148
149/**
150 * Generates a unique event ID
151 * Combines random characters to ensure uniqueness
152 * @returns {string}
153 */
154function generateEventID() {
155	const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
156	let result = '';
157	for (let i = 0; i < 23; i++) {
158		result += chars.charAt(Math.floor(Math.random() * chars.length));
159	}
160	return result; // Returns a 23-character unique ID
161}
162
163/**
164 * Retrieves a cookie value by name
165 * Handy for getting Facebook cookies like _fbp and _fbc
166 * @param {string} name - The name of the cookie to retrieve
167 * @returns {string|null} - The value of the cookie or null if not found
168 */
169function getCookie(name) {
170	const nameEQ = `${name}=`;
171	const cookies = document.cookie.split(';');
171 // Split all cookies
172	for (let cookie of cookies) {
173		cookie = cookie.trim(); // Remove any extra spaces
174		if (cookie.startsWith(nameEQ)) {
175			return cookie.substring(nameEQ.length); // Return the cookie value
176		}
177	}
178	return null; // Return null if not found
179}
180
181/**
182 * Sends the basic event to Facebook CAPI
183 * Handles the actual network request to Facebook's API
184 * @param {Object} data - The event data to send
185 */
186async function sendBasicEvent(data) {
187	const { accessToken, testCodeId, endpoint } = getCapiDetails(); // Get CAPI config details
188	const eventData = getBaseEventData(data.event_name, data.event_source, data.event_id, { fbp: data.fbp, fbc: data.fbc }); // Construct event data
189
190	// Prepare the data to be sent in the POST request
191	const postFields = new URLSearchParams({
192		data: JSON.stringify(eventData),
193		access_token: accessToken,
194		test_event_code: testCodeId
195	});
196
197	try {
198		// Make the POST request to Facebook's CAPI endpoint
199		const response = await fetch(endpoint, {
200			method: 'POST',
201			headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
202			body: postFields.toString()
203		});
204		const result = await response.text(); // Get the response text
205		console.log(result); // Log the result for debugging
206	} catch (error) {
207		console.error('Error sending event:', error); // Log any errors that occur
208	}
209}
210
211/**
212 * Constructs the base event data
213 * Prepares the data structure required by Facebook CAPI
214 * @param {string} eventName - Name of the event
215 * @param {string} eventSource - URL where the event occurred
216 * @param {string} eventId - Unique ID for the event
217 * @param {Object} fbCookies - Object containing fbp and fbc cookie values
218 * @returns {Array<Object>|false} - Returns an array with event data or false if eventName is missing
219 */
220function getBaseEventData(eventName, eventSource, eventId, fbCookies) {
221	if (!eventName) return false; // If no event name, exit early
222
223	const pageUrl = eventSource || `${window.location.origin}${window.location.pathname}`; // Determine the page URL
224	const baseData = {
225		event_name: eventName,
226		event_time: Math.floor(Date.now() / 1000), // Current timestamp in seconds
227		action_source: 'website', // Source of the action
228		event_id: eventId || generateDedupeEventID(), // Use existing event ID or generate a new one
229		event_source_url: pageUrl,
230		user_data: {
231			client_user_agent: navigator.userAgent, // Browser info
232			client_ip_address: scriptVars.ipAddress || '', // User's IP address
233			fbc: fbCookies.fbc, // Facebook Click ID
234			fbp: fbCookies.fbp, // Facebook Pixel ID
235		}
236	};
237
238	return [baseData]; // Return as an array as required by Facebook CAPI
239}
240
241/**
242 * Generates a deduplication event ID
243 * Ensures that each event has a unique ID to prevent duplicates
244 * @returns {string} - A unique event ID combining timestamp and random hex
245 */
246function generateDedupeEventID() {
247	const timestamp = Date.now(); // Current timestamp in milliseconds
248	const randomBytes = crypto.getRandomValues(new Uint8Array(15)); // Generate 15 random bytes
249	const randomHex = Array.from(randomBytes).map(b => b.toString(16).padStart(2, '0')).join(''); // Convert to hex string
250	return `${timestamp}${randomHex}`; // Combine timestamp and random hex
251}
252
253/**
254 * Dynamically loads the Facebook Pixel script
255 */
256function loadFacebookPixel() {
257	// Skip if already loaded
258	if (window.fbq) return;
259
260	// Get pixel ID from CAPI details
261	const pixelId = getCapiDetails().pixel;
262
263	// Create and add Facebook Pixel base code
264	const pixelScript = document.createElement('script');
265	pixelScript.innerHTML = `
266		!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
267		n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
268		n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
269		t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
270		document,'script','https://connect.facebook.net/en_US/fbevents.js');
271		
272		fbq.disablePushState = true;
273		fbq.allowDuplicatePageViews = true;
274		fbq('init', '${pixelId}');
275	`;
276	document.head.appendChild(pixelScript);
277
278	// Create and add noscript pixel image 
279	const noscriptPixel = document.createElement('noscript');
280	noscriptPixel.innerHTML = `<img height="1" width="1" style="display:none" 
281		src="https://www.facebook.com/tr?id=${pixelId}&ev=PageView&noscript=1"/>`;
282	document.body.appendChild(noscriptPixel);
283
284	console.log('Facebook Pixel loaded dynamically');
285}
286
287/**
288 * Removes all Facebook Pixel scripts and resets FB variables
289 */
290function cleanupFacebookPixel() {
291	// Remove any Facebook pixel scripts
292	const scripts = document.querySelectorAll('script[src*="facebook"], script[src*="fbevents"]');
293	scripts.forEach(script => script.remove());
294
295	// Also remove inline scripts we added
296	const allScripts = document.querySelectorAll('script');
297	allScripts.forEach(script => {
298		if (script.innerHTML.includes('fbq(') || script.innerHTML.includes('fbevents.js')) {
299			script.remove();
300		}
301	});
302
303	// Remove any Facebook pixel iframes
304	const iframes = document.querySelectorAll('iframe[src*="facebook"]');
305	iframes.forEach(iframe => iframe.remove());
306
307	// Reset Facebook pixel variables
308	if (window.fbq) {
309		window.fbq = undefined;
310	}
311	if (window._fbq) {
312		window._fbq = undefined;
313	}
314
315	// Remove Facebook pixel noscript images
316	const noscripts = document.querySelectorAll('noscript');
317	noscripts.forEach(noscript => {
318		if (noscript.innerHTML.indexOf('facebook.com') !== -1) {
319			noscript.remove();
320		}
321	});
322
323	console.log('Facebook Pixel removed due to consent withdrawal');
324}

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.