PageSourceSearch

https://zuomod.com/wp-content/mu-plugins/helpers/wiz-analytics-destination.js?ver=47b7676b

js zuomod.com collected 2026-09-25 18:42:42 UTC 8,075 bytes, 219 lines download raw bytes

1/**
2 * WizAnalytics Destination — thin adapter for WizAnalytics backend API.
3 *
4 * Receives enriched events from WizTracking pipeline.
5 * Normalizes payload per schema, builds wire format, POSTs to /raw-events/track.
6 *
7 * Handles ALL event types.
8 */
9(function () {
10    'use strict';
11
12    var CONFIG = {
13        TRACK_PATH: '/raw-events/track',
14        CHANNEL: 'Wizshop'
15    };
16
17    /**
18     * Payload schema per event type: defaults + optional fields.
19     * Controls what goes in the wire payload to the analytics backend.
20     */
21    var PAYLOAD_SCHEMAS = {
22        product_interaction: {
23            defaults: { type: undefined, variant_id: '', source: 'all_products' },
24            optional: ['product_name', 'price', 'in_stock', 'inventory_available', 'discount_value', 'category_id', 'category_name', 'collection_id', 'collection_name', 'quantity', 'page_number', 'cart_id']
25        },
26        perform_search: {
27            defaults: { search_term: '', result_count: 0, type: 'keyword' },
28            optional: []
29        },
30        cart_viewed: {
31            defaults: { cart_id: '', number_of_products: 0 },
32            optional: ['items', 'currency']
33        },
34        checkout_started: {
35            defaults: { item_count: '', cart_value: '', checkout_source: '', coupon_code: '', cart_id: '', cart_grouping: '' },
36            optional: ['meta', 'items', 'currency']
37        },
38        checkout_steps: {
39            defaults: { step_number: '', step_name: '', cart_id: '', document_id: '' },
40            optional: ['meta', 'items', 'currency']
41        },
42        order_placed: {
43            defaults: { order_id: '', order_value: '', product_count: '', coupon_code: '' },
44            optional: ['meta', 'items', 'currency']
45        },
46        device_id_mapping: {
47            defaults: { device_id: '', user_id: '' },
48            optional: []
49        },
50        perform_suggested_search: {
51            defaults: { search_label: '' },
52            optional: []
53        },
54        cancel_suggested_search: {
55            defaults: { search_label: '' },
56            optional: []
57        }
58    };
59
60    function hasValue(v) {
61        return v !== undefined && v !== null && v !== '';
62    }
63
64    var apiEndpoint = null;
65
66    /**
67     * Normalize payload per schema — apply defaults, strip unknown keys.
68     */
69    function normalizePayload(evt) {
70        var schema = PAYLOAD_SCHEMAS[evt.type];
71        if (!schema) return evt;
72
73        var p = evt.payload || {};
74        var out = {};
75
76        if (schema.defaults) {
77            for (var k in schema.defaults) {
78                if (Object.prototype.hasOwnProperty.call(schema.defaults, k)) {
79                    out[k] = hasValue(p[k]) ? p[k] : schema.defaults[k];
80                }
81            }
82        }
83        if (schema.optional) {
84            for (var i = 0; i < schema.optional.length; i++) {
85                var optKey = schema.optional[i];
86                if (hasValue(p[optKey])) out[optKey] = p[optKey];
87            }
88        }
89
90        return { type: evt.type, payload: out, context: evt.context, meta: evt.meta };
91    }
92
93    /**
94     * Build wire-format payload for the WizAnalytics API.
95     */
96    function buildWirePayload(evt) {
97        var context = evt.context || {};
98        var fp = context.fingerprint || {};
99        var ip = fp.ipLocation || {};
100        var userDetails = context.userDetails;
101        var is_loggedin = !!(userDetails && (userDetails.id || userDetails.user_id));
102        var customerMetadata = context.customerMetadata || {};
103
104        var properties = {
105            time: Math.floor((evt.meta && evt.meta.ts) ? evt.meta.ts / 1000 : Date.now() / 1000),
106            $browser: fp.browserName || '',
107            $browser_version: fp.browserVersion || '',
108            $city: (ip.city && ip.city.name) || '',
109            $region: (ip.subdivisions && ip.subdivisions[0] && ip.subdivisions[0].name) || '',
110            $country: (ip.country && ip.country.name) || '',
111            $timezone: ip.timezone || '',
112            $continent: (ip.continent && ip.continent.name) || '',
113            $accuracy_radius: ip.accuracyRadius || '',
114            $current_url: context.currentUrl || '',
115            $visitor_id: fp.visitorId || '',
116            $postal_code: ip.postalCode || '',
117            $latitude: ip.latitude || '',
118            $longitude: ip.longitude || '',
119            $request_id: fp.requestId || '',
120            $initial_referrer: context.initialReferrer || '',
121            $initial_referring_domain: context.initialReferringDomain || '',
122            $os: fp.os || '',
123            $os_version: fp.osVersion || '',
124            $ip: fp.ip || '',
125            $screen_height: context.screenHeight || 0,
126            $screen_width: context.screenWidth || 0,
127            channel: CONFIG.CHANNEL,
128            customer_metadata: customerMetadata,
129            email: userDetails ? (userDetails.email || '') : '',
130            first_name: userDetails ? (userDetails.first_name || userDetails.user_first_name || '') : '',
131            is_web: true,
132            last_name: userDetails ? (userDetails.last_name || userDetails.user_last_name || '') : '',
133            phone: userDetails ? (userDetails.phone || '') : '',
134            tenant_id: (userDetails && userDetails.tenant_id) || localStorage.getItem('tenant_id') || '',
135            user_id: userDetails ? (userDetails.id || userDetails.user_id || '') : '',
136            is_loggedin: is_loggedin,
137            meta: evt.meta || {}
138        };
139
140        if (evt.payload && typeof evt.payload === 'object') {
141            var payloadCopy = Object.assign({}, evt.payload);
142            delete payloadCopy.event;
143            Object.assign(properties, payloadCopy);
144        }
145
146        var snakeEventName = (evt.type || '').replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '');
147        var result = { event: snakeEventName, properties: properties };
148        var payloadStr = JSON.stringify(result);
149        try {
150            result.properties.event_size = new TextEncoder().encode(payloadStr).length;
151        } catch (e) {
152            result.properties.event_size = payloadStr.length;
153        }
154        return result;
155    }
156
157    /**
158     * POST to WizAnalytics API. Fire-and-forget.
159     */
160    function sendEvent(wirePayload) {
161        if (!apiEndpoint) return;
162        var tenantId = localStorage.getItem('tenant_id') || '';
163
164        fetch(apiEndpoint + CONFIG.TRACK_PATH, {
165            method: 'POST',
166            headers: {
167                'Content-Type': 'application/json',
168                'channel': CONFIG.CHANNEL,
169                'tenant-id': tenantId
170            },
171            body: JSON.stringify({ events: [wirePayload] }),
172            keepalive: true
173        }).catch(function () {
174            // Silently fail — don't break page
175        });
176    }
177
178    /**
179     * Destination handler — receives enriched event from pipeline.
180     */
181    function handle(enrichedEvent) {
182        if (!apiEndpoint) return;
183        var normalized = normalizePayload(enrichedEvent);
184        var wirePayload = buildWirePayload(normalized);
185        sendEvent(wirePayload);
186    }
187
188    // Register with pipeline when DOM is ready
189    function register() {
190        if (typeof wizTrackerConfig !== 'undefined') {
191            apiEndpoint = wizTrackerConfig.apiEndpoint || null;
192        }
193        if (!apiEndpoint) return;
194
195        if (window.WizTracking) {
196            window.WizTracking.addDestination('wizAnalytics', {
197                events: [
198                    'product_interaction',
199                    'perform_search',
200                    'cart_viewed',
201                    'checkout_started',
202                    'checkout_steps',
203                    'order_placed',
204                    'device_id_mapping',
205                    'perform_suggested_search',
206                    'cancel_suggested_search'
207                ],
208                handler: handle
209            });
210        }
211    }
212
213    if (document.readyState === 'loading') {
214        document.addEventListener('DOMContentLoaded', register);
215    } else {
216        register();
217    }
218
219})();

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.