PageSourceSearch

https://likethewindmagazine.com/wp-content/plugins/le-pixel-woo/assets/js/lepixel.js?ver=1.1.0

js likethewindmagazine.com collected 2026-10-03 19:14:15 UTC 13,128 bytes, 352 lines download raw bytes

1(function(){
2  if (typeof window.LEPIXEL_WOO === 'undefined') { return; }
3  var cfg = window.LEPIXEL_WOO;
4
5  var HOST = cfg.TRACKING_SERVER;
6  function nowTs(){ return Date.now(); }
7  function newEventId(){ return (''+Date.now()) + '' + Math.floor(Math.random() * 1e16); }
8
9  function headers(){
10    var h = { 'Content-Type': 'application/json' };
11    try {
12      if (cfg && cfg.nonce) { h['X-WP-Nonce'] = cfg.nonce; }
13    } catch(e){}
14    return h;
15  }
16
17  async function getVisitorId(){
18    try {
19      var id = localStorage.getItem('lePixelVisitorId');
20      if(!id){
21        id = 'v-' + Date.now() + '-' + Math.floor(Math.random()*1000000);
22        localStorage.setItem('lePixelVisitorId', id);
23      }
24      return id;
25    } catch(e){}
26  }
27
28  async function getSessionId(){
29    try {
30      var id = sessionStorage.getItem('lePixelSessionId');
31      if(!id){
32        id = 's-' + Date.now() + '-' + Math.floor(Math.random()*1000000);
33        sessionStorage.setItem('lePixelSessionId', id);
34      }
35      return id;
36    } catch(e){}
37  }
38
39  function sendTrackingData(payload, retries){
40    retries = (typeof retries === 'number') ? retries : 3;
41    var body = JSON.stringify(payload);
42    try{
43      fetch(HOST, {
44        method: 'POST',
45        headers: { 'Content-Type': 'application/json' },
46        body: body
47      }).catch(function(){
48        try{
49          if (navigator && typeof navigator.sendBeacon === 'function') {
50            var blob = new Blob([body], { type: 'application/json' });
51            navigator.sendBeacon(HOST, blob);
52          }
53        }catch(e){}
54        if(retries > 0){ sendTrackingData(payload, retries-1); }
55      });
56    }catch(e){}
57  }
58
59  function baseEvent(evtName, extra){
60    return Promise.all([getVisitorId(), getSessionId()]).then(function(ids){
61      var visitorId = ids[0], sessionId = ids[1];
62      var base = {
63        event: evtName,
64        eventId: newEventId(),
65        visitorId: visitorId,
66        url: window.location.href,
67        referrer: document.referrer,
68        timestamp: nowTs(),
69        sessionId: sessionId,
70        shop: cfg.shop,
71        customerId: cfg.user ? cfg.user.id : undefined,
72        customerEmail: cfg.user ? cfg.user.email : undefined,
73        firstName: cfg.user ? cfg.user.firstName : undefined,
74        lastName: cfg.user ? cfg.user.lastName : undefined
75      };
76      if (extra) { for (var k in extra) { base[k] = extra[k]; } }
77      return base;
78    });
79  }
80
81  function sendPageView(){ baseEvent('page_view').then(sendTrackingData); }
82
83  // jQuery helpers
84  function onJq(eventName, handler){
85    if (typeof jQuery === 'undefined') { return; }
86    jQuery(function($){ $(document.body).on(eventName, handler); });
87  }
88  function onDoc(eventName, selector, handler){
89    if (typeof jQuery === 'undefined') { return; }
90    jQuery(function($){ $(document).on(eventName, selector, handler); });
91  }
92
93  // REST helpers
94  function restGet(path){
95    var url = cfg.restRoot.replace(/\/$/, '') + path;
96    return fetch(url, { headers: headers() }).then(function(r){ return r.json(); });
97  }
98  function enrichProduct(productId, variationId, quantity){
99    var qs = '?id=' + encodeURIComponent(productId) +
100             (variationId ? ('&variation_id=' + encodeURIComponent(variationId)) : '') +
101             '&quantity=' + encodeURIComponent(quantity || 1);
102    return restGet('/product' + qs);
103  }
104  function fetchCart(){
105    return restGet('/cart');
106  }
107
108  // ---- CART DIFFING ----
109  var cartSnapshot = {}; // key -> { quantity, item }
110  var suppressDiffUntil = 0;
111  var snapshotReady = false;
112
113  function updateSnapshot(items){
114    cartSnapshot = {};
115    (items || []).forEach(function(it){
116      cartSnapshot[it.key] = { quantity: it.quantity, item: it };
117    });
118    snapshotReady = true;
119  }
120
121  function buildCartPayload(item, deltaQty){
122    var unit = (item && item.variant && typeof item.variant.price === 'number') ? item.variant.price : undefined;
123    return {
124      quantity: deltaQty,
125      totalPrice: (unit ? { amount: unit * deltaQty, currency: item.variant.currency || cfg.currency } : undefined),
126      variant: item.variant
127    };
128  }
129
130  function diffEmit(prevMap, nextItems){
131    var nextMap = {};
132    nextItems.forEach(function(it){ nextMap[it.key] = { quantity: it.quantity, item: it }; });
133
134    // Detect increases/decreases
135    var keys = {};
136    for (var k in prevMap) { keys[k] = true; }
137    for (var k in nextMap) { keys[k] = true; }
138
139    var emits = [];
140    for (var k in keys) {
141      var prev = prevMap[k] ? prevMap[k].quantity : 0;
142      var next = nextMap[k] ? nextMap[k].quantity : 0;
143      var item = nextMap[k] ? nextMap[k].item : (prevMap[k] ? prevMap[k].item : null);
144      if (!item) { continue; }
145      if (next > prev) {
146        emits.push({ type: 'add_to_cart', payload: buildCartPayload(item, next - prev) });
147      } else if (next < prev) {
148        emits.push({ type: 'remove_from_cart', payload: buildCartPayload(item, prev - next) });
149      }
150    }
151
152    emits.sort(function(a,b){ return a.type === 'remove_from_cart' ? -1 : 1; });
153    var proms = emits.map(function(evt){
154      return baseEvent(evt.type, evt.type === 'add_to_cart' ? { addedItem: evt.payload } : { removedItem: evt.payload })
155        .then(function(data){ sendTrackingData(data, 0); });
156    });
157
158    updateSnapshot(nextItems);
159    return Promise.all(proms);
160  }
161
162  function refreshAndDiff(reason){
163    if (Date.now() < suppressDiffUntil) { return; }
164    if (!snapshotReady) { return; } // avoid false positives on first load
165    fetchCart().then(function(res){
166      var items = (res && res.items) ? res.items : [];
167      diffEmit(cartSnapshot, items);
168    }).catch(function(){ /* ignore */ });
169  }
170  function suppressFor(ms){ suppressDiffUntil = Date.now() + (ms||1200); }
171
172  // ------------- EVENT WIRING -------------
173
174  // 1) Page view
175  try { sendPageView(); } catch(e){}
176
177  // Initialize initial snapshot, then allow diffing
178  setTimeout(function(){ fetchCart().then(function(res){ updateSnapshot((res && res.items) || []); }); }, 150);
179
180  // 2) Add to cart (AJAX standard)
181  onJq('added_to_cart', function(ev, fragments, cart_hash, $button){
182    try{
183      var $btn = (typeof jQuery !== 'undefined' && $button && $button.length) ? $button : null;
184      var productId = $btn ? ($btn.data('product_id') || $btn.attr('data-product_id') || $btn.data('product') || $btn.attr('data-product')) : null;
185      var variationId = $btn ? ($btn.data('variation_id') || $btn.attr('data-variation_id')) : null;
186      var qty = 1;
187      try{
188        qty = ($btn && ($btn.data('quantity') || $btn.attr('data-quantity'))) || 1;
189        var $form = $btn && $btn.closest('form');
190        if ($form && !variationId) {
191          var vid = $form.find('input[name=variation_id]').val();
192          if (vid) variationId = parseInt(vid, 10);
193        }
194        if ($form) {
195          var qv = $form.find('input[name=quantity]').val();
196          if (qv) qty = parseInt(qv, 10);
197        }
198      }catch(e){}
199
200      if (!productId) { return; }
201
202      enrichProduct(productId, variationId, qty).then(function(data){
203        baseEvent('add_to_cart', { addedItem: data.addedItem }).then(function(evt){
204          sendTrackingData(evt, 0);
205          suppressFor(1200);
206        });
207      }).catch(function(){
208        baseEvent('add_to_cart', {
209          addedItem: {
210            quantity: qty,
211            variant: {
212              id: variationId || productId,
213              product: { id: productId }
214            }
215          }
216        }).then(function(evt){ sendTrackingData(evt, 0); suppressFor(1200); });
217      });
218    }catch(e){}
219  });
220
221  // 3) Remove from cart (AJAX standard)
222  onJq('removed_from_cart', function(){ setTimeout(function(){ refreshAndDiff('removed_from_cart'); }, 200); suppressFor(1200); });
223
224  // 4) Non-AJAX fallbacks via server-snapshotted items (already cleared server-side)
225  try{
226    if (cfg.lastAddedItem) { baseEvent('add_to_cart', { addedItem: cfg.lastAddedItem }).then(sendTrackingData); }
227    if (cfg.lastRemovedItem) { baseEvent('remove_from_cart', { removedItem: cfg.lastRemovedItem }).then(sendTrackingData); }
228  }catch(e){}
229
230  // 5) Initiate checkout (on checkout page load snapshot)
231  try{
232    if (cfg.context && cfg.context.isCheckout && cfg.checkout) {
233      baseEvent('initiate_checkout', {
234        totalPrice: cfg.checkout.totalPrice,
235        lineItems: cfg.checkout.lineItems,
236        discountsAmount: cfg.checkout.discountsAmount || undefined,
237        totalTax: cfg.checkout.totalTax,
238        shippingPrice: cfg.checkout.shippingPrice
239      }).then(function(evt){ sendTrackingData(evt, 0); });
240    }
241  }catch(e){}
242
243// 6) Purchase (thank-you page)
244try {
245  if (cfg.purchase) {
246    var orderKey = cfg.purchase.orderId;
247    var dedupKey = 'lePixelPurchase' + orderKey;
248    // Skip if already fired this session
249    if (!sessionStorage.getItem(dedupKey)) {
250      sessionStorage.setItem(dedupKey, '1');
251      baseEvent('purchase').then(function(base){
252        var p = cfg.purchase;
253        p.eventId = base.eventId;
254        p.visitorId = base.visitorId;
255        p.sessionId = base.sessionId;
256        p.url = base.url;
257        p.referrer = base.referrer;
258        p.timestamp = base.timestamp;
259        p.shop = p.shop || base.shop;
260
261        sendTrackingData(p, 0);
262      });
263    }
264  }
265}catch (e) {}
266
267
268  // 7) Broad coverage: listen to cart & fragments refreshes and diff.
269  onJq('updated_wc_div', function(){ refreshAndDiff('updated_wc_div'); });
270  onJq('cart_page_refreshed', function(){ refreshAndDiff('cart_page_refreshed'); });
271  onJq('wc_cart_emptied', function(){ refreshAndDiff('wc_cart_emptied'); });
272  onJq('wc_fragments_refreshed', function(){ refreshAndDiff('wc_fragments_refreshed'); });
273
274  // Extra: clicks that are known to remove items in various templates
275  onDoc('click', 'a.remove, a.remove_from_cart_button, .wc-block-cart-item__remove-link', function(){ setTimeout(function(){ refreshAndDiff('click_remove'); }, 200); });
276
277  // Fallback: observe jQuery AJAX calls to Woo endpoints in case a theme/plugin skips standard events
278  if (typeof jQuery !== 'undefined') {
279    jQuery(document).ajaxSuccess(function(event, xhr, settings){
280      try{
281        if (!settings || !settings.url) return;
282        var url = settings.url;
283        var dataStr = settings.data || '';
284        if (url.indexOf('wc-ajax=add_to_cart') !== -1) {
285          var p = new URLSearchParams(dataStr || '');
286          var productId = p.get('product_id') || p.get('add-to-cart');
287          var variationId = p.get('variation_id');
288          var qty = parseInt(p.get('quantity') || '1', 10);
289          if (productId) {
290            enrichProduct(productId, variationId, qty).then(function(data){
291              baseEvent('add_to_cart', { addedItem: data.addedItem }).then(function(evt){
292                sendTrackingData(evt, 0);
293                suppressFor(1200);
294              });
295            });
296          }
297        } else if (url.indexOf('wc-ajax=remove_from_cart') !== -1) {
298          setTimeout(function(){ refreshAndDiff('ajax_remove'); }, 200);
299          suppressFor(1200);
300        } else if (url.indexOf('wc-ajax=update_cart') !== -1 || url.indexOf('wc-ajax=apply_coupon') !== -1) {
301          setTimeout(function(){ refreshAndDiff('ajax_update'); }, 300);
302        }
303      }catch(e){}
304    });
305  }
306
307  // Intercept fetch used by Blocks / modern themes to detect cart changes (Store API).
308  (function(){
309    if (!window.fetch) { return; }
310    var origFetch = window.fetch;
311    window.fetch = function(input, init){
312      var url = (typeof input === 'string') ? input : (input && input.url) || '';
313      var method = (init && init.method) ? String(init.method).toUpperCase() : 'GET';
314      var p = origFetch.apply(this, arguments);
315      try {
316        if (/wc-ajax=(add_to_cart|remove_from_cart|update_cart)|\/wc\/store\/|\/wp-json\/wc\/store\//.test(url)) {
317          p.then(function(){ setTimeout(function(){ refreshAndDiff('fetch:'+method); }, 220); });
318        }
319      } catch(e){}
320      return p;
321    };
322  })();
323
324  // DOM observer for mini-cart/cart mutations (covers pure DOM-based updates)
325  (function(){
326    if (typeof MutationObserver === 'undefined') { return; }
327    var sel = ['.widget_shopping_cart', '.woocommerce-mini-cart', '.wc-block-mini-cart', '.wc-block-cart-items'].join(',');
328    function observe(el){
329      try {
330        var mo = new MutationObserver(function(){ setTimeout(function(){ refreshAndDiff('mo'); }, 300); });
331        mo.observe(el, { childList: true, subtree: true, characterData: true });
332      } catch(e){}
333    }
334    function scan(){
335      var nodes = document.querySelectorAll(sel);
336      for (var i=0; i<nodes.length; i++){ observe(nodes[i]); }
337    }
338    if (document.readyState === 'loading') {
339      document.addEventListener('DOMContentLoaded', scan);
340    } else { scan(); }
341  })();
342
343  // Optional: for themes that auto-update cart on quantity input change without events
344  if (typeof jQuery !== 'undefined') {
345    jQuery(function($){
346      $(document).on('change', '.woocommerce-cart-form input.qty', function(){
347        setTimeout(function(){ refreshAndDiff('qty_change'); }, 500);
348      });
349    });
350  }
351
352})();

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.