SignUp Event – Sept 24 Notes

You’ve set up the getCookie and setCookie functions globally, which is great for consistency. Just ensure that window.domain (or singleWhiteLabelCookieDomain) is correctly populated to handle cross-subdomain cookies.

function setCookie(name, value, days) {  var expires = "";  if (days) {    var date = new Date();    date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));    expires = "; expires=" + date.toUTCString();  }  document.cookie = name + "=" + (value || "") + expires + "; path=/; domain=" + window.domain + ";max-age=31536000;secure;SameSite=Lax;";}

2. User ID Handling:

You’ve correctly defined the function to generate or retrieve the user_id and set it in the cookie. You’re also attaching it to the window object so it can be accessed globally. This is important when sending events to GTM or GA4.

function getOrCreateUserId() {  var name = "user_id=";  var decodedCookie = decodeURIComponent(document.cookie);  var ca = decodedCookie.split(';');  for (var i = 0; i < ca.length; i++) {    var c = ca[i];    while (c.charAt(0) == ' ') {      c = c.substring(1);    }    if (c.indexOf(name) == 0) {      return c.substring(name.length, c.length);    }  }  // If user_id cookie does not exist, create a new one  var userId = 'user_' + Math.random().toString(36).substr(2, 9);  setCookie('user_id', userId, 365);  return userId;}window.userId = getOrCreateUserId();console.log('user_id: ', window.userId);

3. NATS Decoding and Event Handling:

You’ve set up the decoding of NATS data into structured values (precomputedNatsValues). These values are then stored globally for later use and sent during the form submission process.

  • Form Submission Interception: You correctly intercept form submission, hash the email asynchronously, and push events to GTM/GA4. This is important to ensure the proper handling of user data and tracking.
document.getElementById('natsPreJoinForm').addEventListener('submit', async function(event) {  console.log('Form submission intercepted');  event.preventDefault(); // Prevent the form from submitting immediately  if (window.isSubmitting) {    console.log('Form is already submitting');    return;  }  window.isSubmitting = true;  try {    const email = document.getElementById('usernameemail').value;    const hashedEmail = await hashEmailAndStore(email);    console.log('Hashed email: ', hashedEmail);    document.getElementById('hashedEmailCustom9Input').value = hashedEmail;    document.getElementById('userIdCustom10Input').value = window.userId;    const selectedOption = document.querySelector('.option.pressed-button');    if (!selectedOption) throw new Error('No option selected');    const optionId = selectedOption.id.split('-')[1];    const priceInit = selectedOption.querySelector('.price-init').textContent.trim();    const priceCurrency = selectedOption.querySelector('.price-currency').textContent.trim() || 'USD';    let signUpValue;    if (priceInit.toUpperCase() === "FREE") {      signUpValue = 0;    } else {      const priceWhole = selectedOption.querySelector('.price-init').textContent.trim();      const priceCents = selectedOption.querySelector('.price-cents').textContent.trim();      signUpValue = parseFloat(`${priceWhole}${priceCents}`).toFixed(2);    }    const referrerValue = `${precomputedNatsValues.nats.nats_SiteName || 'default'} | Affiliate ${precomputedNatsValues.nats.nats_AffiliateID || 'default'} | From ${document.referrer || 'Direct/No Referrer'}`;    setCookie('nats_affiliate_referrer', referrerValue, 7);  // Set cookie with 7-day expiration    sendGA4Event(precomputedNatsValues, { hashedEmail, optionId, priceCurrency, signUpValue });    console.log('About to submit the form');    event.target.submit();  // Submit the form after processing  } catch (error) {    console.error("Error processing form submission:", error);  } finally {    window.isSubmitting = false;    document.getElementById('waiter').style.display = 'block';  }});

4. GA4 Event Sending:

You’ve set up the sendGA4Event function to handle the necessary event tracking for GA4. This function packages up the event details and pushes them to the dataLayer.

function sendGA4Event(precomputedNatsValues, optionDetails) {  const { hashedEmail, optionId, priceCurrency, signUpValue } = optionDetails;  const currentTime = new Date().toISOString();  const eventDetails = {    "event": "sign_up",    "user_id": window.userId,    "hashed_email": hashedEmail,    "time_sign_up": currentTime,    "method": 'email',    "hostname": window.location.hostname,    "items": [      {        "item_name": `${precomputedNatsValues.nats.nats_SiteName || 'default'} | OptionId ${optionId} - ${priceCurrency}${signUpValue}`,        "item_id": `${precomputedNatsValues.nats.nats_SiteName || 'default'} | OptionId ${optionId}`,        "item_brand": `${precomputedNatsValues.nats.nats_SiteName || 'default'} | Tour ${precomputedNatsValues.nats.nats_TourID || 'default'}`,        "affiliation": `${precomputedNatsValues.nats.nats_SiteName || 'default'} | Affiliate ${precomputedNatsValues.nats.nats_AffiliateID || 'default'}`,        "discount": 0,        "item_category": 'Membership',        "price": signUpValue,        "quantity": 1      }    ],    "currency": signUpValue === 0 ? "USD" : priceCurrency,    "value": signUpValue,    "event_category": 'nats_pre_join',    "event_action": optionId,    "event_label": document.referrer || 'Direct/No Referrer',    "event_currency": priceCurrency,    "event_value": signUpValue,    "nats": precomputedNatsValues.nats  };  console.log("Sending GA4 event with details: ", eventDetails);  // Set the time_sign_up cookie  setCookie('time_sign_up', currentTime, 365);  if (typeof window.dataLayer !== 'undefined') {    window.dataLayer.push(eventDetails);    console.log('sign_up event sent to dataLayer:', eventDetails);  } else {    console.error('dataLayer is not defined.');  }}

5. Minor Fixes and Suggestions:

  • Variable Naming Consistency: Ensure that all references to window.domain, window.siteURL, etc., are correctly passed through Smarty or another template engine. Double-check the variables populated by {#singleWhiteLabelCookieDomain#} and other placeholders.
  • Avoid Duplicating Cookie Functions: You have defined getCookie and setCookie multiple times in different parts of the code. It’s more efficient to define these functions once globally and reuse them throughout the script.
  • GTM Script Load: You’ve added GTM dynamically, which is good. Just ensure that the external script is loading as expected without blocking the rest of the page.

Summary:

Your overall setup for handling cookies, user identification, form submission, and GA4 event sending is solid. You’ve correctly implemented asynchronous form handling and pushed event data to the dataLayer. The suggestions are mainly around improving code efficiency and ensuring consistency in variable names.

Is there any specific area you would like me to focus on further or clarify in this setup?


Refdactored

document.addEventListener('DOMContentLoaded', function() {  console.log('Script loaded: start decoding NATS');  function decodeNatsSMP() {    console.log('decodeNatsSMP called');    var data = document.getElementById('nats').value;  // Ensure 'nats' input exists in your HTML    var chars = data.length;    var add = 4 - (chars % 4);    if (add < 4) {      while (add) {        data += '=';        add -= 1;      }    }    var decodedData = atob(data);    var splitData = decodedData.split('.');    var labels = ['AffiliateID', 'ProgramID', 'SiteID', 'TourID', 'CampaignID', 'AdtoolID', 'AdToolSub1ID', 'AdToolSub2ID'];    var decodedValues = { nats: {} };    splitData.forEach((value, index) => {  if (index < labels.length) {    decodedValues.nats[`nats_${labels[index]}`] = value;    if (labels[index] === 'SiteID') {      decodedValues.nats['nats_SiteName'] = siteNames[value] || 'Unknown Site';    }  }});return decodedValues;  }  // Declare current time globally  window.currentTime = new Date().toISOString();  console.log('Fetching NATS element');  let natsElement = document.getElementById('nats');  if (natsElement && natsElement.value) {    let precomputedNatsValues = decodeNatsSMP();  // Compute NATS values    if (precomputedNatsValues) {      console.log('Precomputed NATS values:', JSON.stringify(precomputedNatsValues));      window.precomputedNatsValues = precomputedNatsValues; // Store globally for later use    } else {      console.error("Failed to decode NATS values.");    }  } else {    console.error("NATS element or value not found.");  }  let precomputedNatsValues = window.precomputedNatsValues || {};  console.log('Preparing to hash email and store');  async function hashEmailAndStore(email) {    console.log('hashEmailAndStore called');    const msgUint8 = new TextEncoder().encode(email.toLowerCase());    const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8);    const hashArray = Array.from(new Uint8Array(hashBuffer));    const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');    localStorage.setItem('hashed_email', hashHex);    return hashHex;  }  document.getElementById('natsPreJoinForm').addEventListener('submit', async function(event) {    console.log('Form submission intercepted');    event.preventDefault(); // Prevent the form from submitting immediately
    if (window.isSubmitting) {  console.log('Form is already submitting');  return;}window.isSubmitting = true;try {  const email = document.getElementById('usernameemail').value;  const hashedEmail = await hashEmailAndStore(email);  console.log('Hashed email: ', hashedEmail);  document.getElementById('hashedEmailCustom9Input').value = hashedEmail;  document.getElementById('userIdCustom10Input').value = window.userId;  const selectedOption = document.querySelector('.option.pressed-button');  if (!selectedOption) throw new Error('No option selected');  const optionId = selectedOption.id.split('-')[1];  const priceInit = selectedOption.querySelector('.price-init').textContent.trim();  const priceCurrency = selectedOption.querySelector('.price-currency').textContent.trim() || 'USD';  let signUpValue;  if (priceInit.toUpperCase() === "FREE") {    signUpValue = 0;  } else {    const priceWhole = selectedOption.querySelector('.price-init').textContent.trim();    const priceCents = selectedOption.querySelector('.price-cents').textContent.trim();    signUpValue = parseFloat(`${priceWhole}${priceCents}`).toFixed(2);  }  if (!precomputedNatsValues.nats) {    throw new Error("NATS values are not properly defined.");  }  const referrerValue = `${precomputedNatsValues.nats.nats_SiteName || 'default'} | Affiliate ${precomputedNatsValues.nats.nats_AffiliateID || 'default'} | From ${document.referrer || 'Direct/No Referrer'}`;  setCookie('nats_affiliate_referrer', referrerValue, 7);  // Set cookie with 7-day expiration  console.log("nats_affiliate_referrer cookie set:", referrerValue);  sendGA4Event(precomputedNatsValues, { hashedEmail, optionId, priceCurrency, signUpValue });  console.log('About to submit the form');  event.target.submit();} catch (error) {  console.error("Error processing form submission:", error);} finally {  window.isSubmitting = false;  document.getElementById('waiter').style.display = 'block';}  });  // Add the GTM script asynchronously  var gtmScript = document.createElement('script');  gtmScript.async = true;  gtmScript.src = `https://www.googletagmanager.com/gtag/js?id=${envGAMeasurementID}`;  document.head.appendChild(gtmScript);  // Define the gtag function  window.dataLayer = window.dataLayer || [];  function gtag() { dataLayer.push(arguments); }  gtag('js', new Date());  // Enable debug mode for GA4  gtag('config', envGAMeasurementID, { 'debug_mode': true });  function sendGA4Event(precomputedNatsValues, optionDetails) {    console.log('sendGA4Event called');    const { hashedEmail, optionId, priceCurrency, signUpValue } = optionDetails;
const eventDetails = {  "event": "sign_up",  "user_id": window.userId,  "hashed_email": hashedEmail,  "time_sign_up": window.currentTime,  // Use globally declared time  "method": 'email',  "hostname": window.location.hostname,  "items": [    {      "item_name": `${precomputedNatsValues.nats.nats_SiteName || 'default'} | OptionId ${optionId} - ${priceCurrency}${signUpValue}`,      "item_id": `${precomputedNatsValues.nats.nats_SiteName || 'default'} | OptionId ${optionId}`,      "item_brand": `${precomputedNatsValues.nats.nats_SiteName || 'default'} | Tour ${precomputedNatsValues.nats.nats_TourID || 'default'}`,      "affiliation": `${precomputedNatsValues.nats.nats_SiteName || 'default'} | Affiliate ${precomputedNatsValues.nats.nats_AffiliateID || 'default'}`,      "discount": 0,      "item_category": 'Membership',      "price": signUpValue,      "quantity": 1    }  ],  "currency": signUpValue === 0 ? "USD" : priceCurrency,  "value": signUpValue,  "event_category": 'nats_pre_join',  "event_action": optionId,  "event_label": document.referrer || 'Direct/No Referrer',  "event_currency": signUpValue === 0 ? "USD" : priceCurrency,  "event_value": signUpValue,  "nats": precomputedNatsValues.nats};  console.log("Sending GA4 event with details: ", eventDetails);    // Set the 'time_sign_up' cookie globally    setCookie('time_sign_up', window.currentTime, 365);    // Send the event data to GTM    if (typeof window.dataLayer !== 'undefined') {      window.dataLayer.push(eventDetails);      console.log('sign_up event sent to dataLayer with details:', eventDetails);    } else {      console.error('dataLayer is not defined.');    }  }});