PageSourceSearch

https://torqit.com.au/wp-content/plugins/facebook-for-woocommerce/assets/js/frontend/pixel-events.js?ver=3.7.6

js torqit.com.au collected 2026-09-24 13:15:19 UTC 10,078 bytes, 312 lines download raw bytes

1/**
2 * Facebook Pixel Events - External JavaScript Handler
3 *
4 * This script fires pixel events in an isolated execution context,
5 * ensuring events are sent even if other plugins cause JavaScript errors.
6 *
7 * Supports WooCommerce Blocks via Store API fetch interception.
8 * The pixel event data is read from the Store API response extensions.
9 *
10 * @package FacebookCommerce
11 */
12
13(function() {
14    'use strict';
15
16    // Data from PHP (may be missing if no events were queued for initial render).
17    // Keep Store API interception active even when eventQueue is empty/undefined.
18    var data = (typeof wc_facebook_pixel_data !== 'undefined' && wc_facebook_pixel_data) ?
19        wc_facebook_pixel_data :
20        { eventQueue: [] };
21    window.wcFacebookPixelFiredEvents = window.wcFacebookPixelFiredEvents || {};
22    var firedEvents = window.wcFacebookPixelFiredEvents;
23
24    /**
25     * Build event data object for fbq()
26     *
27     * @param {Object} event Event object from PHP
28     * @return {Object} Prepared event data
29     */
30    function isCachedPage() {
31        return window.__wc_fb_page_generated &&
32            (Math.floor(Date.now() / 1000) - window.__wc_fb_page_generated) > 1800;
33    }
34
35    function buildEventData(event) {
36        return {
37            method: event.method || 'track',
38            name: event.name,
39            params: event.params || {},
40            eventId: isCachedPage() ? null : (event.eventId || null)
41        };
42    }
43
44    /**
45     * Check if event should be skipped (already fired)
46     *
47     * @param {string|null} eventId Event ID for deduplication
48     * @return {boolean} True if should skip
49     */
50    function shouldSkipEvent(eventId) {
51        return eventId && firedEvents[eventId];
52    }
53
54    /**
55     * Mark event as fired for deduplication
56     *
57     * @param {string|null} eventId Event ID
58     */
59    function markEventFired(eventId) {
60        if (eventId) {
61            firedEvents[eventId] = true;
62        }
63    }
64
65    /**
66     * Log warning to console (with safety check)
67     *
68     * @param {string} message Warning message
69     * @param {*} data Additional data to log
70     */
71    function logWarning(message, data) {
72        if (typeof console !== 'undefined' && console.warn) {
73            console.warn('[FB Pixel]', message, data);
74        }
75    }
76
77    /**
78     * Fire a single event using fbq()
79     *
80     * @param {Object} event Event object with name, params, method, eventId
81     */
82    function fireEvent(event) {
83        var eventData = buildEventData(event);
84
85        // Skip if already fired (deduplication)
86        if (shouldSkipEvent(eventData.eventId)) {
87            return;
88        }
89
90        try {
91            var params = eventData.params;
92
93            if (window.FacebookSignals && typeof window.FacebookSignals.trackEvent === 'function') {
94                window.FacebookSignals.trackEvent(eventData.name, params, null, eventData.method, eventData.eventId);
95            } else {
96                if (typeof fbq !== 'function') {
97                    logWarning('fbq not available, skipping event:', eventData.name);
98                    return;
99                }
100
101                if (eventData.eventId) {
102                    fbq(eventData.method, eventData.name, params, {eventID: eventData.eventId});
103                } else {
104                    fbq(eventData.method, eventData.name, params);
105                }
106            }
107
108            markEventFired(eventData.eventId);
109
110        } catch (e) {
111            logWarning('Event error: ' + eventData.name, e);
112        }
113    }
114
115    /**
116     * Fire all queued events from PHP
117     */
118    function fireQueuedEvents() {
119        var events = data.eventQueue;
120
121        if (!events || !Array.isArray(events)) {
122            return;
123        }
124
125        for (var i = 0; i < events.length; i++) {
126            try {
127                fireEvent(events[i]);
128            } catch (e) {
129                logWarning('fireQueuedEvents loop error:', e);
130            }
131        }
132
133        // Clear events after firing to prevent duplicate firing
134        data.eventQueue = [];
135    }
136
137    // =========================================================================
138    // WooCommerce Blocks: Store API approach
139    // =========================================================================
140
141    /**
142     * Process pixel event data from Store API response.
143     *
144     * @param {Object} eventData Event data from Store API extensions
145     */
146    function processStoreApiEvent(eventData) {
147        if (!eventData || !eventData.event) {
148            return;
149        }
150
151        var params = eventData.params || {};
152
153        var event = {
154            method: 'track',
155            name: eventData.event,
156            params: params,
157            eventId: params.event_id || null
158        };
159
160        fireEvent(event);
161    }
162
163    /**
164     * Returns the request URL from a fetch() input.
165     *
166     * @param {*} input fetch() input argument
167     * @return {string}
168     */
169    function getRequestUrl(input) {
170        if (typeof input === 'string') {
171            return input;
172        }
173
174        if (input && typeof input.url === 'string') {
175            return input.url;
176        }
177
178        return '';
179    }
180
181    /**
182     * Checks whether a URL targets the Store API add-item endpoint.
183     *
184     * @param {string} url Request URL
185     * @return {boolean}
186     */
187    function isStoreApiAddItemRequest(url) {
188        return typeof url === 'string' &&
189            (url.indexOf('/wc/store/v1/cart/add-item') !== -1 ||
190             url.indexOf('/wc/store/cart/add-item') !== -1);
191    }
192
193    /**
194     * Checks whether a URL targets the Store API batch endpoint.
195     *
196     * @param {string} url Request URL
197     * @return {boolean}
198     */
199    function isStoreApiBatchRequest(url) {
200        return typeof url === 'string' &&
201            (url.indexOf('/wc/store/v1/batch') !== -1 ||
202             url.indexOf('/wc/store/batch') !== -1);
203    }
204
205    /**
206     * Process Store API response payload and extract plugin extension data.
207     * Handles both direct cart responses and batch responses.
208     *
209     * @param {Object} responseData Parsed Store API response payload
210     */
211    function processStoreApiResponseData(responseData) {
212        if (!responseData || typeof responseData !== 'object') {
213            return;
214        }
215
216        // Direct response shape: { extensions: { 'facebook-for-woocommerce': ... } }
217        if (responseData.extensions && responseData.extensions['facebook-for-woocommerce']) {
218            processStoreApiEvent(responseData.extensions['facebook-for-woocommerce']);
219        } else if (Array.isArray(responseData.responses)) {
220            // Batch response shape: { responses: [ { body: { extensions: ... } } ] }
221            for (var i = 0; i < responseData.responses.length; i++) {
222                var item = responseData.responses[i];
223                if (item && item.body && item.body.extensions && item.body.extensions['facebook-for-woocommerce'] &&
224                    item.status >= 200 && item.status < 300) {
225                    processStoreApiEvent(item.body.extensions['facebook-for-woocommerce']);
226                }
227            }
228        }
229    }
230
231    /**
232     * Set up fetch interceptor to capture Store API responses.
233     * Intercepts cart/add-item and batch requests to fire AddToCart pixel events.
234     */
235    function setupFetchInterceptor() {
236        var originalFetch = window.fetch;
237        if (!originalFetch) {
238            return;
239        }
240
241        window.fetch = function() {
242            var args = arguments;
243            var url = getRequestUrl(args[0]);
244
245            var isAddToCartRequest = isStoreApiAddItemRequest(url);
246            var isBatchRequest = isStoreApiBatchRequest(url);
247
248            return originalFetch.apply(this, args).then(function(response) {
249                if ((isAddToCartRequest || isBatchRequest) && response.ok) {
250                    // Clone response so we can read it without consuming
251                    response.clone().json().then(function(responseData) {
252                        processStoreApiResponseData(responseData);
253                    }).catch(function(e) {
254                        logWarning('Store API JSON parse error:', e);
255                    });
256                }
257                return response;
258            });
259        };
260    }
261
262    /**
263     * Initialize pixel event handling.
264     *
265     * If fbq() is already available, fires queued events immediately.
266     * If not (e.g. consent manager blocking the SDK), uses Object.defineProperty
267     * to set a trap on window.fbq — our handler fires automatically the moment
268     * fbq is assigned, with zero overhead in between. No polling, no timers.
269     *
270     * Also sets up Store API interceptor for WooCommerce Blocks AJAX AddToCart.
271     */
272    function init() {
273        // Set up fetch interceptor for WooCommerce Blocks Store API
274        setupFetchInterceptor();
275
276        if (typeof fbq === 'function') {
277            fireQueuedEvents();
278            return;
279        }
280
281        // fbq doesn't exist yet — watch for it (zero overhead, no timers).
282        // Consent managers block fbq until the
283        // user accepts. This fires the moment they assign window.fbq.
284        var _fbq = window.fbq;
285        Object.defineProperty(window, 'fbq', {
286            configurable: true,
287            enumerable: true,
288            get: function() { return _fbq; },
289            set: function(value) {
290                _fbq = value;
291                if (typeof value === 'function') {
292                    // Restore normal property so FB SDK works normally
293                    Object.defineProperty(window, 'fbq', {
294                        configurable: true,
295                        enumerable: true,
296                        writable: true,
297                        value: value
298                    });
299                    setTimeout(fireQueuedEvents, 0);
300                }
301            }
302        });
303    }
304
305    // Start
306    if (document.readyState === 'complete') {
307        init();
308    } else {
309        window.addEventListener('load', init);
310    }
311
312})();

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.