PageSourceSearch

https://www.solario24.com/wp-content/plugins/wc-product-recommendations/assets/js/visitor-tracking.js?ver=3.43

js solario24.com collected 2026-10-08 10:58:08 UTC 42,928 bytes, 884 lines download raw bytes

1(function() {
2            'use strict';
3
4            var API_BASE = window.SOL24_TRACKING_API;
5            var COOKIE_NAME = 'sol24_vid';
6            var SESSION_KEY = 'sol24_session';
7            var SESSION_TIMEOUT = 30 * 60 * 1000; // 30 minutes
8
9            // ========== SESSION MANAGEMENT (like GA) ==========
10            function getOrCreateSession() {
11                var sessionData = sessionStorage.getItem(SESSION_KEY);
12                var now = Date.now();
13
14                if (sessionData) {
15                    try {
16                        var session = JSON.parse(sessionData);
17                        // Check if session is still valid (within timeout)
18                        if (now - session.lastActivity < SESSION_TIMEOUT) {
19                            session.lastActivity = now;
20                            session.pageCount++;
21                            session.isNew = false;
22                            sessionStorage.setItem(SESSION_KEY, JSON.stringify(session));
23                            return session;
24                        }
25                    } catch (e) {}
26                }
27
28                // Create new session
29                var newSession = {
30                    id: 'ses_' + now + '_' + Math.random().toString(36).substr(2, 9),
31                    startTime: now,
32                    lastActivity: now,
33                    pageCount: 1,
34                    isNew: true
35                };
36                sessionStorage.setItem(SESSION_KEY, JSON.stringify(newSession));
37                return newSession;
38            }
39
40            // ========== DEVICE DETECTION ==========
41            function getDeviceInfo() {
42                var ua = navigator.userAgent;
43                var deviceType = 'desktop';
44                if (/Mobile|Android|iPhone|iPad|iPod|Windows Phone/i.test(ua)) {
45                    deviceType = /iPad|Tablet/i.test(ua) ? 'tablet' : 'mobile';
46                }
47
48                var browser = 'other';
49                if (/Chrome/i.test(ua) && !/Edge|Edg/i.test(ua)) browser = 'Chrome';
50                else if (/Firefox/i.test(ua)) browser = 'Firefox';
51                else if (/Safari/i.test(ua) && !/Chrome/i.test(ua)) browser = 'Safari';
52                else if (/Edge|Edg/i.test(ua)) browser = 'Edge';
53                else if (/MSIE|Trident/i.test(ua)) browser = 'IE';
54
55                var os = 'other';
56                if (/Windows/i.test(ua)) os = 'Windows';
57                else if (/Mac OS/i.test(ua)) os = 'macOS';
58                else if (/Android/i.test(ua)) os = 'Android';
59                else if (/iOS|iPhone|iPad|iPod/i.test(ua)) os = 'iOS';
60                else if (/Linux/i.test(ua)) os = 'Linux';
61
62                return {
63                    screen_width: screen.width,
64                    screen_height: screen.height,
65                    viewport_width: window.innerWidth,
66                    viewport_height: window.innerHeight,
67                    device_type: deviceType,
68                    browser: browser,
69                    os: os,
70                    language: navigator.language || navigator.userLanguage
71                };
72            }
73
74            // ========== PAGE TYPE DETECTION ==========
75            function getPageType() {
76                var body = document.body;
77                if (body.classList.contains('home') || body.classList.contains('front-page')) return 'home';
78                if (body.classList.contains('single-product')) return 'product';
79                if (body.classList.contains('woocommerce-cart')) return 'cart';
80                if (body.classList.contains('woocommerce-checkout')) return 'checkout';
81                if (body.classList.contains('archive') || body.classList.contains('product-category')) return 'category';
82                if (body.classList.contains('woocommerce-account')) return 'account';
83                if (body.classList.contains('search-results')) return 'search';
84                return 'other';
85            }
86
87            function getCategory() {
88                // Try to get category from breadcrumbs or body class
89                var breadcrumb = document.querySelector('.woocommerce-breadcrumb');
90                if (breadcrumb) {
91                    var links = breadcrumb.querySelectorAll('a');
92                    if (links.length > 1) return links[links.length - 1].textContent.trim();
93                }
94                return null;
95            }
96
97            // Store visitor_id in localStorage for persistence across pages
98            var STORAGE_KEY = 'sol24_visitor_id';
99            var currentVisitorId = null;
100
101            function loadVisitorId() {
102                // 1. Check URL parameter first (from email click)
103                var params = new URLSearchParams(window.location.search);
104                var vid = params.get('vid');
105                if (vid) {
106                    localStorage.setItem(STORAGE_KEY, vid);
107                    currentVisitorId = vid;
108                    return vid;
109                }
110                // 2. Check localStorage (persists across pages)
111                var stored = localStorage.getItem(STORAGE_KEY);
112                if (stored) {
113                    currentVisitorId = stored;
114                    return stored;
115                }
116                // 3. Check cookie as fallback
117                var match = document.cookie.match(new RegExp('(^| )' + COOKIE_NAME + '=([^;]+)'));
118                if (match) {
119                    currentVisitorId = match[2];
120                    localStorage.setItem(STORAGE_KEY, match[2]);
121                    return match[2];
122                }
123                return null;
124            }
125
126            function getVisitorId() {
127                if (currentVisitorId) return currentVisitorId;
128                return loadVisitorId();
129            }
130
131            function track(endpoint, data) {
132                // Always include visitor_id in request body
133                var vid = getVisitorId();
134                if (vid && !data.visitor_id) {
135                    data.visitor_id = vid;
136                }
137                var xhr = new XMLHttpRequest();
138                xhr.open('POST', API_BASE + '/api/v1/tracking/' + endpoint, true);
139                xhr.setRequestHeader('Content-Type', 'application/json');
140                xhr.withCredentials = true;
141                xhr.send(JSON.stringify(data));
142            }
143
144            // Parse German price format "1.234,56 €" to float 1234.56
145            function parseGermanPrice(text) {
146                if (!text) return 0;
147                // Remove currency symbols, whitespace
148                var cleaned = text.replace(/[€$£\s]/g, '').trim();
149                // Check if it's German format (has comma as decimal separator)
150                if (cleaned.indexOf(',') > -1) {
151                    // German format: 1.234,56 -> remove dots (thousands), replace comma with dot
152                    cleaned = cleaned.replace(/\./g, '').replace(',', '.');
153                }
154                // Otherwise assume it's already in standard format (1234.56)
155                var result = parseFloat(cleaned);
156                return isNaN(result) ? 0 : result;
157            }
158
159            function init() {
160                // Load visitor_id from URL/localStorage/cookie
161                loadVisitorId();
162
163                var xhr = new XMLHttpRequest();
164                var initUrl = API_BASE + '/api/v1/tracking/init';
165
166                // Pass visitor_id if we have one
167                if (currentVisitorId) {
168                    initUrl += '?vid=' + encodeURIComponent(currentVisitorId);
169                }
170
171                xhr.open('GET', initUrl, true);
172                xhr.withCredentials = true;
173                xhr.onload = function() {
174                    if (xhr.status === 200) {
175                        try {
176                            var response = JSON.parse(xhr.responseText);
177                            if (response.visitor_id) {
178                                currentVisitorId = response.visitor_id;
179                                localStorage.setItem(STORAGE_KEY, response.visitor_id);
180                            }
181                        } catch(e) {}
182                        trackPageView();
183                        checkIdentification();
184                        initEngagementTracking();
185                    }
186                };
187                xhr.onerror = function() {
188                    console.log('Sol24 Tracking: Could not connect to API');
189                };
190                xhr.send();
191            }
192
193            function trackPageView() {
194                var params = new URLSearchParams(window.location.search);
195                var productSku = getProductSku();
196                var session = getOrCreateSession();
197                var deviceInfo = getDeviceInfo();
198
199                track('pageview', {
200                    page_url: window.location.href,
201                    page_title: document.title,
202                    product_sku: productSku,
203                    referrer: document.referrer,
204                    utm_source: params.get('utm_source'),
205                    utm_medium: params.get('utm_medium'),
206                    utm_campaign: params.get('utm_campaign'),
207                    // GA-like session tracking
208                    session_id: session.id,
209                    is_new_session: session.isNew,
210                    // Page classification
211                    page_type: getPageType(),
212                    category: getCategory(),
213                    // Device info
214                    screen_width: deviceInfo.screen_width,
215                    screen_height: deviceInfo.screen_height,
216                    viewport_width: deviceInfo.viewport_width,
217                    viewport_height: deviceInfo.viewport_height,
218                    device_type: deviceInfo.device_type,
219                    browser: deviceInfo.browser,
220                    os: deviceInfo.os,
221                    language: deviceInfo.language
222                });
223            }
224
225            // ========== ENGAGEMENT TRACKING (like GA) ==========
226            var scrollDepthMarkers = [25, 50, 75, 90];
227            var scrollDepthTracked = {};
228            var pageLoadTime = Date.now();
229
230            function initEngagementTracking() {
231                // Scroll depth tracking
232                window.addEventListener('scroll', throttle(function() {
233                    var scrollPercent = Math.round((window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100);
234                    scrollDepthMarkers.forEach(function(marker) {
235                        if (scrollPercent >= marker && !scrollDepthTracked[marker]) {
236                            scrollDepthTracked[marker] = true;
237                            trackEvent('scroll_depth', null, { depth: marker });
238                        }
239                    });
240                }, 500));
241
242                // Time on page tracking (at intervals and on unload)
243                var timeIntervals = [30, 60, 120, 300]; // 30s, 1m, 2m, 5m
244                var timeTracked = {};
245                setInterval(function() {
246                    var timeOnPage = Math.round((Date.now() - pageLoadTime) / 1000);
247                    timeIntervals.forEach(function(interval) {
248                        if (timeOnPage >= interval && !timeTracked[interval]) {
249                            timeTracked[interval] = true;
250                            trackEvent('time_on_page', null, { seconds: interval });
251                        }
252                    });
253                }, 10000);
254
255                // Track time on page before leaving
256                window.addEventListener('beforeunload', function() {
257                    var timeOnPage = Math.round((Date.now() - pageLoadTime) / 1000);
258                    if (timeOnPage > 5) {
259                        trackEvent('page_exit', null, {
260                            time_on_page: timeOnPage,
261                            max_scroll_depth: Math.max.apply(null, Object.keys(scrollDepthTracked).map(Number)) || 0
262                        });
263                    }
264                });
265
266                // Track search
267                var searchForm = document.querySelector('.search-form, form.woocommerce-product-search');
268                if (searchForm) {
269                    searchForm.addEventListener('submit', function(e) {
270                        var query = searchForm.querySelector('input[type="search"], input[name="s"]');
271                        if (query && query.value) {
272                            trackEvent('search', null, { query: query.value });
273                        }
274                    });
275                }
276
277                // Track search result clicks (when on search results page)
278                if (document.body.classList.contains('search-results') || document.body.classList.contains('search')) {
279                    var searchQuery = '';
280                    var urlParams = new URLSearchParams(window.location.search);
281                    searchQuery = urlParams.get('s') || '';
282
283                    if (searchQuery) {
284                        // Find all product links in search results
285                        var productLinks = document.querySelectorAll('.products .product a.woocommerce-loop-product__link, .products .product a.woocommerce-LoopProduct-link, ul.products li.product > a');
286
287                        productLinks.forEach(function(link, index) {
288                            var product = link.closest('.product');
289                            if (!product) return;
290
291                            // Get SKU from product element
292                            var sku = '';
293                            var skuEl = product.querySelector('[data-product_sku]');
294                            if (skuEl) {
295                                sku = skuEl.dataset.product_sku;
296                            } else {
297                                // Try to extract from product class (post-123)
298                                var classMatch = product.className.match(/post-(\d+)/);
299                                if (classMatch) {
300                                    sku = 'id:' + classMatch[1]; // Fallback to product ID
301                                }
302                            }
303
304                            link.addEventListener('click', function() {
305                                trackEvent('search_click', sku, {
306                                    query: searchQuery,
307                                    position: index + 1,
308                                    page_type: 'search'
309                                });
310                            });
311                        });
312
313                        // Also track clicks on add-to-cart buttons in search results
314                        var addToCartBtns = document.querySelectorAll('.products .product .add_to_cart_button, .products .product .ajax_add_to_cart');
315                        addToCartBtns.forEach(function(btn, index) {
316                            btn.addEventListener('click', function() {
317                                var sku = btn.dataset.product_sku || '';
318                                trackEvent('search_add_to_cart', sku, {
319                                    query: searchQuery,
320                                    position: index + 1,
321                                    page_type: 'search'
322                                });
323                            });
324                        });
325                    }
326                }
327
328                // Track outbound links
329                document.addEventListener('click', function(e) {
330                    var link = e.target.closest('a');
331                    if (link && link.href && link.hostname !== window.location.hostname) {
332                        trackEvent('outbound_link', null, { url: link.href });
333                    }
334                });
335            }
336
337            function throttle(func, limit) {
338                var inThrottle;
339                return function() {
340                    if (!inThrottle) {
341                        func.apply(this, arguments);
342                        inThrottle = true;
343                        setTimeout(function() { inThrottle = false; }, limit);
344                    }
345                };
346            }
347
348            function trackEvent(eventType, productSku, eventData) {
349                var session = getOrCreateSession();
350                track('event', {
351                    event_type: eventType,
352                    product_sku: productSku,
353                    event_data: eventData,
354                    session_id: session.id
355                });
356            }
357
358            function checkIdentification() {
359                var params = new URLSearchParams(window.location.search);
360                var email = params.get('email');
361                var uid = params.get('uid');
362
363                if (email || uid) {
364                    var url = API_BASE + '/api/v1/tracking/identify?';
365                    if (email) url += 'email=' + encodeURIComponent(email);
366                    if (uid) url += 'uid=' + encodeURIComponent(uid);
367                    url += '&source=newsletter';
368
369                    var xhr = new XMLHttpRequest();
370                    xhr.open('GET', url, true);
371                    xhr.withCredentials = true;
372                    xhr.send();
373                }
374            }
375
376            function getProductSku() {
377                // WooCommerce product page - check various data sources
378                // IMPORTANT: Check MAIN PRODUCT first, not recommendation widgets!
379                var sku = null;
380
381                // 1. PRIORITY: Check for WooCommerce product SKU in product meta area
382                // This is the most reliable source for the MAIN product
383                var skuWrapper = document.querySelector('.product_meta .sku, .sku_wrapper .sku, .summary .sku');
384                if (skuWrapper && skuWrapper.textContent.trim()) {
385                    return skuWrapper.textContent.trim();
386                }
387
388                // 2. Check meta tags (set by WooCommerce for the main product)
389                var metaSku = document.querySelector('meta[property="product:sku"]');
390                if (metaSku && metaSku.content) return metaSku.content;
391
392                // 3. Check WooCommerce product form (main product add-to-cart)
393                var form = document.querySelector('form.cart');
394                if (form) {
395                    // Check for data-product_sku on the form itself
396                    if (form.dataset && form.dataset.product_sku) {
397                        return form.dataset.product_sku;
398                    }
399                    // Check for hidden input with SKU
400                    var skuInput = form.querySelector('input[name="sku"]');
401                    if (skuInput && skuInput.value) return skuInput.value;
402                }
403
404                // 4. Check for main product container (not recommendation widgets!)
405                // Look for data-product_sku ONLY in the main product area
406                var mainProduct = document.querySelector('.product.type-product, .single-product .product');
407                if (mainProduct) {
408                    var mainSkuEl = mainProduct.querySelector(':scope >
408 [data-product_sku], .summary [data-product_sku]');
409                    if (mainSkuEl && mainSkuEl.dataset.product_sku) {
410                        return mainSkuEl.dataset.product_sku;
411                    }
412                }
413
414                // 5. Extract SKU from URL as fallback (e.g., /produkt/SM-AU430/ or ?sku=SM-AU430)
415                var urlMatch = window.location.href.match(/[?&]sku=([^&]+)/);
416                if (urlMatch) return decodeURIComponent(urlMatch[1]);
417
418                // 6. Last resort: data-sku attribute (but not in recommendation sections)
419                var dataEl = document.querySelector('.product:not(.wc-product-rec) [data-sku]');
420                if (dataEl && dataEl.dataset.sku) return dataEl.dataset.sku;
421
422                return null;
423            }
424
425            // ========== A/B TEST TRACKING ==========
426            // Track A/B test events (click, add_to_cart, conversion)
427            function trackABTestEvent(eventType, testId, variant, productId, recommendedProductId, position, revenue) {
428                if (!testId) return;
429
430                var xhr = new XMLHttpRequest();
431                xhr.open('POST', API_BASE + '/api/v1/admin/ab-test-event', true);
432                xhr.setRequestHeader('Content-Type', 'application/json');
433                xhr.withCredentials = true;
434                xhr.send(JSON.stringify({
435                    test_id: parseInt(testId),
436                    variant: variant,
437                    event_type: eventType,
438                    session_id: getVisitorId(),
439                    product_id: productId ? parseInt(productId) : null,
440                    recommended_product_id: recommendedProductId ? parseInt(recommendedProductId) : null,
441                    position: position ? parseInt(position) : null,
442                    revenue: revenue ? parseFloat(revenue) : null
443                }));
444            }
445
446            // Get A/B test info from a recommendation element
447            function getABTestInfo(element) {
448                var track = element.closest('.wc-rec-track');
449                if (!track) return null;
450
451                var testId = track.dataset.abTestId;
452                var variant = track.dataset.abVariant;
453
454                if (!testId || !variant) return null;
455
456                return {
457                    testId: testId,
458                    variant: variant,
459                    recType: track.dataset.recType,
460                    sourceProduct: track.dataset.sourceProduct
461                };
462            }
463
464            // Public API - GA-like comprehensive tracking
465            window.Sol24Tracking = {
466                trackEvent: function(eventType, productSku, eventData) {
467                    // Uses internal trackEvent which includes session_id
468                    trackEvent(eventType, productSku, eventData);
469                },
470                identify: function(email, source) {
471                    track('identify', {
472                        email: email,
473                        source: source || 'form'
474                    });
475                },
476                trackPageView: trackPageView,
477                trackABTestEvent: trackABTestEvent,
478                // Additional GA-like methods
479                getSessionId: function() {
480                    return getOrCreateSession().id;
481                },
482                getDeviceInfo: getDeviceInfo,
483                getPageType: getPageType
484            };
485
486            // Initialize tracking immediately (before jQuery-dependent code)
487            if (document.readyState === 'loading') {
488                document.addEventListener('DOMContentLoaded', init);
489            } else {
490                init();
491            }
492
493            // ========== META PIXEL-LIKE E-COMMERCE TRACKING ==========
494
495            // Track ViewContent (product page)
496            function trackViewContent() {
497                var pageType = getPageType();
498                console.log('Sol24 Tracking: trackViewContent called, pageType=' + pageType);
499                if (pageType !== 'product') return;
500
501                var productData = getProductData();
502                console.log('Sol24 Tracking: Product data:', productData);
503                if (productData) {
504                    console.log('Sol24 Tracking: ViewContent', productData);
505                    track('event', {
506                        event_type: 'ViewContent',
507                        product_sku: productData.sku,
508                        event_data: {
509                            content_type: 'product',
510                            content_ids: [productData.sku],
511                            content_name: productData.name,
512                            content_category: productData.category,
513                            value: productData.price,
514                            currency: 'EUR'
515                        }
516                    });
517                }
518            }
519
520            // Get product data from product page
521            function getProductData() {
522                try {
523                    var sku = getProductSku();
524                    var name = document.querySelector('.product_title, h1.entry-title');
525                    var priceEl = document.querySelector('.price .woocommerce-Price-amount, .summary .price ins .amount, .summary .price .amount');
526                    var category = getCategory();
527
528                    var price = null;
529                    if (priceEl) {
530                        price = parseGermanPrice(priceEl.textContent);
531                    }
532
533                    return {
534                        sku: sku,
535                        name: name ? name.textContent.trim() : null,
536                        price: price,
537                        category: category
538                    };
539                } catch(e) {
540                    return null;
541                }
542            }
543
544            // Track cart contents (ViewCart event)
545            function trackViewCart() {
546                var pageType = getPageType();
547                console.log('Sol24 Tracking: trackViewCart called, pageType=' + pageType);
548                if (pageType !== 'cart') return;
549
550                var cartItems = getCartItems();
551                console.log('Sol24 Tracking: Cart items found:', cartItems);
552                if (cartItems && cartItems.length > 0) {
553                    var totalValue = cartItems.reduce(function(sum, item) {
554                        return sum + (item.price * item.quantity);
555                    }, 0);
556
557                    console.log('Sol24 Tracking: ViewCart', { items: cartItems.length, value: totalValue });
558                    track('event', {
559                        event_type: 'ViewCart',
560                        event_data: {
561                            content_type: 'product',
562                            contents: cartItems,
563                            content_ids: cartItems.map(function(i) { return i.sku; }),
564                            num_items: cartItems.reduce(function(sum, i) { return sum + i.quantity; }, 0),
565                            value: totalValue,
566                            currency: 'EUR'
567                        }
568                    });
569                } else {
570                    console.log('Sol24 Tracking: No cart items found on cart page');
571                }
572            }
573
574            // Track InitiateCheckout
575            function trackInitiateCheckout() {
576                var pageType = getPageType();
577                console.log('Sol24 Tracking: trackInitiateCheckout called, pageType=' + pageType);
578                if (pageType !== 'checkout') return;
579
580                var cartItems = getCartItems();
581                console.log('Sol24 Tracking: Checkout items found:', cartItems);
582                if (cartItems && cartItems.length > 0) {
583                    var totalValue = cartItems.reduce(function(sum, item) {
584                        return sum + (item.price * item.quantity);
585                    }, 0);
586
587                    console.log('Sol24 Tracking: InitiateCheckout', { items: cartItems.length, value: totalValue });
588                    track('event', {
589                        event_type: 'InitiateCheckout',
590                        event_data: {
591                            content_type: 'product',
592                            contents: cartItems,
593                            content_ids: cartItems.map(function(i) { return i.sku; }),
594                            num_items: cartItems.reduce(function(sum, i) { return sum + i.quantity; }, 0),
595                            value: totalValue,
596                            currency: 'EUR'
597                        }
598                    });
599                } else {
600                    console.log('Sol24 Tracking: No items found on checkout page');
601                }
602            }
603
604            // Get cart items from page
605            function getCartItems() {
606                var items = [];
607                try {
608                    console.log('Sol24 Tracking: getCartItems() searching for cart rows...');
609
610                    // WooCommerce cart page - try multiple selectors
611                    var cartRows = document.querySelectorAll('.woocommerce-cart-form .cart_item, .woocommerce-checkout-review-order-table .cart_item, table.cart .cart_item, tr.cart_item, .shop_table .cart_item');
612                    console.log('Sol24 Tracking: Found ' + cartRows.length + ' cart rows');
613
614                    cartRows.forEach(function(row, idx) {
615                        // IMPORTANT: Exclude .remove links (× button) - they are NOT the product name!
616                        var nameEl = row.querySelector('.product-name a:not(.remove), td.product-name a:not(.remove), .cart_item a[href*="/produkt/"]:not(.remove)');
617                        var skuEl = row.querySelector('[data-product_sku]');
618                        var qtyEl = row.querySelector('.quantity input, input.qty, .product-quantity, td.product-quantity input');
619                        var priceEl = row.querySelector('.product-price .amount, td.product-price .amount, .product-price bdi');
620                        var subtotalEl = row.querySelector('.product-subtotal .amount, td.product-subtotal .amount, .product-subtotal bdi');
621
622                        // Get name, fallback to data attribute or URL parsing
623                        var name = nameEl ? nameEl.textContent.trim() : '';
624
625                        // If name is empty or just special chars, try to get it from the product link
626                        if (!name || name === '×' || name.length < 3) {
627                            // IMPORTANT: Also exclude .remove links in the fallback!
628                            var productLink = row.querySelector('a[href*="/produkt/"]:not(.remove)');
629                            if (productLink && productLink.textContent.trim().length > 2 && productLink.textContent.trim() !== '×') {
630                                name = productLink.textContent.trim();
631                            }
632                        }
633
634                        // Final validation: if name is still invalid, try alternative selectors
635                        if (!name || name === '×' || name.length < 3) {
636                            // Try td.product-name direct text or data-title attribute
637                            var productNameTd = row.querySelector('.product-name, td[data-title="Product"], td[data-title="Produkt"]');
638                            if (productNameTd) {
639                                var links = productNameTd.querySelectorAll('a:not(.remove)');
640                                for (var i = 0; i < links.length; i++) {
641                                    var linkText = links[i].textContent.trim();
642                                    if (linkText && linkText !== '×' && linkText.length > 2) {
643                                        name = linkText;
644                                        break;
645                                    }
646                                }
647                            }
648                        }
649                        var sku = skuEl ? skuEl.dataset.product_sku : (nameEl ? nameEl.href.split('/').filter(
649Boolean).pop() : '');
650                        var qty = qtyEl ? (parseInt(qtyEl.value || qtyEl.textContent) || 1) : 1;
651                        var price = 0;
652
653                        if (priceEl) {
654                            price = parseGermanPrice(priceEl.textContent) || 0;
655                        } else if (subtotalEl) {
656                            var subtotal = parseGermanPrice(subtotalEl.textContent) || 0;
657                            price = qty > 0 ? subtotal / qty : 0;
658                        }
659
660                        console.log('Sol24 Tracking: Row ' + idx + ': name=' + name + ', sku=' + sku + ', qty=' + qty + ', price=' + price);
661
662                        if (name || sku) {
663                            items.push({
664                                sku: sku,
665                                name: name,
666                                quantity: qty,
667                                price: price
668                            });
669                        }
670                    });
671
672                    // Also try mini-cart / fragments if no items found
673                    if (items.length === 0) {
674                        console.log('Sol24 Tracking: No cart rows, trying mini-cart...');
675                        var miniCartItems = document.querySelectorAll('.mini_cart_item, .woocommerce-mini-cart-item');
676                        console.log('Sol24 Tracking: Found ' + miniCartItems.length + ' mini-cart items');
677
678                        miniCartItems.forEach(function(item) {
679                            var nameEl = item.querySelector('a:not(.remove)');
680                            var qtyEl = item.querySelector('.quantity');
681                            if (nameEl) {
682                                var qtyMatch = qtyEl ? qtyEl.textContent.match(/(\d+)/) : null;
683                                items.push({
684                                    sku: nameEl.href.split('/').filter(Boolean).pop(),
685                                    name: nameEl.textContent.trim(),
686                                    quantity: qtyMatch ? parseInt(qtyMatch[1]) : 1,
687                                    price: 0
688                                });
689                            }
690                        });
691                    }
692
693                    // Try order review table on checkout (different structure)
694                    if (items.length === 0) {
695                        console.log('Sol24 Tracking: Trying order review table...');
696                        var reviewRows = document.querySelectorAll('.woocommerce-checkout-review-order-table tbody tr, .order-total, .cart_totals tr');
697                        reviewRows.forEach(function(row) {
698                            var nameEl = row.querySelector('.product-name, td:first-child');
699                            var qtyMatch = nameEl ? nameEl.textContent.match(/×\s*(\d+)/) : null;
700                            var priceEl = row.querySelector('.product-total .amount, td:last-child .amount');
701
702                            if (nameEl && !row.classList.contains('order-total') && !row.classList.contains('cart-subtotal')) {
703                                var nameText = nameEl.textContent.replace(/×\s*\d+/, '').trim();
704                                if (nameText && nameText.length > 2) {
705                                    items.push({
706                                        sku: '',
707                                        name: nameText,
708                                        quantity: qtyMatch ? parseInt(qtyMatch[1]) : 1,
709                                        price: priceEl ? parseGermanPrice(priceEl.textContent) : 0
710                                    });
711                                }
712                            }
713                        });
714                    }
715                } catch(e) {
716                    console.log('Sol24 Tracking: Error reading cart', e);
717                }
718                console.log('Sol24 Tracking: getCartItems() returning ' + items.length + ' items');
719                return items;
720            }
721
722            // Enhanced page tracking based on page type
723            function trackEcommerceEvents() {
724                var pageType = getPageType();
725                console.log('Sol24 Tracking: trackEcommerceEvents called, pageType=' + pageType);
726
727                if (pageType === 'product') {
728                    trackViewContent();
729                } else if (pageType === 'cart') {
730                    trackViewCart();
731                } else if (pageType === 'checkout') {
732                    trackInitiateCheckout();
733                }
734            }
735
736            // ========== RECOMMENDATION CLICK TRACKING ==========
737            // Track clicks on recommended products (jQuery-dependent, runs after jQuery loads)
738            function initJQueryTracking() {
739                if (typeof jQuery === 'undefined') {
740                    // Wait for jQuery
741                    setTimeout(initJQueryTracking, 100);
742                    return;
743                }
744
745                // Track e-commerce events after jQuery is ready
746                trackEcommerceEvents();
747
748            // Track clicks on BOTH regular slides (.wc-rec-slide) AND FBT 
748cards (.wc-rec-fbt-card)
749            jQuery(document).on('click', '.wc-rec-slide a, .wc-rec-fbt-card a', function(e) {
750                var $slide = jQuery(this).closest('.wc-rec-slide, .wc-rec-fbt-card');
751                var abInfo = getABTestInfo($slide[0]);
752
753                if (abInfo) {
754                    var productId = $slide.data('product-id');
755                    var position = $slide.data('position');
756                    var sourceProduct = abInfo.sourceProduct;
757
758                    // Track click event for A/B test
759                    trackABTestEvent('click', abInfo.testId, abInfo.variant, sourceProduct, productId, position, null);
760
761                    // Also track in visitor events
762                    var sku = $slide.data('product-sku');
763                    if (sku) {
764                        window.Sol24Tracking.trackEvent('recommendation_click', sku, {
765                            position: position,
766                            rec_type: abInfo.recType,
767                            ab_test_id: abInfo.testId,
768                            ab_variant: abInfo.variant
769                        });
770                    }
771                }
772            });
773
774            // ========== ADD TO CART TRACKING (Meta Pixel style) ==========
775            // Auto-track add to cart (WooCommerce AJAX) with full product details
776            jQuery(document.body).on('added_to_cart', function(event, fragments, cart_hash, $button) {
777                var productId = $button.data('product_id');
778                var sku = $button.data('product_sku') || getProductSku();
779                var quantity = parseInt($button.closest('form').find('input.qty').val()) || 1;
780
781                // Get product details from page
782                var productName = jQuery('.product_title, h1.entry-title').first().text().trim();
783                var priceEl = jQuery('.price .woocommerce-Price-amount, .summary .price ins .amount, .summary .price .amount').first();
784                var price = priceEl.length ? parseGermanPrice(priceEl.text()) : 0;
785                var category = getCategory();
786
787                // Check if this is from a recommendation (regular slide OR FBT card)
788                var $slide = $button.closest('.wc-rec-slide, .wc-rec-fbt-card');
789                var abInfo = $slide.length ? getABTestInfo($slide[0]) : null;
790
791                if (abInfo) {
792                    var position = $slide.data('position');
793                    var sourceProduct = abInfo.sourceProduct;
794
795                    // Track add_to_cart for A/B test
796                    trackABTestEvent('add_to_cart', abInfo.testId, abInfo.variant, sourceProduct, productId, position, price * quantity);
797
798                    // Store A/B test info in sessionStorage for conversion tracking
799                    var abCartItems = JSON.parse(sessionStorage.getItem('sol24_ab_cart_items') || '{}');
800                    abCartItems[productId] = {
801                        testId: abInfo.testId,
802                        variant: abInfo.variant,
803                        position: position,
804                        sourceProduct: sourceProduct,
805                        addedAt: Date.now()
806                    };
807                    sessionStorage.setItem('sol24_ab_cart_items', JSON.stringify(abCartItems));
808                }
809
810                // Meta Pixel style AddToCart event
811                track('event', {
812                    event_type: 'AddToCart',
813                    product_sku: sku,
814                    event_data: {
815                        content_type: 'product',
816                        content_ids: [sku],
817                        content_name: productName,
818                        content_category: category,
819                        quantity: quantity,
820                        value: price * quantity,
821                        currency: 'EUR',
822                        product_id: productId,
823                        from_recommendation: !!abInfo,
824                        ab_test_id: abInfo ? abInfo.testId : null,
825                        ab_variant: abInfo ? abInfo.variant : null
826                    }
827                });
828            });
829
830            // Auto-track add to cart (form submit for variable products)
831            jQuery(document).on('submit', 'form.cart', function() {
832                var sku = getProductSku();
833                if (sku) {
834                    window.Sol24Tracking.trackEvent('add_to_cart', sku, {
835                        quantity: jQuery(this).find('input[name="quantity"]').val() || 1
836                    });
837                }
838            });
839            // Track AddPaymentInfo when payment method is selected or form is filled
840            jQuery(document).on('change', 'input[name="payment_method"]', function() {
841                var cartItems = getCartItems();
842                var totalValue = cartItems.reduce(function(sum, item) {
843                    return sum + (item.price * item.quantity);
844                }, 0);
845
846                console.log('Sol24 Tracking: AddPaymentInfo', { items: cartItems.length, value: totalValue });
847                track('event', {
848                    event_type: 'AddPaymentInfo',
849                    event_data: {
850                        content_type: 'product',
851                        contents: cartItems,
852                        content_ids: cartItems.map(function(i) { return i.sku; }),
853                        value: totalValue,
854                        currency: 'EUR',
855                        payment_method: jQuery(this).val()
856                    }
857                });
858            });
859
860            // Track checkout form submission (before actual purchase)
861            jQuery(document).on('submit', 'form.checkout', function() {
862                var cartItems = getCartItems();
863                var totalValue = cartItems.reduce(function(sum, item) {
864                    return sum + (item.price * item.quantity);
865                }, 0);
866
867                console.log('Sol24 Tracking: SubmitCheckout', { items: cartItems.length, value: totalValue });
868                track('event', {
869                    event_type: 'SubmitCheckout',
870                    event_data: {
871                        content_type: 'product',
872                        contents: cartItems,
873                        content_ids: cartItems.map(function(i) { return i.sku; }),
874                        value: totalValue,
875                        currency: 'EUR'
876                    }
877                });
878            });
879
880            } // end initJQueryTracking
881
882            // Start jQuery tracking when ready
883            initJQueryTracking();
884})();

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.