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.