PageSourceSearch

https://foodsocial.io/wp-content/themes/paleo/js/ga_script.js?ver=20260829.1753816875

js foodsocial.io collected 2026-09-25 21:07:57 UTC 6,527 bytes, 187 lines download raw bytes

1/**
2 * Event Enum for Standard GA4 Events
3 * Reference: https://support.google.com/analytics/answer/9267735#online-sales
4 */
5const GA4Event = Object.freeze({
6  VIEW_ITEM: "view_item",
7  VIEW_ITEM_LIST: "view_item_list",
8  ADD_TO_CART: "add_to_cart",
9  SELECT_CONTENT: "select_content",
10  SELECT_ITEM: "select_item",
11  // Add more events as needed
12});
13
14/**
15 * Custom Event Types for Foodsocial
16 */
17const FoodsocialEventType = Object.freeze({
18  BANNER_AD_CLICK: "banner_ad_click",
19  BANNER_AD_VIEW: "banner_ad_view",
20  RECIPE_INGREDIENT_CLICK: "recipe_ingredient_click",
21  SHOPPING_CART_ICON_CLICK: "shopping_cart_icon_click",
22  CAROUSEL_INGREDIENT_CLICK: "carousel_ingredient_ad_click",
23  CAROUSEL_INGREDIENT_VIEW: "carousel_ingredient_ad_view",
24  INSTACART_SHOPPING_LIST_CLICK: "instacart_shopping_list_click",
25  // TODO: BANNER VIEW ? in some other php file. 
26});
27
28/**
29 * Class representing a Foodsocial Event
30 */
31class FoodsocialEvent {
32  /**
33   * @param {string} eventType - The type of event (FoodsocialEventType).
34   * @param {boolean} isShopify - Whether the event is related to Shopify.
35   * @param {Array} relatedShopifyIds - A list of the shopify ids related to this event.
36   * @param {Object} item - The main object to pass to GA4's `gtag()` function.
37   */
38  constructor(eventType, isShopify, relatedShopifyIds = [], item) {
39    if (typeof eventType !== 'string' || !Object.values(FoodsocialEventType).includes(eventType)) {
40      throw new Error(`Invalid eventType: ${eventType}`);
41    }
42
43    this.eventType = eventType;
44    this.isShopify = Boolean(isShopify);
45    this.relatedShopifyIds = Array.isArray(relatedShopifyIds) ? relatedShopifyIds.filter(id => id) : [];
46    this.item = item || {};
47  }
48
49/**
50 * Sends the event data to GA4 (or logs the input to the console for testing)
51 */
52async sendToGA4() {
53  if (typeof gtag === "undefined") {
54    console.error("GA4 gtag() function is not defined.");
55    return;
56  }
57
58  /* Comment for testing */
59  // // Mock gtag calls by printing to the console
60  // const mockGtag = (event, eventName, eventParams) => {
61  //   console.log(`gtag("${event}", "${eventName}",`, eventParams, `);`);
62  // };
63
64  let item;
65  switch (this.eventType) {
66    case FoodsocialEventType.BANNER_AD_CLICK:
67    case FoodsocialEventType.BANNER_AD_VIEW:
68      gtag("event", GA4Event.SELECT_CONTENT, {
69        content_type: "banner_ad",
70        content_id: this.item.content_id.split('.')[0],  /* get the name without the '.jpg' at the end of the adname attr */
71      });
72      break;
73
74    case FoodsocialEventType.RECIPE_INGREDIENT_CLICK:
75      gtag("event", GA4Event.SELECT_CONTENT, {
76        content_type: "recipe_ingredient",
77        content_id: this.item.content_id, /* ingredient_id attr should be passed here */
78      });
79      break;
80
81    case FoodsocialEventType.SHOPPING_CART_ICON_CLICK:
82    case FoodsocialEventType.CAROUSEL_INGREDIENT_CLICK:
83      if(!this.isShopify || !this.relatedShopifyIds){ /* only track click on shopify productions on the shopping cart for now */
84        /* maybe consider amazon products in the future */
85        console.warn(`Unhandled event type: ${this.eventType} when isShopify: ${this.isShopify}`);
86        return;
87      }
88      item = await getItem(this.relatedShopifyIds[0]);
89      gtag("event", GA4Event.VIEW_ITEM, item);
90      break;
91
92    case FoodsocialEventType.CAROUSEL_INGREDIENT_VIEW:
93      if(!this.isShopify || !this.relatedShopifyIds){ /* carousels only allow shopify products */
94        console.warn(`Unhandled event type: ${this.eventType} when isShopify: ${this.isShopify}`);
95        return;
96      }
97      item = await getItem(this.relatedShopifyIds[0]);
98      const items = item?.items || [];
99      const slug = this.item.slug;
100      const name = this.item.name;
101      
102      let dict = {
103        items, // Spread doesn't work directly on arrays; just assign it
104        item_list_id: slug,
105        item_list_name: name
106      };
107
108      gtag("event", GA4Event.VIEW_ITEM_LIST, dict);      
109      break;
110    case FoodsocialEventType.INSTACART_SHOPPING_LIST_CLICK:
111      let creatorId = this.item.creator_id
112      let recipeId = this.item.recipe_id;
113      if(!creatorId || !recipeId){
114        console.warn('Unable to track event clicks on instacart link')
115      }
116      gtag('event', FoodsocialEventType.INSTACART_SHOPPING_LIST_CLICK, this.item);
117      break
118    default:
119      console.warn(`Unhandled event type: ${this.eventType}`);
120  }
121}
122}
123
124function getItem(shopifyId) {
125  const encodedId = btoa(`gid://shopify/Product/${shopifyId}`);
126
127  return client.product.fetch(encodedId)
128    .then((product) => {
129      if (!product) {
130        console.error("No product found with the given ID (" + shopifyId + ")");
131        return null;
132      }
133      const variant = product.variants[0];
134      const price = parseFloat(variant.price.amount);
135      const quantity = 1;
136      const productDetails = {
137        currency: variant.price.currencyCode,
138        value: (price * quantity).toFixed(2),
139        items: [
140          {
141            item_id: `shopify_US_${shopifyId}_${variant.sku}`,
142            affiliation: paleo_GetCookie("firstAuthor") || "None",
143            item_name: product.title,
144            item_brand: product.vendor || "Unknown Brand",
145            item_category: product.productType || "Uncategorized",
146            item_variant: variant.title || "Default Variant",
147            price: price.toFixed(2),
148            quantity: quantity,
149          }
150        ]
151      };
152
153      return productDetails;
154    })
155    .catch((error) => {
156      console.error("Error fetching product details ("+shopifyId+")", error);
157      return null;
158    });
159}
160
161function custom_gtag(event, name, val, add_key, add_val, add_key2, add_val2){
162  if(add_key2 !== undefined && add_key2 != ''){
163    gtag('event', event, {[name]: val, [add_key]: add_val, [add_key2]: add_val2});
164  }
165  else if(add_key !== undefined && add_key != ''){
166    gtag('event', event, {[name]: val, [add_key]: add_val});
167  }
168  else{
169    gtag('event', event, {[name]: val});
170  }
171}
172
173window.dataLayer = window.dataLayer || [];
174function gtag(){ dataLayer.push(arguments); }
175gtag('js', new Date());
176
177(function(w,d,s,l,i){
178  w[l] = w[l] || [];
179  w[l].push({'gtm.start': new Date().getTime(),
180             event: 'gtm.js'});
181  var f=d.getElementsByTagName(s)[0],
182      j=d.createElement(s),
183      dl=l!='dataLayer'?'&l='+l:'';
184  j.async=true;
185  j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;
186  f.parentNode.insertBefore(j,f);
187})(window,document, 'script', 'dataLayer', customLinks.googleTagManager);

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.