PageSourceSearch

https://exitmedia.net/wp-content/plugins/facebook-for-woocommerce/assets/js/frontend/pixel-events.js?ver=3.7.0

js exitmedia.net collected 2026-09-24 22:51:52 UTC 7,353 bytes, 241 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    // Early exit if no data from PHP
17    if (typeof wc_facebook_pixel_data === 'undefined') {
18        return;
19    }
20
21    var data = wc_facebook_pixel_data;
22    var firedEvents = {};
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 buildEventData(event) {
31        return {
32            method: event.method || 'track',
33            name: event.name,
34            params: event.params || {},
35            eventId: event.eventId || null
36        };
37    }
38
39    /**
40     * Check if event should be skipped (already fired)
41     *
42     * @param {string|null} eventId Event ID for deduplication
43     * @return {boolean} True if should skip
44     */
45    function shouldSkipEvent(eventId) {
46        return eventId && firedEvents[eventId];
47    }
48
49    /**
50     * Mark event as fired for deduplication
51     *
52     * @param {string|null} eventId Event ID
53     */
54    function markEventFired(eventId) {
55        if (eventId) {
56            firedEvents[eventId] = true;
57        }
58    }
59
60    /**
61     * Log warning to console (with safety check)
62     *
63     * @param {string} message Warning message
64     * @param {*} data Additional data to log
65     */
66    function logWarning(message, data) {
67        if (typeof console !== 'undefined' && console.warn) {
68            console.warn('[FB Pixel]', message, data);
69        }
70    }
71
72    /**
73     * Fire a single event using fbq()
74     *
75     * @param {Object} event Event object with name, params, method, eventId
76     */
77    function fireEvent(event) {
78        var eventData = buildEventData(event);
79
80        // Skip if already fired (deduplication)
81        if (shouldSkipEvent(eventData.eventId)) {
82            return;
83        }
84
85        // Skip if fbq not available
86        if (typeof fbq !== 'function') {
87            logWarning('fbq not available, skipping event:', eventData.name);
88            return;
89        }
90
91        try {
92            var params = eventData.params;
93
94            // Fire the event with eventID as 4th argument for deduplication
95            if (eventData.eventId) {
96                fbq(eventData.method, eventData.name, params, {eventID: eventData.eventId});
97            } else {
98                fbq(eventData.method, eventData.name, params);
99            }
100
101            markEventFired(eventData.eventId);
102
103        } catch (e) {
104            logWarning('Event error: ' + eventData.name, e);
105        }
106    }
107
108    /**
109     * Fire all queued events from PHP
110     */
111    function fireQueuedEvents() {
112        var events = data.eventQueue;
113
114        if (!events || !Array.isArray(events)) {
115            return;
116        }
117
118        for (var i = 0; i < events.length; i++) {
119            try {
120                fireEvent(events[i]);
121            } catch (e) {
122                logWarning('fireQueuedEvents loop error:', e);
123            }
124        }
125
126        // Clear events after firing to prevent duplicate firing
127        data.eventQueue = [];
128    }
129
130    // =========================================================================
131    // WooCommerce Blocks: Store API approach
132    // =========================================================================
133
134    /**
135     * Process pixel event data from Store API response.
136     *
137     * @param {Object} eventData Event data from Store API extensions
138     */
139    function processStoreApiEvent(eventData) {
140        if (!eventData || !eventData.event) {
141            return;
142        }
143
144        var params = eventData.params || {};
145
146        var event = {
147            method: 'track',
148            name: eventData.event,
149            params: params,
150            eventId: params.event_id || null
151        };
152
153        fireEvent(event);
154    }
155
156    /**
157     * Set up fetch interceptor to capture Store API responses.
158     * Only intercepts cart/add-item requests to fire AddToCart pixel events.
159     */
160    function setupFetchInterceptor() {
161        var originalFetch = window.fetch;
162        if (!originalFetch) {
163            return;
164        }
165
166        window.fetch = function() {
167            var args = arguments;
168            var url = args[0];
169
170            // Only intercept add-item requests (not general cart requests)
171            var isAddToCartRequest = typeof url === 'string' &&
172                (url.indexOf('/wc/store/v1/cart/add-item') !== -1 ||
173                 url.indexOf('/wc/store/cart/add-item') !== -1);
174
175            return originalFetch.apply(this, args).then(function(response) {
176                if (isAddToCartRequest && response.ok) {
177                    // Clone response so we can read it without consuming
178                    response.clone().json().then(function(responseData) {
179                        if (responseData && responseData.extensions && responseData.extensions['facebook-for-woocommerce']) {
180                            processStoreApiEvent(responseData.extensions['facebook-for-woocommerce']);
181                        }
182                    }).catch(function(e) {
183                        logWarning('Store API JSON parse error:', e);
184                    });
185                }
186                return response;
187            });
188        };
189    }
190
191    /**
192     * Initialize pixel event handling.
193     *
194     * If fbq() is already available, fires queued events immediately.
195     * If not (e.g. consent manager blocking the SDK), uses Object.defineProperty
196     * to set a trap on window.fbq — our handler fires automatically the moment
197     * fbq is assigned, with zero overhead in between. No polling, no timers.
198     *
199     * Also sets up Store API interceptor for WooCommerce Blocks AJAX AddToCart.
200     */
201    function init() {
202        // Set up fetch interceptor for WooCommerce Blocks Store API
203        setupFetchInterceptor();
204
205        if (typeof fbq === 'function') {
206            fireQueuedEvents();
207            return;
208        }
209
210        // fbq doesn't exist yet — watch for it (zero overhead, no timers).
211        // Consent managers block fbq until the
212        // user accepts. This fires the moment they assign window.fbq.
213        var _fbq = window.fbq;
214        Object.defineProperty(window, 'fbq', {
215            configurable: true,
216            enumerable: true,
217            get: function() { return _fbq; },
218            set: function(value) {
219                _fbq = value;
220                if (typeof value === 'function') {
221                    // Restore normal property so FB SDK works normally
222                    Object.defineProperty(window, 'fbq', {
223                        configurable: true,
224                        enumerable: true,
225                        writable: true,
226                        value: value
227                    });
228                    setTimeout(fireQueuedEvents, 0);
229                }
230            }
231        });
232    }
233
234    // Start
235    if (document.readyState === 'complete') {
236        init();
237    } else {
238        window.addEventListener('load', init);
239    }
240
241})();

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.