PageSourceSearch

https://www.legalseafoods.com/wp-content/themes/legal-sea-foods/ap…/js/capi-tracking.js?debug=1790344619

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

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.