PageSourceSearch

https://bhi.org.il/wp-content/plugins/pixelyoursite-pro/dist/scripts/public.js?ver=12.6.3

js bhi.org.il collected 2026-10-01 15:03:22 UTC 385,133 bytes, 8,478 lines download raw bytes

1/* global pysOptions */
2
3! function ($, options) {
4    if (options.debug) {
5        console.log('PYS:', options);
6    }
7
8    var uniqueId = {};
9
10    // State tracking for AND trigger logic: { eventId: { triggered: {}, total: N, fired: false } }
11    var pysCustomEventsState = {};
12
13    // State tracking for "once per page" fire frequency: { eventId: true }
14    var pysEventFiredOnce = {};
15    if (options.triggerLogic) {
16        Object.entries(options.triggerLogic).forEach(function([eventId, logic]) {
17            if (logic === 'AND') {
18                var count = 0;
19                Object.values(options.triggerEventTypes).forEach(function(events) {
20                    if (events.hasOwnProperty(eventId)) count++;
21                });
22                pysCustomEventsState[eventId] = { triggered: {}, total: count, fired: false };
23            }
24        });
25    }
26
27    var firstVisit = false;
28
29    var isTrackEventForGA = [];
30
31    var isAdsLoad = false;
32
33    let gtm_variables = {};
34    let gtm_datalayername = "dynamicVariable";
35    var domain = '';
36    if(options.hasOwnProperty("track_cookie_for_subdomains") && options.track_cookie_for_subdomains) {
37        domain = getRootDomain(true);
38    }
39
40    // Per-page referrer for Facebook CAPI events. Captured once per script
41    // load so SPA-style navigations within the same HTML document do not
42    // overwrite it. Internal/external is NOT filtered — Meta expects this
43    // field to describe the URL of the previous page for THIS event.
44    var pageReferrer = '';
45    try {
46        pageReferrer = stripUrlQuery((document.referrer || '').toString());
47    } catch (e) {}
48
49    // Session-entry referrer (fallback). Captured at session start the first
50    // time the visitor lands from an EXTERNAL site, mirrored to a session
51    // cookie so PHP can read it for orphan events (async/cron) that arrive
52    // without a per-page payload. Never the primary source.
53    var PYS_SESSION_ENTRY_REFERRER_KEY = 'pys_session_entry_referrer';
54    function stripUrlQuery(url) {
55        if (!url) return '';
56        var clean = String(url);
57        var q = clean.indexOf('?');
58        if (q !== -1) clean = clean.substring(0, q);
59        var h = clean.indexOf('#');
60        if (h !== -1) clean = clean.substring(0, h);
61        return clean;
62    }
63    var sessionEntryReferrer = '';
64    function getOrInitSessionEntryReferrer(consentGranted) {
65        var value = '';
66        try {
67            if (typeof window.sessionStorage !== 'undefined') {
68                value = window.sessionStorage.getItem(PYS_SESSION_ENTRY_REFERRER_KEY) || '';
69            }
70        } catch (e) {}
71
72        if (!value) {
73            var cookieValue = Cookies.get(PYS_SESSION_ENTRY_REFERRER_KEY);
74            if (cookieValue && cookieValue !== 'undefined') {
75                value = cookieValue;
76                try {
77                    if (typeof window.sessionStorage !== 'undefined') {
78                        window.sessionStorage.setItem(PYS_SESSION_ENTRY_REFERRER_KEY, value);
79                    }
80                } catch (e) {}
81            }
82        }
83
84        if (!value) {
85            var raw = '';
86            try {
87                raw = stripUrlQuery((document.referrer || '').toString());
88            } catch (e) {}
89            if (raw) {
90                var refHost = '';
91                try {
92                    refHost = new URL(raw).hostname;
93                } catch (e) {}
94                if (refHost && refHost !== window.location.hostname) {
95                    value = raw;
96                    try {
97                        if (typeof window.sessionStorage !== 'undefined') {
98                            window.sessionStorage.setItem(PYS_SESSION_ENTRY_REFERRER_KEY, value);
99                        }
100                    } catch (e) {}
101                }
102            }
103        }
104
105        // Mirror to a session cookie only once consent is given. The cookie
106        // is session-scoped (no expires) and dies with the browser, just
107        // like the sessionStorage entry.
108        if (value && consentGranted && options && options.cookie
109            && !options.cookie.disabled_all_cookie) {
110            var existingCookie = Cookies.get(PYS_SESSION_ENTRY_REFERRER_KEY);
111            if (!existingCookie || existingCookie === 'undefined') {
112                Cookies.set(PYS_SESSION_ENTRY_REFERRER_KEY, value, { path: '/', domain: domain });
113            }
114        }
115
116        sessionEntryReferrer = value;
117        return value;
118    }
119
120    // Populate sessionStorage / sessionEntryReferrer immediately. Cookie
121    // mirroring is deferred to the consent-gated path in Facebook.loadPixel().
122    getOrInitSessionEntryReferrer(false);
123
124    // Per-page event referrer (session cookie). Snapshot of document.referrer
125    // for the CURRENT page, refreshed on every page load. Read server-side
126    // by saveFbTagsInOrder() to capture the buyer's checkout-page referrer
127    // for orphan CAPI events (APT, gateway webhooks, manual status changes).
128    // Internal URLs are intentionally NOT filtered — the goal is the page
129    // that led the buyer to checkout, which is usually internal.
130    // If document.referrer is empty (direct navigation), the previous value
131    // is preserved so a session that began with an external referrer keeps
132    // it across same-tab navigations.
133    var PYS_EVENT_REFERRER_KEY = 'pys_event_referrer';
134    function writeEventReferrerCookie() {
135        if (!pageReferrer) {
136            return;
137        }
138        if (!options || !options.cookie || options.cookie.disabled_all_cookie) {
139            return;
140        }
141        try {
142            Cookies.set(PYS_EVENT_REFERRER_KEY, pageReferrer, { path: '/', domain: domain });
143        } catch (e) {}
144    }
145
146    /**
147     * Resolve parameter value based on mode (static or dynamic)
148     *
149     * @param {Object|string} param - Parameter object with { value, selector } or string value
150     * @returns {string|null} - Resolved value
151     */
152    function resolveParamValue(param, key = '') {
153
154        // 1️⃣ null / undefined - skip this parameter
155        if (param === null || param === undefined) {
156            return null;
157        }
158
159        // 2️⃣ primitive
160        if (typeof param !== 'object') {
161            return param;
162        }
163
164        // 3️⃣ array
165        if (Array.isArray(param)) {
166            return param.map(item => resolveParamValue(item, key));
167        }
168
169        // 4️⃣ NEW FORMAT OBJECT (config-object)
170        const isConfigObject =
171            ('value' in param || 'selector' in param || 'dynamic' in param || 'input_type' in param) &&
172            Object.keys(param).every(k =>
173                ['value','selector','dynamic','input_type','name'].includes(k)
174            );
175
176        if (isConfigObject) {
177
178            // STATIC MODE
179            const isStatic =
180                !param.dynamic ||
181                !param.selector ||
182                param.selector.trim() === '';
183
184            if (isStatic) {
185                let value = param.value ?? null;
186
187                if (param.input_type === "float" || param.input_type === "int") {
188                    value = extractNumericValue(
189                        value,
190                        param.input_type === "int"
191                    );
192                }
193
194                return value;
195            }
196
197            // DYNAMIC MODE
198            try {
199                const el = document.querySelector(param.selector);
200                if (!el) return null;
201
202                let value =
203                    el.value ??
204                    el.innerText ??
205                    el.textContent ??
206                    el.getAttribute("content") ??
207                    el.getAttribute("data-value") ??
208                    null;
209
210                if (param.input_type === "float" || param.input_type === "int") {
211                    value = extractNumericValue(
212                        value,
213                        param.input_type === "int"
214                    );
215                }
216
217                return value;
218
219            } catch {
220                return null;
221            }
222        }
223
224        // 5️⃣ Plain object (recursive traversal) - filter out null/undefined values
225        return Object.fromEntries(
226            Object.entries(param)
227                .map(([k, v]) => [k, resolveParamValue(v, k)])
228                .filter(([k, v]) => v !== null && v !== undefined)
229        );
230    }
231
232    function extractNumericValue(value, isInt=false) {
233        if (value === null || value === undefined) {
234            return null;
235        }
236        if (isInt) {
237            return parseInt(value);
238        }
239        return parseFloat(value);
240    }
241
242    var loadTags = [];
243
244    var dummyPinterest = function () {
245
246        /**
247         * Public API
248         */
249        return {
250            tag: function() {
251                return "pinterest";
252            },
253            isEnabled: function () {},
254
255            disable: function () {},
256
257            loadPixel: function () {},
258
259            fireEvent: function (name, data) {
260                return false;
261            },
262
263            onAdSenseEvent: function (event) {},
264
265            onClickEvent: function (params) {},
266
267            onWatchVideo: function (params) {},
268
269            onCommentEvent: function (event) {},
270
271            onFormEvent: function (params) {},
272
273            onDownloadEvent: function (params) {},
274
275
276
277            onWooAddToCartOnButtonEvent: function (product_id) {},
278
279            onWooAddToCartOnSingleEvent: function (product_id, qty, product_type, is_external, $form) {},
280
281            onWooRemoveFromCartEvent: function (cart_item_hash) {},
282
283            onWooAffiliateEvent: function (product_id) {},
284
285            onWooPayPalEvent: function (event) {},
286
287            onEddAddToCartOnButtonEvent: function (download_id, price_index, qty) {},
288
289            onEddRemoveFromCartEvent: function (item) {},
290
291            onPageScroll: function (event) {},
292            onTime: function (event) {
293
294            },
295
296        }
297
298    }();
299
300    var dummyBing = function () {
301
302        /**
303         * Public API
304         */
305        return {
306            tag: function() {
307                return "bing";
308            },
309            isEnabled: function () {},
310
311            disable: function () {},
312
313            loadPixel: function () {},
314
315            fireEvent: function (name, data) {
316                return false;
317            },
318
319            onAdSenseEvent: function (event) {},
320
321            onClickEvent: function (params) {},
322
323            onWatchVideo: function (params) {},
324
325            onCommentEvent: function (event) {},
326
327            onFormEvent: function (params) {},
328
329            onDownloadEvent: function (params) {},
330
331
332            onWooAddToCartOnButtonEvent: function (product_id) {},
333
334            onWooAddToCartOnSingleEvent: function (product_id, qty, product_type, is_external, $form) {},
335
336            onWooRemoveFromCartEvent: function (cart_item_hash) {},
337
338            onWooAffiliateEvent: function (product_id) {},
339
340            onWooPayPalEvent: function (event) {},
341
342            onEddAddToCartOnButtonEvent: function (download_id, price_index, qty) {},
343
344            onEddRemoveFromCartEvent: function (item) {},
345            onPageScroll: function (event) {},
346            onTime: function (event) {
347
348            },
349        }
350
351    }();
352
353    var dummyReddit = function () {
354        /**
355         * Public API
356         */
357        return {
358            tag: function () {
359                return "reddit";
360            },
361            isEnabled: function () {
362            },
363
364            disable: function () {
365            },
366
367            loadPixel: function () {
368            },
369
370            fireEvent: function ( name, data ) {
371                return false;
372            },
373
374            onAdSenseEvent: function ( event ) {
375            },
376
377            onClickEvent: function ( params ) {
378            },
379
380            onWatchVideo: function ( params ) {
381            },
382
383            onCommentEvent: function ( event ) {
384            },
385
386            onFormEvent: function ( params ) {
387            },
388
389            onDownloadEvent: function ( params ) {
390            },
391
392            onWooAddToCartOnButtonEvent: function ( product_id ) {
393            },
394
395            onWooAddToCartOnSingleEvent: function ( product_id, qty, product_type, is_external, $form ) {
396            },
397
398            onWooRemoveFromCartEvent: function ( cart_item_hash ) {
399            },
400
401            onWooAffiliateEvent: function ( product_id ) {
402            },
403
404            onWooPayPalEvent: function ( event ) {
405            },
406
407            onEddAddToCartOnButtonEvent: function ( download_id, price_index, qty ) {
408            },
409
410            onEddRemoveFromCartEvent: function ( item ) {
411            },
412
413            onPageScroll: function ( event ) {
414            },
415
416            onTime: function ( event ) {
417            },
418        }
419    }();
420
421    var Utils = function (options) {
422
423
424        var Pinterest = dummyPinterest;
425
426        var Bing = dummyBing;
427
428	    var Reddit = dummyReddit;
429
430        var gtag_loaded = false;
431
432        var gtm_loaded = false;
433
434        let isNewSession = checkSession();
435
436        let dataLayerName = 'dataLayerPYS';
437
438        let GTMdataLayerName = 'dataLayer';
439
440
441        function loadPixels() {
442
443            if (!options.gdpr.all_disabled_by_api) {
444                if (!options.gdpr.tiktok_disabled_by_api) {
445                    TikTok.loadPixel();
446                }
447
448                if (!options.gdpr.facebook_disabled_by_api) {
449                    Facebook.loadPixel();
450                }
451
452                if (!options.gdpr.analytics_disabled_by_api) {
453                    Analytics.loadPixel();
454                }
455
456                if (!options.gdpr.google_ads_disabled_by_api) {
457                    GAds.loadPixel();
458                }
459
460                if (!options.gdpr.analytics_disabled_by_api) {
461                    GTM.loadPixel();
462                }
463
464                if (!options.gdpr.pinterest_disabled_by_api) {
465                    Pinterest.loadPixel();
466                }
467
468                if (!options.gdpr.bing_disabled_by_api) {
469                    Bing.loadPixel();
470                }
471
472                if (!options.gdpr.reddit_disabled_by_api) {
473                    Reddit.loadPixel();
474                }
475
476            }
477            if (options.gdpr.consent_magic_integration_enabled && typeof CS_Data !== "undefined") {
478                if (typeof CS_Data.cs_google_analytics_consent_mode !== "undefined" && CS_Data.cs_google_analytics_consent_mode == 1) {
479                    Analytics.loadPixel();
480                }
481                if (typeof CS_Data.cs_google_ads_consent_mode !== "undefined" && CS_Data.cs_google_ads_consent_mode == 1 ) {
482                    GAds.loadPixel();
483                }
484            }
485        }
486
487        /**
488         * WATCHVIDEO UTILS
489         */
490
491        function isJSApiAttrEnabled(url) {
492            return url.indexOf('enablejsapi') > -1;
493        }
494
495        function isOriginAttrEnabled(url) {
496            return url.indexOf('origin') > -1;
497        }
498
499        // Returns key/value pairs of percentages: number of seconds to achieve
500        function getVideoCompletionMarks(duration) {
501
502            var marks = {};
503            var points = [0, 10, 50, 90, 100];
504
505            for (var i = 0; i < points.length; i++) {
506
507                var _point = points[i];
508                var _mark = _point + '%';
509                var _time = duration * _point / 100;
510
511                if (_point === 100) {
512                    _time = _time - 1;
513                }
514
515                // 10% => 123
516                marks[_mark] = Math.floor(_time);
517
518            }
519
520            return marks;
521
522        }
523
524        // Determine if the element is a YouTube video or not
525        function tagIsYouTubeVideo(tag) {
526            var src = tag.src || '';
527            return src.indexOf('youtube.com/embed/') > -1 || src.indexOf('youtube.com/v/') > -1;
528        }
529
530        function tagIsYouTubeAsyncVideo(tag) {
531            if(tag.src && tag.src.indexOf("data:image") !== -1) return false; // video is loaded
532            var keys = Object.keys(tag.dataset);
533            for(var i = 0;i<keys.length;i++) {
534                if(keys[i].toLowerCase().indexOf("src") > -1) {
535                    var src = tag.dataset[keys[i]];
536                    return src.indexOf('youtube.com/embed/') > -1 || src.indexOf('youtube.com/v/') > -1;
537                }
538            }
539            return false; //not find src
540        }
541
542        // Turn embed objects into iframe objects and ensure they have the right parameters
543        function normalizeYouTubeIframe(tag) {
544
545            var loc = window.location;
546            var a = document.createElement('a');
547            a.href = tag.src;
548            a.hostname = 'www.youtube.com';
549            a.protocol = loc.protocol;
550            var tmpPathname = a.pathname.charAt(0) === '/' ? a.pathname : '/' + a.pathname; // IE10 shim
551
552            if (!isJSApiAttrEnabled(a.search)) {
553                a.search = (a.search.length > 0 ? a.search + '&' : '') + 'enablejsapi=1';
554            }
555
556            // for security reasons, YouTube wants an origin parameter set that matches our hostname
557            if (!isOriginAttrEnabled(a.search) && loc.hostname.indexOf('localhost') === -1) {
558
559                var port = loc.port ? ':' + loc.port : '';
560                var origin = loc.protocol + '%2F%2F' + loc.hostname + port;
561
562                a.search = a.search + '&origin=' + origin;
563
564            }
565
566            if (tag.type === 'application/x-shockwave-flash') {
567
568                var newIframe = document.createElement('iframe');
569                newIframe.height = tag.height;
570                newIframe.width = tag.width;
571                tmpPathname = tmpPathname.replace('/v/', '/embed/');
572
573                tag.parentNode.parentNode.replaceChild(newIframe, tag.parentNode);
574
575                tag = newIframe;
576
577            }
578
579            a.pathname = tmpPathname;
580
581            if (tag.src !== a.href + a.hash) {
582                tag.src = a.href + a.hash;
583            }
584
585            return tag;
586
587        }
588
589        // Add event handlers for events emitted by the YouTube API
590        function addYouTubeEvents(iframe) {
591
592            var player = YT.get(iframe.id);
593            if (!player) {
594                player = new YT.Player(iframe, {});
595            }
596
597            if (typeof iframe.pauseFlag === 'undefined') {
598
599                iframe.pauseFlag = false;
600                player.addEventListener('onStateChange', function (evt) {
601                    onYouTubePlayerStateChange(evt, iframe);
602                });
603
604                player.addEventListener('onPlaybackRateChange', function(evt) {
605                    var currRate = evt.data;
606                    var prevRate = iframe._pysSpeedRate !== undefined ? iframe._pysSpeedRate : 1;
607                    iframe._pysSpeedRate = currRate;
608                    if(currRate == prevRate) return;
609                    if (options.triggerEventTypes && options.triggerEventTypes.video_speed) {
610                        var href = window.location.href;
611                        var ytVideoId = '';
612                        try { ytVideoId = player.getVideoData().video_id || ''; } catch(e) {}
613                        Object.keys(options.triggerEventTypes.video_speed).forEach(function(evtId) {
614                            var evtConfig   = options.triggerEventTypes.video_speed[evtId];
615                            var urls        = Array.isArray(evtConfig) ? evtConfig : (evtConfig.urls || []);
616                            var speedRate   = Array.isArray(evtConfig) ? 'any' : (evtConfig.speed_rate || 'any');
617                            var evtTriggers = Array.isArray(evtConfig) ? [] : (evtConfig.triggers || []);
618                            var pageOk      = !urls || urls.length === 0 || urls.some(function(u) { return u === '*' || u === '' || href.indexOf(u) !== -1; });
619                            var rateOk      = speedRate === 'any' ? currRate !== prevRate : (speedRate === 'increase' ? currRate > prevRate : currRate < prevRate);
620                            var videoOk     = evtTriggers.length === 0 || evtTriggers.some(function(t) { return t.type === 'youtube' && t.rule === ytVideoId; });
621                            if (pageOk && rateOk && videoOk) {
622                                Utils.handleTriggerFired(evtId, 'video_speed');
623                            }
624                        });
625                    }
626                    if (options.dynamicEvents && options.dynamicEvents.automatic_event_video_speed) {
627                        var ytSpeedUrl = iframe.src || '';
628                        var ytSpeedTitle = '';
629                        try { ytSpeedTitle = player.getVideoData().title || ''; } catch(e) {}
630                        var params = {
631                            video_url: ytSpeedUrl,
632                            video_type: 'youtube',
633                            video_title: ytSpeedTitle || 'Unknown Title',
634                            playback_speed: currRate,
635                            element: 'iframe'
636                        };
637                        Object.keys(options.dynamicEvents.automatic_event_video_speed).forEach(function(slug) {
638                            var pysEvent = Utils.clone(options.dynamicEvents.automatic_event_video_speed[slug]);
639                            Utils.copyProperties(params, pysEvent.params);
640                            var px = getPixelBySlag(slug); if (px) px.fireEvent(pysEvent.name, pysEvent);
641                        });
642                    }
643                });
644
645            }
646
647        }
648
649        function addDynYouTubeVideos(el) {
650            // We only bind to iFrames with a YouTube URL with the enablejsapi=1 and
651            // origin=<<hostname>> parameters
652            if (el.tagName === 'IFRAME'
653                && tagIsYouTubeVideo(el)
654                && isJSApiAttrEnabled(el.src)
655                && isOriginAttrEnabled(el.src))
656            {
657                addYouTubeEvents(el);
658            }
659        }
660
661        // Event handler for events emitted from the YouTube API
662        function onYouTubePlayerStateChange(evt, iframe) {
663
664            var stateIndex = evt.data;
665            var player = evt.target;
666            var targetVideoUrl = player.getVideoUrl();
667            var targetVideoId = targetVideoUrl.match(/[?&]v=([^&#]*)/)[1]; // Extract the ID
668            var playerState = player.getPlayerState();
669            var marks = getVideoCompletionMarks(player.getDuration());
670
671            iframe.playTracker = iframe.playTracker || {};
672
673            if (playerState === YT.PlayerState.PLAYING && !iframe.timer) {
674
675                clearInterval(iframe.timer);
676
677                // check every second to see if we've hit any of our percentage viewed marks
678                iframe.timer = setInterval(function () {
679                    checkYouTubeCompletion(player, marks, iframe.videoId);
680                }, 1000);
681
682            } else {
683
684                clearInterval(iframe.timer);
685                iframe.timer = false;
686
687            }
688
689            // playlist edge-case handler
690            if (stateIndex === YT.PlayerState.PLAYING) {
691                iframe.playTracker[targetVideoId] = true;
692                iframe.videoId = targetVideoId;
693                iframe.pauseFlag = false;
694            }
695
696            if (!iframe.playTracker[iframe.videoId]) {
697                return false; // this video hasn't started yet, so this is spam
698            }
699
700            if (stateIndex === YT.PlayerState.PAUSED) {
701
702                if (!iframe.pauseFlag) {
703                    iframe.pauseFlag = true;
704                } else {
705                    return false; // we don't want to fire consecutive pause events
706                }
707
708            }
709
710        }
711
712        // Trigger event if YouTube video mark was reached
713        function checkYouTubeCompletion(player, marks, videoId) {
714
715            var currentTime = player.getCurrentTime();
716
717            player[videoId] = player[videoId] || {};
718
719            for (var key in marks) {
720
721                if (marks[key] <= currentTime && !player[videoId][key]) {
722                    player[videoId][key] = true;
723
724                    var data = player.getVideoData();
725
726                    if (key === '0%') {
727                        key = 'play';
728                    }
729
730                    var params = {
731                        video_type: 'youtube',
732                        video_id: videoId,
733                        video_title: data.title,
734                    };
735
736                    let disable_watch_video = [];
737
738                    //Custom
739                    if ( options.triggerEventTypes.hasOwnProperty( "video_view" ) ) {
740                        Object.entries( options.triggerEventTypes.video_view ).forEach( function ( [ trigger_id, triggers ] ) {
741                            triggers.forEach( function ( trigger ) {
742                                if ( trigger.type === 'youtube' && trigger.rule === videoId ) {
743
744                                    let pixels = Object.keys( options.triggerEvents[ trigger_id ] );
745                                    for ( let i = 0; i < pixels.length; i++ ) {
746                                        let event = Utils.clone( options.triggerEvents[ trigger_id ][ pixels[ i ] ] );
747
748                                        if ( trigger.disable_watch_video ) {
749                                            disable_watch_video.push( pixels[ i ] );
750                                        }
751
752                                        if ( currentTime >= marks[ trigger.value ] ) {
753                                            if (pixels[i] !== 'reddit') {
754                                                event.params["progress"] = key;
755                                                Utils.copyProperties( params, event.params );
756                                            }
757
758                                            if ( event.fired !== true ) {
759                                                if ( Utils.isEventInTimeWindow( event.name, event, 'dyn_' + pixels[ i ] + '_' + trigger_id ) ) {
760                                                    event = Utils.getFormFilledData( event );
761                                                    getPixelBySlag( pixels[ i ] ).fireEvent( event.name, event );
762                                                    options.triggerEvents[ trigger_id ][ pixels[ i ] ].fired = true;
763                                                }
764                                            }
765                                        }
766                                    }
767                                }
768                            } )
769                        } )
770                    }
771
772                    // Auto
773                    if( options.automatic.enable_video
774                        && options.automatic.enable_youtube
775                        && options.dynamicEvents.hasOwnProperty("automatic_event_video")
776                    ) {
777                        var pixels = Object.keys(options.dynamicEvents.automatic_event_video);
778
779                        for (var i = 0; i < pixels.length; i++) {
780                            if ( disable_watch_video.includes( pixels[ i ] ) ) {
781                                continue;
782                            }
783                            var event = Utils.clone(options.dynamicEvents.automatic_event_video[pixels[i]]);
784
785                            if ( pixels[ i ] !== 'reddit' ) {
786                                event.params[ "progress" ] = key;
787                                Utils.copyProperties(params, event.params)
788                            }
789
790                            if ( pixels[i] === 'tiktok' || pixels[i] === 'reddit' ) {
791                                var time_trigger = event.automatic_event_video_trigger;
792                                if ( currentTime >= marks[time_trigger] && event.fired !== true ) {
793                                    getPixelBySlag(pixels[i]).onWatchVideo(event);
794
795                                    //tiktok and reddit watch video fire once
796                                    options.dynamicEvents.automatic_event_video[pixels[i]].fired = true;
797                                }
798                            } else {
799                                Utils.copyProperties(Utils.getRequestParam
799s(), event.params);
800                                getPixelBySlag(pixels[i]).onWatchVideo(event);
801                            }
802                        }
803                    }
804
805                    if(key == "play") {
806
807                        $.each(options.triggerEventTypes, function (triggerType, events) {
808                            $.each(events, function (eventId, triggers) {
809                                switch (triggerType) {
810                                    case 'video_play':
811                                        Utils.handleTriggerFired(eventId, triggerType);
812                                        break;
813                                }
814
815                            });
816                        });
817                    }
818
819                }
820
821            }
822
823        }
824
825        // Determine if the element is a Vimeo video or not
826        function tagIsVimeoVideo(tag) {
827            var src = tag.src || '';
828            return src.indexOf('player.vimeo.com/video/') > -1;
829        }
830
831        function tagIsAsincVimeoVideo(tag) {
832            if(tag.src) return false; // video is loaded
833            var keys = Object.keys(tag.dataset);
834            for(var i = 0;i<keys.length;i++) {
835                if(keys[i].toLowerCase().indexOf("src") > -1) {
836                    var src = tag.dataset[keys[i]];
837                    return src.indexOf('player.vimeo.com/video/') > -1;
838                }
839            }
840            return false; //not find src
841        }
842
843        function attachVimeoPlayerToTag(tag) {
844            var player = new Vimeo.Player(tag);
845
846            player.getDuration().then(function (pl,seconds) {
847                pl.pysMarks = getVideoCompletionMarks(seconds);
848            }.bind(null,player));
849
850            player.getVideoTitle().then(function (pl,title) {
851                pl.pysVideoTitle = title;
852            }.bind(null,player));
853
854            player.getVideoId().then(function (pl,id) {
855                pl.pysVideoId = id;
856            }.bind(null,player));
857
858            player.pysCompletedMarks = {};
859
860            player.on('play', function () {
861
862                if (this.pysTimer) {
863                    return;
864                }
865
866                clearInterval(this.pysTimer);
867
868                var player = this;
869
870                this.pysTimer = setInterval(function () {
871                    checkVimeoCompletion(player);
872                }, 1000);
873
874            });
875
876            player.on('pause', function () {
877                clearInterval(this.pysTimer);
878                this.pysTimer = false;
879            });
880
881            player.on('ended', function () {
882                clearInterval(this.pysTimer);
883                this.pysTimer = false;
884            });
885
886            player.on('playbackratechange', function(data) {
887                var currRate = data.playbackRate;
888                var prevRate = player._pysSpeedRate !== undefined ? player._pysSpeedRate : 1;
889                player._pysSpeedRate = currRate;
890                if(currRate == prevRate) return;
891                if (options.triggerEventTypes && options.triggerEventTypes.video_speed) {
892                    var href = window.location.href;
893                    var vmVideoId = player.pysVideoId ? String(player.pysVideoId) : '';
894                    Object.keys(options.triggerEventTypes.video_speed).forEach(function(evtId) {
895                        var evtConfig   = options.triggerEventTypes.video_speed[evtId];
896                        var urls        = Array.isArray(evtConfig) ? evtConfig : (evtConfig.urls || []);
897                        var speedRate   = Array.isArray(evtConfig) ? 'any' : (evtConfig.speed_rate || 'any');
898                        var evtTriggers = Array.isArray(evtConfig) ? [] : (evtConfig.triggers || []);
899                        var pageOk      = !urls || urls.length === 0 || urls.some(function(u) { return u === '*' || u === '' || href.indexOf(u) !== -1; });
900                        var rateOk      = speedRate === 'any' ? currRate !== prevRate : (speedRate === 'increase' ? currRate > prevRate : currRate < prevRate);
901                        var videoOk     = evtTriggers.length === 0 || evtTriggers.some(function(t) { return t.type === 'vimeo' && t.rule == vmVideoId; });
902                        if (pageOk && rateOk && videoOk) {
903                            Utils.handleTriggerFired(evtId, 'video_speed');
904                        }
905                    });
906                }
907                if (options.dynamicEvents && options.dynamicEvents.automatic_event_video_speed) {
908                    var vimeoEl = player.element || null;
909                    var params = {
910                        video_url: vimeoEl ? (vimeoEl.src || '') : '',
911                        video_type: 'vimeo',
912                        video_title: player.pysVideoTitle || 'Unknown Title',
913                        playback_speed: currRate,
914                        element: 'iframe'
915                    };
916                    Object.keys(options.dynamicEvents.automatic_event_video_speed).forEac
916h(function(slug) {
917                        var pysEvent = Utils.clone(options.dynamicEvents.automatic_event_video_speed[slug]);
918                        Utils.copyProperties(params, pysEvent.params);
919                        var px = getPixelBySlag(slug); if (px) px.fireEvent(pysEvent.name, pysEvent);
920                    });
921                }
922            });
923        }
924
925        // Trigger event if Vimeo video mark was reached
926        function checkVimeoCompletion(player) {
927
928            player.getCurrentTime().then(function (seconds) {
929
930                for (var key in player.pysMarks) {
931
932                    if (player.pysMarks[key] <= seconds && !player.pysCompletedMarks[key]) {
933
934                        player.pysCompletedMarks[key] = true;
935
936                        if (key === '0%') {
937                            key = 'play';
938                        }
939
940                        var params = {
941                            video_type: 'vimeo',
942                            video_id: player.pysVideoId,
943                            video_title: player.pysVideoTitle,
944                        };
945
946                        let disable_watch_video = [];
947
948                        //Custom
949                        if ( options.triggerEventTypes.hasOwnProperty( "video_view" ) ) {
950                            Object.entries( options.triggerEventTypes.video_view ).forEach( function ( [ trigger_id, triggers ] ) {
951                                triggers.forEach( function ( trigger ) {
952                                    if ( trigger.type === 'vimeo' && trigger.rule == player.pysVideoId ) {
953                                        let pixels = Object.keys( options.triggerEvents[ trigger_id ] );
954                                        for ( let i = 0; i < pixels.length; i++ ) {
955                                            let event = Utils.clone( options.triggerEvents[ trigger_id ][ pixels[ i ] ] );
956
957                                            if ( trigger.disable_watch_video ) {
958                                                disable_watch_video.push( pixels[ i ] );
959                                            }
960
961                                            if ( seconds >= player.pysMarks[ trigger.value ] ) {
962                                                if ( pixels[ i ] !== 'reddit' ) {
963                                                    event.params[ "progress" ] = key;
964                                                    Utils.copyProperties( params, event.params );
965                                                }
966
967                                                if ( event.fired !== true ) {
968                                                    if ( Utils.isEventInTimeWindow( event.name, event, 'dyn_' + pixels[ i ] + '_' + trigger_id ) ) {
969                                                        event = Utils.getFormFilledData( event );
970                                                        getPixelBySlag( pixels[ i ] ).fireEvent( event.name, event );
971                                                        options.triggerEvents[ trigger_id ][ pixels[ i ] ].fired = true;
972                                                    }
973                                                }
974                                            }
975                                        }
976                                    }
977                                } )
978                            } )
979                        }
980
981                        // Auto
982                        if(options.automatic.enable_video
983                            && options.automatic.enable_vimeo
984                            && options.dynamicEvents.hasOwnProperty("automatic_event_video")
985                        ) {
986                            var pixels = Object.keys(options.dynamicEvents.automatic_event_video);
987
988                            for (var i = 0; i < pixels.length; i++) {
989                                if ( disable_watch_video.includes( pixels[ i ] ) ) {
990                                    continue;
991                                }
992
993                                var event = Utils.clone(options.dynamicEvents.automatic_event_video[pixels[i]]);
994
995                                if ( pixels[i] !== 'reddit' ) {
996                                    event.params["progress"] = key;
997                                    Utils.copyProperties(params, event.params);
998                                }
999
1000                                if ( pixels[i] === 'tiktok' || pixels[i] === 'reddit' ) {
1001                                    var time_trigger = event.automatic_event_video_trigger;
1002                                    if ( seconds >= player.pysMarks[time_trigger] && event.fired !== true ) {
1003                                        getPixelBySlag(pixels[i]).onWatchVideo(event);
1004
1005                                        //tiktok and reddit watch video fire once
1006                                        options.dynamicEvents.automatic_event_video[pixels[i]].fired = true;
1007                                    }
1008                                } else {
1009                                    Utils.copyProperties(Utils.getRequestParam
1009s(), event.params);
1010                                    getPixelBySlag(pixels[i]).onWatchVideo(event);
1011                                }
1012                            }
1013                        }
1014
1015                        if(key == "play") {
1016                            $.each(options.triggerEventTypes, function (triggerType, events) {
1017                                $.each(events, function (eventId, triggers) {
1018                                    switch (triggerType) {
1019                                        case 'video_play':
1020                                            Utils.handleTriggerFired(eventId, triggerType);
1021                                            break;
1022                                    }
1023
1024                                });
1025                            });
1026                        }
1027                    }
1028
1029                }
1030
1031            });
1032
1033        }
1034
1035        // ── HTML5 <video> WatchVideo tracking ───────────────────────────────────
1036        function initHtml5VideoTracking() {
1037
1038            if ( !options.automatic.enable_video
1039                || !options.dynamicEvents.hasOwnProperty('automatic_event_video')
1040            ) {
1041                return;
1042            }
1043
1044            // play event — fires once per element when playback starts
1045            document.addEventListener('play', function (e) {
1046                var el = e.target;
1047                if ( !el || el.tagName !== 'VIDEO' ) return;
1048
1049                el.pysCompletedMarks = el.pysCompletedMarks || {};
1050                if ( el.pysCompletedMarks['play'] ) return;
1051
1052                var url = el.src || el.currentSrc || '';
1053                el.pysCompletedMarks['play'] = true;
1054
1055                fireHtml5VideoMilestone( el, url, 'play' );
1056
1057            }, true );
1058
1059            // timeupdate — check percentage milestones
1060            document.addEventListener('timeupdate', function (e) {
1061                var el = e.target;
1062                if ( !el || el.tagName !== 'VIDEO' ) return;
1063
1064                var duration = el.duration;
1065                if ( !duration || isNaN(duration) || !isFinite(duration) ) return;
1066
1067                var currentTime = el.currentTime;
1068                var marks = getVideoCompletionMarks( duration );
1069                var url   = el.src || el.currentSrc || '';
1070
1071                el.pysCompletedMarks = el.pysCompletedMarks || {};
1072
1073                for ( var key in marks ) {
1074                    if ( key === '0%' ) continue; // handled by 'play' listener
1075
1076                    if ( marks[ key ] <= currentTime && !el.pysCompletedMarks[ key ] ) {
1077                        el.pysCompletedMarks[ key ] = true;
1078                        fireHtml5VideoMilestone( el, url, key );
1079                    }
1080                }
1081
1082            }, true );
1083        }
1084
1085        function fireHtml5VideoMilestone( el, url, key ) {
1086
1087            var progressLabel = ( key === '0%' ) ? 'play' : key;
1088
1089            var params = {
1090                video_type:  'html5',
1091                video_id:    el.id || url,
1092                video_title: Utils.getHtml5VideoTitle( url ),
1093            };
1094
1095            var pixels = Object.keys( options.dynamicEvents.automatic_event_video );
1096
1097            for ( var i = 0; i < pixels.length; i++ ) {
1098                var event = Utils.clone( options.dynamicEvents.automatic_event_video[ pixels[ i ] ] );
1099
1100                if ( pixels[ i ] !== 'reddit' ) {
1101                    event.params[ 'progress' ] = progressLabel;
1102                    Utils.copyProperties( params, event.params );
1103                }
1104
1105                if ( pixels[ i ] === 'tiktok' || pixels[ i ] === 'reddit' ) {
1106                    var time_trigger = event.automatic_event_video_trigger;
1107                    var marks        = getVideoCompletionMarks( el.duration || 0 );
1108                    if ( el.currentTime >= marks[ time_trigger ] && event.fired !== true ) {
1109                        getPixelBySlag( pixels[ i ] ).onWatchVideo( event );
1110                        options.dynamicEvents.automatic_event_video[ pixels[ i ] ].fired = true;
1111                    }
1112                } else {
1113                    Utils.copyProperties( Utils.getRequestParams(), event.params );
1114                    getPixelBySlag( pixels[ i ] ).onWatchVideo( event );
1115                }
1116            }
1117
1118            // handle custom video_view triggers (mirrors YouTube/Vimeo pattern)
1119            if ( key === 'play' ) {
1120                $.each( options.triggerEventTypes, function ( triggerType, events ) {
1121                    $.each( events, function ( eventId ) {
1122                        if ( triggerType === 'video_play' ) {
1123                            Utils.handleTriggerFired( eventId, triggerType );
1124                        }
1125                    } );
1126                } );
1127            }
1128        }
1129
1130        /**
1131         * COOKIES UTILS
1132         */
1133
1134        var utmTerms = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content' ,'utm_term'];
1135        var utmId = ['fbadid', 'gadid', 'padid', 'bingid'];
1136        var requestParams = [];
1137
1138
1139        function getDomain(url) {
1140
1141            url = url.replace(/(https?:\/\/)?(www.)?/i, '');
1142
1143            if (url.indexOf('/') !== -1) {
1144                return url.split('/')[0];
1145            }
1146
1147            return url;
1148        }
1149
1150        function checkSession() {
1151
1152            if( Cookies.get('pys_start_session') === undefined ||
1153                Cookies.get('pys_session_limit') === undefined) {
1154                firstVisit = true;
1155                return true
1156            }
1157            return false
1158
1159        }
1160
1161        function getTrafficSource() {
1162
1163            try {
1164
1165                let referrer = stripUrlQuery(document.referrer.toString()),
1166                    source;
1167
1168                let direct = referrer.length === 0;
1169                let internal = direct ? false : referrer.indexOf(options.siteUrl) === 0;
1170                let external = !direct && !internal;
1171
1172                if (external === false) {
1173                    source = 'direct';
1174                } else {
1175                    source = referrer;
1176                }
1177
1178                if (source !== 'direct') {
1179                    // leave only domain (Issue #70)
1180                    return getDomain(source);
1181                } else {
1182                    return source;
1183                }
1184
1185            } catch (e) {
1186                console.error(e);
1187                return 'direct';
1188            }
1189
1190        }
1191
1192        /**
1193         * Return query variables object with where property name is query variable
1194         * and property value is query variable value.
1195         */
1196        function getQueryVars() {
1197
1198            try {
1199
1200                var result = {},
1201                    tmp = [];
1202
1203                window.location.search
1204                    .substr(1)
1205                    .split("&")
1206                    .forEach(function (item) {
1207
1208                        tmp = item.split('=');
1209
1210                        if (tmp.length > 1) {
1211                            result[tmp[0]] = tmp[1];
1212                        }
1213
1214                    });
1215
1216                return result;
1217
1218            } catch (e) {
1219                console.error(e);
1220                return {};
1221            }
1222
1223        }
1224
1225        function getLandingPageValue() {
1226            let name = "pys_landing_page"
1227            if(options.visit_data_model === "last_visit") {
1228                name = "last_pys_landing_page"
1229            }
1230            if(Cookies.get(name) && Cookies.get(name) !== "undefined") {
1231                return Cookies.get(name);
1232            }
1233            else if(options.hasOwnProperty("tracking_analytics") && options.tracking_analytics.TrafficLanding){
1234                return options.tracking_analytics.TrafficLanding;
1235            } else{
1236                return "";
1237            }
1238        }
1239        function getTrafficSourceValue() {
1240            let name = "pysTrafficSource"
1241            if(options.visit_data_model === "last_visit") {
1242                name = "last_pysTrafficSource"
1243            }
1244            if(Cookies.get(name) && Cookies.get(name) !== "undefined") {
1245                return Cookies.get(name);
1246            } else{
1247                return "";
1248            }
1249        }
1250
1251        /**
1252         * Handle alternative GCLID parameter for Safari compatibility
1253         * Creates _gcl_aw cookie when alternative parameter is detected and _gcl_aw doesn't exist
1254         */
1255        function handleAlternativeGclid() {
1256            // Check if alternative GCLID is enabled
1257            if (!options.GATags || !options.GATags.gclid_alternative_enabled || !options.GATags.gclid_alternative_param) {
1258                return;
1259            }
1260
1261            // GDPR consent checks - stop if any consent filter blocks it
1262            if (options.gdpr.all_disabled_by_api) {
1263                return;
1264            }
1265            if (options.gdpr.analytics_disabled_by_api) {
1266                return;
1267            }
1268            if (options.cookie.disabled_all_cookie) {
1269                return;
1270            }
1271            if (options.cookie.disabled_google_alternative_id) {
1272                return;
1273            }
1274
1275            const alternativeParam = options.GATags.gclid_alternative_param;
1276            const queryVars = getQueryVars();
1277
1278            // Check if alternative parameter exists in URL
1279            if (queryVars[alternativeParam]) {
1280                const gclidValue = queryVars[alternativeParam];
1281
1282                // Only create _gcl_aw cookie if it doesn't exist
1283                if (!Cookies.get('_gcl_aw')) {
1284                    // Create _gcl_aw cookie with the alternative parameter value in Google's format
1285                    // Format: GCL.{timestamp}.{gclid_value}
1286                    const timestamp = Math.floor(Date.now() / 1000);
1287                    const cookieValue = 'GCL.' + timestamp + '.' + gclidValue;
1288
1289                    // Set cookie for 90 days (Google's standard)
1290                    const expires = new Date();
1291                    expires.setTime(expires.getTime() + (90 * 24 * 60 * 60 * 1000));
1292
1293                    Cookies.set('_gcl_aw', cookieValue, {
1294                        expires: expires,
1295                        path: '/',
1296                        domain: domain
1297                    });
1298
1299                    if (options.debug) {
1300                        console.log('PYS: Created _gcl_aw cookie from alternative parameter:', alternativeParam, '=', gclidValue);
1301                    }
1302                }
1303            }
1304        }
1305
1306        function getUTMId(useLast = false) {
1307            try {
1308                let cookiePrefix = 'pys_'
1309                let terms = [];
1310                if (useLast) {
1311                    cookiePrefix = 'last_pys_'
1312                }
1313                $.each(utmId, function (index, name) {
1314                    if (Cookies.get(cookiePrefix + name)) {
1315                        terms[name] = Cookies.get(cookiePrefix + name)
1316                    }
1317                    else if(options.hasOwnProperty("tracking_analytics") && options.tracking_analytics.TrafficUtmsId[name]) {
1318                        terms[name] = filterEmails(options.tracking_analytics.TrafficUtmsId[name])
1319                    }
1320                });
1321                return terms;
1322            } catch (e) {
1323                console.error(e);
1324                return [];
1325            }
1326        }
1327        /**
1328         * Return UTM terms from request query variables or from cookies.
1329         */
1330        function getUTMs(useLast = false) {
1331
1332            try {
1333                let cookiePrefix = 'pys_'
1334                if(useLast) {
1335                    cookiePrefix = 'last_pys_'
1336                }
1337                let terms = [];
1338                $.each(utmTerms, function (index, name) {
1339                    if (Cookies.get(cookiePrefix + name)) {
1340                        let value = Cookies.get(cookiePrefix + name);
1341                        terms[name] = filterEmails(value); // do not allow email in request params (Issue #70)
1342                    }
1343                    else if(options.hasOwnProperty("tracking_analytics") && options.tracking_analytics.TrafficUtms[name]) {
1344                        terms[name] = filterEmails(options.tracking_analytics.TrafficUtms[name])
1345                    }
1346                });
1347
1348                return terms;
1349
1350            } catch (e) {
1351                console.error(e);
1352                return [];
1353            }
1354
1355        }
1356
1357        function getDateTime() {
1358            var dateTime = new Array();
1359            var date = new Date(),
1360                days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
1361                months = ['January', 'February', 'March', 'April', 'May', 'June',
1362                    'July', 'August', 'September', 'October', 'November', 'December'
1363                ],
1364                hours = ['00-01', '01-02', '02-03', '03-04', '04-05', '05-06', '06-07', '07-08',
1365                    '08-09', '09-10', '10-11', '11-12', '12-13', '13-14', '14-15', '15-16', '16-17',
1366                    '17-18', '18-19', '19-20', '20-21', '21-22', '22-23', '23-24'
1367                ];
1368            dateTime.push(hours[date.getHours()]);
1369            dateTime.push(days[date.getDay()]);
1370            dateTime.push(months[date.getMonth()]);
1371            return dateTime;
1372        }
1373
1374        function filterEmails(value) {
1375            return Utils.validateEmail(value) ? undefined : value;
1376        }
1377
1378        /**
1379         * PUBLIC API
1380         */
1381        return {
1382
1383            PRODUCT_SIMPLE : 0,
1384            PRODUCT_VARIABLE : 1,
1385            PRODUCT_BUNDLE : 2,
1386            PRODUCT_GROUPED : 3,
1387            utmTerms : utmTerms,
1388            utmId : utmId,
1389            isNewSession: checkSession(),
1390            setHidePixelCookie: function(){
1391                let refresh_after_consent = false;
1392                if ( !firstVisit && options.gdpr.consent_magic_integration_enabled && window.CS_Data !== undefined && window.CS_Data.cs_refresh_after_consent) {
1393                    refresh_after_consent = true;
1394                }
1395                if( !refresh_after_consent && !firstVisit) return false;
1396
1397                const url_parts = window.location.href;
1398                const url_params = new URLSearchParams(window.location.search);
1399                const matchingPixels = ["facebook", "ga", "gtm", "google_ads", "bing", "pinterest", "tiktok", "reddit"];
1400                $.each(matchingPixels, function (index, slug) {
1401                    var module = getPixelBySlag(slug);
1402                    if (module && module.isEnabled()) {
1403                        const fullUrl = window.location.href;
1404                        $.each(module.getHidePixel(), function (index, hide_info) {
1405                            for (const item of hide_info.hide_tag_contain) {
1406                                if (item && fullUrl.includes(item)) {
1407                                    let hideTagTimeInMilliseconds = hide_info.hide_tag_time * 60 * 60 * 1000;
1408                                    let expiresTime = new Date().getTime() + hideTagTimeInMilliseconds;
1409                                    Cookies.set('hide_tag_' + hide_info.pixel, true, { expires: new Date(expiresTime), path: '/' });
1410                                    break;
1411                                }
1412                            }
1413                        });
1414                    }
1415
1416                });
1417            },
1418            hideMatchingPixel: function(pixelValue, slug) {
1419                if(Cookies.get('hide_tag_'+pixelValue))
1420                {
1421                    return true;
1422                }
1423                return false;
1424            },
1425            /**
1426             * Whether a send_to value actually points at a destination.
1427             *
1428             * gtag('event', name, { send_to: [] }) has no destination, so gtag.js
1429             * parks the hit until a matching config command appears. Tag Assistant
1430             * reports that as "Some hits will only be sent after a config command
1431             * is issued via gtag('config')". Callers must skip the event instead.
1432             */
1433            hasSendToTarget: function (target) {
1434                if (Array.isArray(target)) {
1435                    return target.length > 0;
1436                }
1437                return typeof target === 'string' && target !== '';
1438            },
1439            validateEmail: function (email) {
1440                var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
1441                return re.test(email);
1442            },
1443            fireEventForAllPixel:function(functionName,events){
1444                if (events.hasOwnProperty(Facebook.tag()))
1445                    Facebook[functionName](events[Facebook.tag()]);
1446                if (events.hasOwnProperty(Analytics.tag()))
1447                    Analytics[functionName](events[Analytics.tag()]);
1448                if (events.hasOwnProperty(GAds.tag()))
1449                    GAds[functionName](events[GAds.tag()]);
1450                if (events.hasOwnProperty(Pinterest.tag()))
1451                    Pinterest[functionName](events[Pinterest.tag()]);
1452                if (events.hasOwnProperty(Bing.tag()))
1453                    Bing[functionName](events[Bing.tag()]);
1454                if (events.hasOwnProperty(TikTok.tag()))
1455                    TikTok[functionName](events[TikTok.tag()]);
1456                if (events.hasOwnProperty(Reddit.tag()))
1457                    Reddit[functionName](events[Reddit.tag()]);
1458
1459                if (events.hasOwnProperty(GTM.tag()))
1460                    GTM[functionName](events[GTM.tag()]);
1461            },
1462
1463            getQueryValue:function (name){
1464                return getQueryVars()[name];
1465            },
1466
1467            filterEmails: function (value) {
1468                return filterEmails(value);
1469            },
1470
1471            setupPinterestObject: function () {
1472                Pinterest = window.pys.Pinterest || Pinterest;
1473                return Pinterest;
1474            },
1475
1476            setupBingObject: function () {
1477                Bing = window.pys.Bing || Bing;
1478                return Bing;
1479            },
1480
1481            setupRedditObject: function () {
1482                Reddit = window.pys.Reddit || Reddit;
1483                return Reddit;
1484            },
1485
1486            // Clone all object members to another and return it
1487            copyProperties: function (from, to) {
1488                for (var key in from) {
1489                    if("function" == typeof from[key]) {
1490                        continue;
1491                    }
1492                    to[key] = from[key];
1493                }
1494                return to;
1495            },
1496
1497            getVideoTypeFromUrl: function (url) {
1498                if (!url) return 'unknown';
1499                if (url.indexOf('youtube.com') !== -1 || url.indexOf('youtu.be') !== -1) return 'youtube';
1500                if (url.indexOf('vimeo.com') !== -1) return 'vimeo';
1501                return 'html5';
1502            },
1503
1504            getHtml5VideoTitle: function (url) {
1505                if (!url) return 'Unknown Title';
1506                var path = url.split('?')[0].split('#')[0];
1507                var filename = path.split('/').pop();
1508                if (!filename) return 'Unknown Title';
1509                var ext = filename.lastIndexOf('.');
1510                return ext > 0 ? filename.slice(0, ext) : filename;
1511            },
1512
1513            /**
1514             * Generate unique ID
1515             *
1516             * For purchase events (woo_purchase, edd_purchase):
1517             * - Server eventID has priority (for server-browser deduplication)
1518             * - If server didn't provide eventID, generate one keyed by order_id
1519             *
1520             * For other events:
1521             * - Use existing logic with event type as key
1522             */
1523            generateUniqueId : function (event) {
1524                var eventsWithHook = [
1525                    'woo_purchase',
1526                    'woo_start_trial',
1527                    'woo_subscription_created',
1528                    'woo_subscription_renewal',
1529                    'edd_purchase',
1530                    'edd_start_trial',
1531                    'edd_subscription_created',
1532                    'edd_subscription_renewal',
1533                    'edd_license_created',
1534                    'edd_license_upgrade',
1535                    'edd_license_renewal'
1536                ];
1537
1538                // For events with server hooks: prioritize server eventID
1539                if ( eventsWithHook.includes( event.e_id ) ) {
1540                    // If server provided eventID, use it (for deduplication)
1541                    if ( event.eventID && event.eventID.length > 0 ) {
1542                        var orderKey = event.e_id;
1543                        if ( !uniqueId.hasOwnProperty( orderKey ) ) {
1544                            uniqueId[orderKey] = event.eventID;
1545                        }
1546                        return uniqueId[orderKey];
1547                    }
1548                }
1549
1550                if (
1551                    event.eventID.length == 0 ||
1552                    ( !event.isVariationTrigger &&
1553                        ( event.type == "static" && options.ajaxForServerStaticEvent ||
1554                            event.type !== "static" && options.ajaxForServerEvent )
1555                    )
1556                ) {
1557                    let idKey = event.hasOwnProperty('custom_event_post_id') ? event.custom_event_post_id : event.e_id;
1558                    if ( !uniqueId.hasOwnProperty( idKey ) ) {
1559                        uniqueId[idKey] = pys_generate_token();
1560                    }
1561                    return uniqueId[idKey];
1562                }
1563                else if ( event.eventID.length !== 0 ) {
1564                    return event.eventID;
1565                }
1566            },
1567
1568            // Flatten object for sendBeacon compatibility
1569            flattenObject: function (obj, prefix = '', res = {}) {
1570                for (const [key, value] of Object.entries(obj)) {
1571                    const prefixedKey = prefix
1572                        ? (Array.isArray(obj) ? `${prefix}[${key}]` : `${prefix}[${key}]`)
1573                        : key;
1574                    if (value !== null && typeof value === 'object' && !(value instanceof File)) {
1575                        this.flattenObject(value, prefixedKey, res);
1576                    } else {
1577                        res[prefixedKey] = value;
1578                    }
1579                }
1580                return res;
1581            },
1582
1583            sendServerAjaxRequest: function ( url, data ) {
1584                // Check if we should use REST API for TikTok or Facebook
1585                if (data.action === 'pys_tiktok_api_event' && window.pysTikTokRest) {
1586                    // Use TikTok REST API
1587                    this.sendRestAPIRequest(data, 'tiktok');
1588                    return;
1589                }
1590
1591                if (data.action === 'pys_api_event' && data.pixel === 'facebook' && window.pysFacebookRest) {
1592                    // Use Facebook REST API
1593                    this.sendRestAPIRequest(data, 'facebook');
1594                    return;
1595                }
1596
1597                // Check if sendBeacon is enabled and supported
1598                if (options.useSendBeacon && navigator.sendBeacon) {
1599                    try {
1600                        // Flatten the data object for sendBeacon compatibility
1601                        const flattenedData = this.flattenObject(data);
1602                        const formData = new URLSearchParams();
1603
1604                        // Convert flattened data to URLSearchParams
1605                        for (const [key, value] of Object.entries(flattenedData)) {
1606                            if (value !== null && value !== undefined) {
1607                                formData.append(key, value);
1608                            }
1609                        }
1610
1611                        // Try to send using sendBeacon
1612                        const success = navigator.sendBeacon(url, formData);
1613                        if (success) {
1614                            return; // Successfully sent via sendBeacon
1615                        }
1616                    } catch (e) {
1617                        // If sendBeacon fails, fall back to jQuery.ajax
1618                        if (options.debug) {
1619                            console.log('PYS: sendBeacon failed, falling back to jQuery.ajax:', e);
1620                        }
1621                    }
1622                }
1623
1624                // Fallback to jQuery.ajax
1625                jQuery.ajax( {
1626                    type: 'POST',
1627                    url: url,
1628                    data: data,
1629                    headers: {
1630                        'Cache-Control': 'no-cache'
1631                    },
1632                    success: function () {
1633                    },
1634                } );
1635            },
1636
1637            // Send event via REST API (unified function for TikTok, Facebook, and other platforms)
1638            // Replaces the old sendTikTokRestAPIRequest and sendFacebookRestAPIRequest functions
1639            sendRestAPIRequest: function (data, platform) {
1640                let restApiUrl;
1641
1642                // Get platform-specific REST API configuration
1643                switch (platform) {
1644                    case 'tiktok':
1645                        restApiUrl = window.pysTikTokRest ? window.pysTikTokRest.restApiUrl : '/wp-json/pys-tiktok/v1/event';
1646                        break;
1647                    case 'facebook':
1648                        restApiUrl = window.pysFacebookRest ? window.pysFacebookRest.restApiUrl : '/wp-json/pys-facebook/v1/event';
1649                        break;
1650                    case 'pinterest':
1651                        restApiUrl = window.pysPinterestRest ? window.pysPinterestRest.restApiUrl : '/wp-json/pys-pinterest/v1/event';
1652                        break;
1653                    default:
1654                        console.error('PYS: Unknown platform for REST API:', platform);
1655                        this.sendAjaxFallback(data);
1656                        return;
1657                }
1658
1659                // Prepare data for REST API
1660                const restApiData = {
1661                    event: data.event,
1662                    event_slug: data.event_slug,
1663                    data: JSON.stringify(data.data || {}),
1664                    ids: JSON.stringify(data.ids || []),
1665                    eventID: data.event_id || data.eventID || '',
1666                    woo_order: data.woo_order || '0',
1667                    edd_order: data.edd_order || '0',
1668                    order_key: data.order_key || ''
1669                };
1670
1671                // Facebook-only: forward the per-page referrer so the CAPI
1672                // helper can attach it to custom_data.referrer_url.
1673                if (platform === 'facebook') {
1674                    const forwardedReferrer = data.referrer_url || pageReferrer;
1675                    if (forwardedReferrer) {
1676                        restApiData.referrer_url = forwardedReferrer;
1677                    }
1678                }
1679
1680                // Variant A: send the actual page URL where the event happened
1681                // so the server sets event_source_url to this page, not the
1682                // REST endpoint. PHP applies enable_remove_source_url_params.
1683                restApiData.event_source_url = data.url || window.location.href;
1684
1685                // Try to send using sendBeacon first (if enabled)
1686                if (options.useSendBeacon && navigator.sendBeacon) {
1687                    try {
1688                        const formData = new URLSearchParams();
1689                        for (const [key, value] of Object.entries(restApiData)) {
1690                            formData.append(key, value);
1691                        }
1692
1693                        if (navigator.sendBeacon(restApiUrl, formData)) {
1694                            return;
1695                        }
1696                    } catch (e) {
1697                        // sendBeacon failed, continue to fetch
1698                    }
1699                }
1700
1701                // Try to send using fetch with REST API
1702                if (window.fetch) {
1703                    const headers = {
1704                        'Content-Type': 'application/json'
1705                    };
1706
1707                    fetch(restApiUrl, {
1708                        method: 'POST',
1709                        headers: headers,
1710                        body: JSON.stringify(restApiData)
1711                    })
1712                        .then(response => {
1713                            if (!response.ok) {
1714                                throw new Error(platform + ' REST API request failed: ' + response.status);
1715                            }
1716                        })
1717                        .catch(error => {
1718                            // Fallback to AJAX if REST API fails
1719                            if (options.debug) {
1720                                console.log('PYS: ' + platform + ' REST API failed, falling back to AJAX:', error);
1721                            }
1722                            this.sendAjaxFallback(data);
1723                        });
1724                } else {
1725                    // Fallback to AJAX if fetch is not supported
1726                    this.sendAjaxFallback(data);
1727                }
1728            },
1729
1730
1731
1732            // Fallback AJAX method
1733            sendAjaxFallback: function (data) {
1734                jQuery.ajax({
1735                    type: 'POST',
1736                    url: options.ajaxUrl,
1737                    data: data,
1738                    headers: {
1739                        'Cache-Control': 'no-cache'
1740                    },
1741                    success: function () {
1742                    },
1743                });
1744            },
1745
1746            clone: function(obj) {
1747                var copy;
1748
1749                // Handle the 3 simple types, and null or undefined
1750                if (null == obj || "object" != typeof obj) return obj;
1751
1752                // Handle Date
1753                if (obj instanceof Date) {
1754                    copy = new Date();
1755                    copy.setTime(obj.getTime());
1756                    return copy;
1757                }
1758
1759                // Handle Array
1760                if (obj instanceof Array) {
1761                    copy = [];
1762                    for (var i = 0, len = obj.length; i < len; i++) {
1763                        if("function" == typeof obj[i]) {
1764                            continue;
1765                        }
1766                        copy[i] = Utils.clone(obj[i]);
1767                    }
1768                    return copy;
1769                }
1770
1771                // Handle Object
1772                if (obj instanceof Object) {
1773                    copy = {};
1774                    for (var attr in obj) {
1775                        if (obj.hasOwnProperty(attr)) {
1776                            if("function" == typeof obj[attr]) {
1777                                continue;
1778                            }
1779                            copy[attr] = Utils.clone(obj[attr]);
1780                        }
1781                    }
1782                    return copy;
1783                }
1784
1785                return obj;
1786            },
1787
1788            // Returns array of elements with given tag name
1789            getTagsAsArray: function (tag) {
1790                return [].slice.call(document.getElementsByTagName(tag));
1791            },
1792
1793
1794            initializeVideoAPIs: function (options) {
1795                let trigger_has_video_YT, trigger_has_video_Vimeo = false;
1796                if (options.hasOwnProperty('automatic') && options.automatic.enable_video) {
1797                    if ( options.triggerEventTypes.hasOwnProperty( "video_view" ) ) {
1798                        Object.entries( options.triggerEventTypes.video_view ).forEach( function ( [ trigger_id, triggers ] ) {
1799                            triggers.forEach( function ( trigger ) {
1800                                if (trigger.type === 'youtube') {
1801                                    trigger_has_video_YT = true;
1802                                }
1803                                if(trigger.type === 'vimeo'){
1804                                    trigger_has_video_Vimeo = true;
1805                                }
1806                            });
1807                        });
1808                    }
1809                    if (
1810                        options.hasOwnProperty('enable_automatic_events') &&
1811                        options.enable_automatic_events
1812                    ) {
1813                        if (options.automatic.enable_youtube || trigger_has_video_YT) {
1814                            Utils.initYouTubeAPI();
1815                        }
1816                        if (options.automatic.enable_vimeo || trigger_has_video_Vimeo) {
1817                            Utils.initVimeoAPI();
1818                        }
1819                        initHtml5VideoTracking();
1820                    }
1821                }
1822
1823                // Also init YT/Vimeo APIs for video_speed custom triggers and global VideoSpeed event
1824                var has_video_speed_trigger = options.triggerEventTypes && options.triggerEventTypes.hasOwnProperty('video_speed') &&
1825                    Object.keys(options.triggerEventTypes.video_speed).length > 0;
1826                var has_global_video_speed = options.dynamicEvents && options.dynamicEvents.hasOwnProperty('automatic_event_video_speed');
1827                if (has_video_speed_trigger || has_global_video_speed) {
1828                    Utils.initYouTubeAPI();
1829                    Utils.initVimeoAPI();
1830                }
1831            },
1832            /**
1833             * Load and initialize YouTube API
1834             *
1835             * @link: https://developers.google.com/youtube/iframe_api_reference
1836             */
1837            initYouTubeAPI: function () {
1838
1839                // maybe load YouTube JS API
1840                if (typeof window.YT === 'undefined') {
1841                    var tag = document.createElement('script');
1842                    tag.src = '//www.youtube.com/iframe_api';
1843                    var firstScriptTag = document.getElementsByTagName('script')[0];
1844                    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
1845                }
1846
1847                // initialize when API is ready
1848                if ( typeof window.onYouTubeIframeAPIReady !== 'function' ) {
1849
1850                    window.onYouTubeIframeAPIReady = function () {
1851
1852                        // collect all possible YouTube tags
1853                        var potentialVideos = Utils.getTagsAsArray( 'iframe' ).concat( 
1853Utils.getTagsAsArray( 'embed' ) );
1854
1855                        // turn videos into trackable videos with events
1856                        for ( var i = 0; i < potentialVideos.length; i++ ) {
1857                            var video = potentialVideos[ i ];
1858                            if ( tagIsYouTubeVideo( video ) ) {
1859                                var iframe = normalizeYouTubeIframe( video );
1860                                addYouTubeEvents( iframe );
1861                            } else if ( tagIsYouTubeAsyncVideo( video ) ){
1862                                video.addEventListener( "load", function ( evt ) {
1863                                    var iframe = normalizeYouTubeIframe( evt.currentTarget );
1864                                    addYouTubeEvents( iframe );
1865                                } );
1866                            }
1867                        }
1868
1869                        var targets = document.querySelectorAll( '.elementor-widget-video .elementor-wrapper' );
1870
1871                        const config = {
1872                            attributes: false,
1873                            childList: true,
1874                            subtree: true
1875                        };
1876
1877                        const callback = function ( mutationsList, observer ) {
1878                            for ( let mutation of mutationsList ) {
1879                                if ( mutation.type === 'childList' ) {
1880                                    for ( var m = 0; m < mutation.addedNodes.length; m++ ) {
1881                                        addDynYouTubeVideos( mutation.addedNodes[ m ] );
1882                                    }
1883                                }
1884                            }
1885                        };
1886                        // observe elementator widget-video and add event when it add iframe
1887                        for ( var i = 0; i < targets.length; i++ ) {
1888                            const observer = new MutationObserver( callback );
1889                            observer.observe( targets[ i ], config );//maybe remove before add
1890                        }
1891                    }
1892                }
1893            },
1894
1895            /**
1896             * Load and initialize Vimeo API
1897             *
1898             * @link: https://github.com/vimeo/player.js
1899             */
1900            initVimeoAPI: function () {
1901
1902
1903                $(document).ready(function () {
1904
1905                    var potentialVideos = Utils.getTagsAsArray('iframe').concat(Utils.getTagsAsArray('embed'));
1906
1907                    for (var i = 0; i < potentialVideos.length; i++) {
1908                        var tag = potentialVideos[i];
1909                        if (tagIsVimeoVideo(tag)) {
1910                            var nextElementHasClass = $(tag).closest('.elementor-widget-video').find('.elementor-custom-embed-image-overlay');
1911                            if(nextElementHasClass.length > 0) {
1912                                tag.addEventListener("load", function() {
1913                                    attachVimeoPlayerToTag(this);
1914                                });
1915                            }
1916                            else {
1917                                attachVimeoPlayerToTag(tag);
1918                            }
1919
1920
1921                        } else {
1922                            if (tagIsAsincVimeoVideo(tag)) {
1923                                tag.addEventListener("load", function(evt) {
1924                                    attachVimeoPlayerToTag(evt.currentTarget);
1925                                });
1926                            }
1927                        }
1928                    }
1929
1930                });
1931
1932            },
1933
1934            manageCookies: function () {
1935                // Handle alternative GCLID parameter for Safari compatibility
1936                handleAlternativeGclid();
1937
1938                if (options.gdpr.cookiebot_integration_enabled && typeof Cookiebot !== 'undefined') {
1939                    if (Cookiebot.consented === false && !Cookiebot.consent['marketing'] && !Cookiebot.consent['statistics']) {
1940                        return;
1941                    }
1942                }
1943
1944                let cm_consent_not_expressed = false;
1945                if ( options.gdpr.consent_magic_integration_enabled && window.CS_Data !== undefined && window.CS_Data.cs_refresh_after_consent == 1 ) {
1946                    if ( Cookies.get( 'cs_viewed_cookie_policy' ) === undefined ) {
1947                        cm_consent_not_expressed = true;
1948                    }
1949                }
1950
1951                if( !cm_consent_not_expressed && isNewSession && !options.cookie.disabled_all_cookie && !options.cookie.disabled_start_session_cookie) {
1952                    let duration = options.last_visit_duration * 60000
1953                    var now = new Date();
1954                    now.setTime(now.getTime() + duration);
1955                    Cookies.set('pys_session_limit', true,{ expires: now, path: '/',domain: domain })
1956                    Cookies.set('pys_start_session', true,{ path: '/',domain: domain });
1957                    Utils.setHidePixelCookie();
1958                }
1959
1960                if (!Cookies.get('pbid') && Facebook.isEnabled() && options.ajaxForServerEvent) {
1961                    jQuery.ajax({
1962                        url: options.ajaxUrl,
1963                        dataType: 'json',
1964                        data: {
1965                            action: 'pys_get_pbid'
1966                        },
1967                        success: function (res) {
1968                            if (res.data && res.data.pbid != false && options.send_external_id) {
1969                                if(!(options.cookie.disabled_all_cookie || options.cookie.externalID_disabled_by_api)){
1970                                    var expires = parseInt(options.external_id_expire || 180);
1971                                    Cookies.set('pbid', res.data.pbid, { expires: expires,path: '/',domain: domain });
1972                                }
1973
1974                                if(options.hasOwnProperty('facebook')) {
1975                                    options.facebook.advancedMatching = {
1976                                        ...options.facebook.advancedMatching,  // spread current advancedMatching values
1977                                        external_id: res.data.pbid
1978                                    };
1979                                }
1980                            }
1981                        }
1982                    });
1983                } else if (Cookies.get('pbid') && Facebook.isEnabled()){
1984                    if(options.hasOwnProperty('facebook')) {
1985                        options.facebook.advancedMatching = {
1986                            ...options.facebook.advancedMatching,
1987                            external_id: Cookies.get('pbid')
1988                        };
1989                    }
1990                }
1991                let expires = parseInt(options.cookie_duration); //  days
1992                let queryVars = getQueryVars();
1993                let landing = window.location.href;
1994                try {
1995                    // save data for first visit
1996                    if(Cookies.get('pys_first_visit') === undefined && (!options.cookie.disabled_all_cookie)) {
1997
1998                        if(!options.cookie.disabled_first_visit_cookie)
1999                        {
2000                            Cookies.set('pys_first_visit', true, { expires: expires, path: '/',domain: domain });
2001                        }
2002                        else {
2003                            Cookies.remove('pys_first_visit', { path: '/',domain: domain } )
2004                        }
2005
2006                        if(!options.cookie.disabled_trafficsource_cookie)
2007                        {
2008                            Cookies.set('pysTrafficSource', getTrafficSource(), { expires: expires,path: '/',domain: domain });
2009                        }
2010                        else {
2011                            Cookies.remove('pysTrafficSource', { path: '/',domain: domain })
2012                        }
2013
2014                        if(!options.cookie.disabled_landing_page_cookie && options.enable_lading_page_param)
2015                        {
2016                            Cookies.set('pys_landing_page',landing,{ expires: expires,path: '/',domain: domain });
2017                        }
2018                        else {
2019                            Cookies.remove('pys_landing_page', { path: '/',domain: domain })
2020                        }
2021
2022                        if(!options.cookie.disabled_utmTerms_cookie)
2023                        {
2024                            $.each(utmTerms, function (index, name) {
2025                                if (queryVars.hasOwnProperty(name)) {
2026                                    Cookies.set('pys_' + name, queryVars[name], { expires: expires,path: '/',domain: domain });
2027                                } else {
2028                                    Cookies.remove('pys_' + name, { path: '/',domain: domain })
2029                                }
2030                            });
2031                        }
2032                        else {
2033                            $.each(utmTerms, function (index, name) {
2034                                Cookies.remove('pys_' + name, { path: '/',domain: domain })
2035                            });
2036                        }
2037
2038                        if(!options.cookie.disabled_utmId_cookie)
2039                        {
2040                            $.each(utmId,function(index,name) {
2041                                if (queryVars.hasOwnProperty(name)) {
2042                                    Cookies.set('pys_' + name, queryVars[name], { expires: expires,path: '/',domain: domain });
2043                                } else {
2044                                    Cookies.remove('pys_' + name, { path: '/',domain: domain })
2045                                }
2046                            })
2047                        }
2048                        else {
2049                            $.each(utmId, function (index, name) {
2050                                Cookies.remove('pys_' + name, { path: '/',domain: domain })
2051                            });
2052                        }
2053
2054                    }
2055                    // save data for last visit if it new session
2056                    if(isNewSession && (!options.cookie.disabled_all_cookie)) {
2057                        if(!options.cookie.disabled_trafficsource_cookie)
2058                        {
2059                            Cookies.set('last_pysTrafficSource', getTrafficSource(), { expires: expires,path: '/',domain: domain });
2060                        }
2061                        else {
2062                            Cookies.remove('last_pysTrafficSource', { path: '/',domain: domain })
2063                        }
2064
2065                        if(!options.cookie.disabled_landing_page_cookie && options.enable_lading_page_param)
2066                        {
2067                            Cookies.set('last_pys_landing_page',landing,{ expires: expires,path: '/',domain: domain });
2068                        }
2069                        else {
2070                            Cookies.remove('last_pys_landing_page', { path: '/',domain: domain })
2071                        }
2072
2073                        if(!options.cookie.disabled_utmTerms_cookie)
2074                        {
2075                            $.each(utmTerms, function (index, name) {
2076                                if (queryVars.hasOwnProperty(name)) {
2077                                    Cookies.set('last_pys_' + name, queryVars[name], { expires: expires,path: '/',domain: domain });
2078                                }
2079                            });
2080                        }
2081                        else {
2082                            $.each(utmTerms, function (index, name) {
2083                                Cookies.remove('last_pys_' + name, { path: '/',domain: domain })
2084                            });
2085                        }
2086
2087                        if(!options.cookie.disabled_utmId_cookie)
2088                        {
2089                            $.each(utmId,function(index,name) {
2090                                if (queryVars.hasOwnProperty(name)) {
2091                                    Cookies.set('last_pys_' + name, queryVars[name], { expires: expires,path: '/',domain: domain });
2092                                }
2093                            })
2094                        }
2095                        else {
2096                            $.each(utmId, function (index, name) {
2097                                Cookies.remove('last_pys_' + name, { path: '/',domain: domain })
2098                            });
2099                        }
2100
2101                    }
2102                    if(options.cookie.disabled_start_session_cookie) {
2103                        Cookies.remove('pys_start_session', { path: '/',domain: domain })
2104                        Cookies.remove('pys_session_limit', { path: '/',domain: domain })
2105                    }
2106                    if(options.cookie.disabled_all_cookie)
2107                    {
2108                        Cookies.remove('pys_first_visit', { path: '/',domain: domain })
2109                        Cookies.remove('pysTrafficSource', { path: '/',domain: domain })
2110                        Cookies.remove('pys_landing_page', { path: '/',domain: domain })
2111                        Cookies.remove('last_pys_landing_page', { path: '/',domain: domain })
2112                        Cookies.remove('last_pysTrafficSource', { path: '/',domain: domain })
2113                        Cookies.remove('pys_start_session', { path: '/',domain: domain })
2114                        Cookies.remove('pys_session_limit', { path: '/',domain: domain })
2115                        $.each(Utils.utmTerms, function (index, name) {
2116                            Cookies.remove('pys_' + name, { path: '/',domain: domain })
2117                        });
2118                        $.each(Utils.utmId,function(index,name) {
2119                            Cookies.remove('pys_' + name, { path: '/',domain: domain })
2120                        })
2121                        $.each(Utils.utmTerms, function (index, name) {
2122                            Cookies.remove('last_pys_' + name, { path: '/',domain: domain })
2123                        });
2124                        $.each(Utils.utmId,function(index,name) {
2125                            Cookies.remove('last_pys_' + name, { path: '/',domain: domain })
2126                        });
2127                    }
2128                } catch (e) {
2129                    console.error(e);
2130                }
2131            },
2132
2133            initializeRequestParams: function () {
2134
2135                if (options.trackTrafficSource) {
2136                    requestParams.traffic_source = getTrafficSourceValue();
2137                }
2138
2139                if (options.trackUTMs) {
2140
2141                    var utms = getUTMs(options.visit_data_model === "last_visit");
2142
2143                    $.each(utmTerms, function (index, term) {
2144                        if (term in utms) {
2145                            requestParams[term] = utms[term];
2146                        }
2147                    });
2148
2149                }
2150
2151
2152                var dateTime = getDateTime();
2153                if(options.enable_event_time_param) {
2154                    requestParams.event_hour = dateTime[0];
2155                }
2156
2157                if(options.enable_event_day_param) {
2158                    requestParams.event_day = dateTime[1];
2159                }
2160                if(options.enable_event_month_param) {
2161                    requestParams.event_month = dateTime[2];
2162                }
2163
2164                if(options.enable_lading_page_param){
2165                    requestParams.landing_page = stripUrlQuery(getLandingPageValue());
2166                }
2167            },
2168
2169            getRequestParams: function () {
2170                return requestParams;
2171            },
2172
2173            /**
2174             * DOWNLOAD DOCS
2175             */
2176
2177            getLinkExtension: function (link) {
2178
2179                // Remove anchor, query string and everything before last slash
2180                link = link.substring(0, (link.indexOf("#") === -1) ? link.length : link.indexOf("#"));
2181                link = link.substring(0, (link.indexOf("?") === -1) ? link.length : link.indexOf("?"));
2182                link = link.substring(link.lastIndexOf("/") + 1, link.length);
2183
2184                // If there's a period left in the URL, then there's a extension
2185                if (link.length > 0 && link.indexOf('.') !== -1) {
2186                    link = link.substring(link.lastIndexOf(".") + 1); // Remove everything but what's after the first period
2187                    return link;
2188                } else {
2189                    return "";
2190                }
2191            },
2192
2193            getLinkFilename: function (link) {
2194
2195                // Remove anchor, query string and everything before last slash
2196                link = link.substring(0, (link.indexOf("#") === -1) ? link.length : link.indexOf("#"));
2197                link = link.substring(0, (link.indexOf("?") === -1) ? link.length : link.indexOf("?"));
2198                link = link.substring(link.lastIndexOf("/") + 1, link.length);
2199
2200                // If there's a period left in the URL, then there's a extension
2201                if (link.length > 0 && link.indexOf('.') !== -1) {
2202                    return link;
2203                } else {
2204                    return "";
2205                }
2206            },
2207
2208            /**
2209             * CUSTOM EVENTS
2210             */
2211
2212            setupMouseOverClickEvents: function (eventId, triggers, triggerType) {
2213
2214                // Non-default binding used to avoid situations when some code in external js
2215                // stopping events propagation, eg. returns false, and our handler will never called
2216                document.addEventListener('mouseover', function(event) {
2217                    var matchedElements = Array.from(document.querySelectorAll(triggers));
2218                    var clickedElement = event.target;
2219                    var closestMatch = clickedElement.closest(triggers);
2220                    if (matchedElements.includes(clickedElement) || closestMatch){
2221                        if (event.target.classList.contains('pys-mouse-over-' + eventId)) {
2222                            return true;
2223                        } else {
2224                            event.target.classList.add('pys-mouse-over-' + eventId);
2225                        }
2226
2227                        Utils.handleTriggerFired(eventId, triggerType || 'css_mouseover');
2228                    }
2229                });
2230
2231
2232            },
2233
2234            setupFormFieldEvents: function (eventId, triggers, triggerType) {
2235                triggers.forEach(function(trigger) {
2236                    let eventType = 'input';
2237
2238                    if (['checkbox', 'radio', 'select'].includes(trigger.type)) {
2239                        eventType = 'change';
2240                    } else if (['text', 'email', 'tel'].includes(trigger.type)) {
2241                        eventType = 'blur';
2242                    }
2243
2244                    const form = document.querySelector(trigger.form);
2245                    if (!form) return;
2246
2247                    const field = form.querySelector(trigger.field); // search for the field ONLY within the form
2248                    if (!field) return;
2249
2250                    field.addEventListener(eventType, function (event) {
2251                        Utils.handleTriggerFired(eventId, triggerType || 'form_field');
2252                    }, true);
2253                });
2254            },
2255
2256            setupCSSClickEvents: function (eventId, triggers, triggerType, clickCount, clickTimeLimit) {
2257                // Non-default binding used to avoid situations when some code in external js
2258                // stopping events propagation, eg. returns false, and our handler will never called
2259                // add event to document to support dyn class
2260                var requiredClicks = (clickCount && clickCount > 1) ? clickCount : 1;
2261                var timeWindow     = (clickTimeLimit && clickTimeLimit > 0) ? clickTimeLimit : 0;
2262                var clickCounters  = typeof WeakMap !== 'undefined' ? new WeakMap() : null;
2263
2264                document.addEventListener('click', function(event) {
2265                    let matchedElements = Array.from(document.querySelectorAll(triggers)),
2266                        clickedElement = event.target,
2267                        closestMatch = clickedElement.closest(triggers);
2268
2269                    if (matchedElements.includes(clickedElement) || closestMatch) {
2270                        var target = closestMatch || clickedElement;
2271
2272                        if (requiredClicks <= 1) {
2273                            Utils.handleTriggerFired(eventId, triggerType || 'css_click');
2274                            return;
2275                        }
2276
2277                        if (!clickCounters) {
2278                            Utils.handleTriggerFired(eventId, triggerType || 'css_click');
2279                            return;
2280                        }
2281
2282                        var now = Date.now();
2283                        var state = clickCounters.get(target) || { count: 0, last: 0, timer: null };
2284
2285                        // When a time window is set, reset the counter if the gap exceeded it.
2286                        if (timeWindow > 0 && now - state.last > timeWindow) {
2287                            state.count = 0;
2288                        }
2289                        state.count++;
2290                        state.last = now;
2291
2292                        if (state.timer) clearTimeout(state.timer);
2293                        // Schedule cleanup to avoid memory leaks.
2294                        // With time window: delete entry after window expires.
2295                        // Without: quietly clear the timer reference after a long idle period.
2296                        state.timer = setTimeout(function() {
2297                            if (clickCounters) {
2298                                if (timeWindow > 0) {
2299                                    clickCounters.delete(target);
2300                                } else {
2301                                    var s = clickCounters.get(target);
2302                                    if (s) { s.timer = null; clickCounters.set(target, s); }
2303                                }
2304                            }
2305                        }, timeWindow > 0 ? timeWindow : 60000);
2306
2307                        clickCounters.set(target, state);
2308
2309                        if (state.count >= requiredClicks) {
2310                            clickCounters.delete(target);
2311                            Utils.handleTriggerFired(eventId, triggerType || 'css_click');
2312                        }
2313                    }
2314                }, true);
2315            },
2316
2317            setupCopyElementEvents: function (eventId, selectors) {
2318                var selectorList = Array.isArray(selectors) ? selectors.join(',') : selectors;
2319                if (!selectorList) return;
2320
2321                document.addEventListener('copy', function() {
2322                    var selection = window.getSelection();
2323                    if (!selection || selection.isCollapsed) return;
2324
2325                    var node = selection.focusNode;
2326                    var el = node ? (node.nodeType === 3 ? node.parentElement : node) : null;
2327                    if (!el) return;
2328
2329                    try {
2330                        if (el.closest(selectorList)) {
2331                            Utils.handleTriggerFired(eventId, 'copy_element');
2332                        }
2333                    } catch (e) {}
2334                });
2335            },
2336
2337            setupVideoSpeedEvents: function (eventId, config) {
2338                var urls      = Array.isArray(config) ? config : (config.urls || []);
2339                var speedRate = Array.isArray(config) ? 'any' : (config.speed_rate || 'any');
2340                var triggers  = Array.isArray(config) ? [] : (config.triggers || []);
2341
2342                var matchesPage = function() {
2343                    if (!urls || urls.length === 0) return false;
2344                    var href = window.location.href;
2345                    return urls.some(function(u) {
2346                        return u === '*' || u === '' || href.indexOf(u) !== -1;
2347                    });
2348                };
2349
2350                var matchesRate = function(prev, curr) {
2351                    if (speedRate === 'any') return curr !== prev;
2352                    if (speedRate === 'increase') return curr > prev;
2353                    if (speedRate === 'decrease') return curr < prev;
2354                    return false;
2355                };
2356
2357
2358                var matchesVideoId = function(videoId) {
2359                    if (!triggers || triggers.length === 0) return true;
2360                    var decId;
2361                    try { decId = decodeURIComponent(videoId); } catch(e) { decId = videoId; }
2362                    return triggers.some(function(t) {
2363                        if (t.rule === videoId || t.rule === String(videoId)) return true;
2364                        var decRule;
2365                        try { decRule = decodeURIComponent(t.rule); } catch(e) { decRule = t.rule; }
2366                        return decRule === decId;
2367                    });
2368                };
2369
2370                if (!matchesPage()) return;
2371
2372                // HTML5 <video> speed tracking
2373                document.addEventListener('ratechange', function(event) {
2374                    var video = event.target;
2375                    if (!video || video.tagName !== 'VIDEO') return;
2376                    var prev = video._pysSpeedRate !== undefined ? video._pysSpeedRate : 1;
2377                    var curr = video.playbackRate;
2378                    video._pysSpeedRate = curr;
2379
2380                    if (matchesRate(prev, curr)) {
2381                        var videoId = video.id || video.src || video.currentSrc || '';
2382                        if (matchesVideoId(videoId)) {
2383                            Utils.handleTriggerFired(eventId, 'video_speed');
2384                        }
2385                    }
2386                }, true);
2387            },
2388
2389            setupURLClickEvents: function () {
2390
2391                if( !options.triggerEventTypes.hasOwnProperty('url_click') ) {
2392                    return;
2393                }
2394                // Non-default binding used to avoid situations when some code in external js
2395                // stopping events propagation, eg. returns false, and our handler will never called
2396                document.addEventListener('click', function(event) {
2397                    let anchor = event.target.closest('a');
2398
2399                    if (anchor) {
2400                        var url = anchor.getAttribute('href');
2401                        if (url) {
2402                            Object.entries(options.triggerEventTypes.url_click).forEac
2402h(function ([eventId, triggers]) {
2403                                triggers.forEach(function (trigger) {
2404                                    if (Utils.compareUrl(url, trigger.value, trigger.rule)) {
2405                                        Utils.handleTriggerFired(eventId, 'url_click');
2406                                    }
2407                                });
2408                            });
2409                        }
2410                    }
2411                }, true);
2412
2413            },
2414
2415            removeUrlDomain(url) {
2416                if(url.indexOf("/#") > -1) {
2417                    url = url.substring(0, url.indexOf("/#"));
2418                }
2419                return url.replace('http://','')
2420                    .replace('https://','')
2421                    .replace('www.','')
2422                    .trim()
2423                    .replace(/^\/+/g, '')
2424
2425            },
2426
2427            compareUrl: function(base,url,rule){
2428
2429                if(url == "*" || url == '') return true;
2430
2431                base = Utils.removeUrlDomain(base)
2432                url = Utils.removeUrlDomain(url)
2433
2434                if(rule == 'match') {
2435                    return url == base;
2436                } else {
2437                    return base.indexOf(url) !== -1
2438                }
2439
2440            },
2441
2442            setupScrollPosEvents: function (eventId, triggers, triggerType) {
2443
2444                var scrollPosThresholds = {},
2445                    docHeight = $(document).height() - $(window).height();
2446
2447                // convert % to absolute positions
2448                $.each(triggers, function (index, scrollPos) {
2449
2450                    // convert % to pixels
2451                    scrollPos = docHeight * scrollPos / 100;
2452                    scrollPos = Math.round(scrollPos);
2453
2454                    scrollPosThresholds[scrollPos] = eventId;
2455
2456                });
2457
2458                $(document).on("scroll",function () {
2459
2460                    var scrollPos = $(window).scrollTop();
2461
2462                    $.each(scrollPosThresholds, function (threshold, eventId) {
2463
2464                        // position has not reached yes
2465                        if (scrollPos < threshold - 0.5) {
2466                            return true;
2467                        }
2468
2469                        // fire event only once
2470                        if (eventId === null) {
2471                            return true;
2472                        } else {
2473                            scrollPosThresholds[threshold] = null;
2474                        }
2475
2476                        Utils.handleTriggerFired(eventId, triggerType || 'scroll_pos');
2477                    });
2478
2479                });
2480
2481            },
2482            setupCommentEvents : function (eventId,triggers) {
2483                $('form.comment-form').on("submit",function () {
2484                    Utils.fireTriggerEvent(eventId);
2485                });
2486            },
2487
2488            setupEmailLinkEvents: function ( events ) {
2489
2490                $( document ).on( 'click', 'a', function ( event ) {
2491                    let anchor = event.target.closest('a');
2492
2493                    if (anchor) {
2494                        let url = anchor.getAttribute('href');
2495                        if (url && (url.startsWith('mailto:') || url.startsWith('tel:'))) {
2496
2497                            let sendEventIds = [],
2498                                disabled_for_pixels = [],
2499                                disabled_email_action = false;
2500
2501                            $.each(events, function (eventId, trigger_group) {
2502                                url = url.replace('mailto:', '').replace('tel:', '');
2503                                trigger_group.forEach(function (triggers) {
2504                                    triggers.rules.forEach(function (trigger) {
2505                                        if (Utils.compareUrl(url, trigger.value, trigger.rule)) {
2506                                            sendEventIds.push(eventId);
2507                                            if (triggers.hasOwnProperty('disabled_email_link') && triggers.disabled_email_link) {
2508                                                disabled_email_action = true;
2509                                                if (options.triggerEvents.hasOwnProperty(eventId)) {
2510                                                    disabled_for_pixels.push(...Object.keys(options.triggerEvents[eventId]));
2511                                                }
2512                                            }
2513                                        }
2514                                    });
2515                                })
2516                            })
2517
2518                            if (sendEventIds.length > 0) {
2519                                sendEventIds.forEach(function (sendEventId) {
2520                                    Utils.handleTriggerFired(sendEventId, 'email_link');
2521                                })
2522                            }
2523                            setupEmailLinks(disabled_for_pixels);
2524                        }
2525                    }
2526                } )
2527            },
2528
2529            /**
2530             * Events
2531             */
2532
2533            isEventInTimeWindow: function (eventName, event, prefix) {
2534
2535                if(event.hasOwnProperty("hasTimeWindow") && event.hasTimeWindow) {
2536                    var cookieName = prefix+"_"+eventName;
2537                    var now = new Date().getTime();
2538
2539                    if(Cookies.get(cookieName) !== undefined) {
2540
2541                        var lastTimeFire = Cookies.get(cookieName);
2542                        var fireTime = event.timeWindow * 60*60*1000;
2543
2544                        if( now - lastTimeFire > fireTime) {
2545                            Cookies.set(cookieName,now, { expires: event.timeWindow / 24.0, path: '/'} );
2546                        } else {
2547                            return false;
2548                        }
2549                    } else {
2550                        Cookies.set(cookieName,now, { expires: event.timeWindow / 24.0, path: '/'} );
2551                    }
2552                }
2553                return true
2554            },
2555
2556            handleTriggerFired: function (eventId, triggerType) {
2557                if (options.triggerLogic && options.triggerLogic[eventId] === 'AND') {
2558                    var state = pysCustomEventsState[eventId];
2559                    if (!state || state.fired) return;
2560                    state.triggered[triggerType] = true;
2561                    if (Object.keys(state.triggered).length >= state.total) {
2562                        state.fired = true;
2563                        Utils.fireTriggerEvent(eventId);
2564                    }
2565                } else {
2566                    Utils.fireTriggerEvent(eventId);
2567                }
2568            },
2569
2570            fireTriggerEvent: function (eventId) {
2571
2572                if (!options.triggerEvents.hasOwnProperty(eventId)) {
2573                    return;
2574                }
2575
2576                if (options.triggerFrequency && options.triggerFrequency[eventId] === 'once') {
2577                    if (pysEventFiredOnce[eventId]) return;
2578                    pysEventFiredOnce[eventId] = true;
2579                }
2580
2581                var event = {};
2582                var events = options.triggerEvents[eventId];
2583
2584                if (events.hasOwnProperty('facebook')) {
2585                    event = events.facebook;
2586                    if(Utils.isEventInTimeWindow(event.name,event,"dyn_facebook_"+eventId)) {
2587                        event = Utils.getFormFilledData(event);
2588                        Facebook.fireEvent(event.name, event);
2589                    }
2590                }
2591
2592                if (events.hasOwnProperty('ga')) {
2593                    event = events.ga;
2594                    if(Utils.isEventInTimeWindow(event.name,event,"dyn_ga_"+eventId)) {
2595                        event = Utils.getFormFilledData(event);
2596                        Analytics.fireEvent(event.name, event);
2597                    }
2598                }
2599
2600                if (events.hasOwnProperty('google_ads')) {
2601                    event = events.google_ads;
2602                    if(Utils.isEventInTimeWindow(event.name,event,"dyn_google_ads_"+eventId)) {
2603                        event = Utils.getFormFilledData(event);
2604                        GAds.fireEvent(event.name, event);
2605                    }
2606                }
2607
2608                if (events.hasOwnProperty('pinterest')) {
2609                    event = events.pinterest;
2610                    if(Utils.isEventInTimeWindow(event.name,event,"dyn_pinterest_"+eventId)) {
2611                        event = Utils.getFormFilledData(event);
2612                        Pinterest.fireEvent(event.name, event);;
2613                    }
2614                }
2615
2616                if (events.hasOwnProperty('bing')) {
2617                    event = events.bing;
2618                    if(Utils.isEventInTimeWindow(event.name,event,"dyn_bing_"+eventId)) {
2619                        event = Utils.getFormFilledData(event);
2620                        Bing.fireEvent(event.name, event);;
2621                    }
2622                }
2623                if (events.hasOwnProperty('tiktok')) {
2624                    event = events.tiktok;
2625                    if(Utils.isEventInTimeWindow(event.name,event,"dyn_tiktok_"+eventId)) {
2626                        event = Utils.getFormFilledData(event);
2627                        TikTok.fireEvent(event.name, event);
2628                    }
2629                }
2630                if (events.hasOwnProperty('reddit')) {
2631                    event = events.reddit;
2632                    if(Utils.isEventInTimeWindow(event.name,event,"dyn_reddit_"+eventId)) {
2633                        event = Utils.getFormFilledData(event);
2634                        Reddit.fireEvent(event.name, event);
2635                    }
2636                }
2637
2638
2639                if (events.hasOwnProperty('gtm')) {
2640                    event = events.gtm;
2641                    if(Utils.isEventInTimeWindow(event.name,event,"dyn_gtm_"+eventId)) {
2642                        event = Utils.getFormFilledData(event);
2643                        GTM.fireEvent(event.name, event);
2644                    }
2645                }
2646
2647
2648            },
2649
2650            isFirstPurchaseFire: function ($eventName,orderId,pixel) {
2651
2652                if(Cookies.get("pys_"+$eventName+"_order_id_"+pixel) == orderId) {
2653                    return false;
2654                } else {
2655                    Cookies.set("pys_"+$eventName+"_order_id_"+pixel, orderId, { expires: 1,path: '/' });
2656                }
2657                return true;
2658            },
2659
2660            fireStaticEvents: function (pixel, timeout = 0) {
2661
2662                if (options.staticEvents.hasOwnProperty(pixel)) {
2663
2664                    $.each(options.staticEvents[pixel], function (eventId, events) {
2665
2666                        //skip purchase event if this order was fired
2667                        if( options.woo.hasOwnProperty('woo_purchase_on_transaction') &&
2668                            options.woo.woo_purchase_on_transaction &&
2669                            (eventId === "woo_purchase" || eventId === "woo_purchase_category") ) {
2670                            if(!Utils.isFirstPurchaseFire(eventId,events[0].woo_order,pixel)) {
2671                                return;
2672                            }
2673                        }
2674
2675                        if( options.edd.hasOwnProperty('edd_purchase_on_transaction') &&
2676                            options.edd.edd_purchase_on_transaction &&
2677                            (eventId === "edd_purchase" || eventId === "edd_purchase_category") ) {
2678                            if(!Utils.isFirstPurchaseFire(eventId,events[0].edd_order,pixel)) {
2679                                return;
2680                            }
2681                        }
2682
2683
2684                        $.each(events, function (index, event) {
2685
2686                            event.fired = event.fired || false;
2687
2688                            if (!event.fired && Utils.isEventInTimeWindow(event.
2688name,event,'static_' + pixel+"_")) {
2689                                event = Utils.getFormFilledData(event);
2690
2691                                var fired = false;
2692
2693                                // fire event
2694                                if ( pixel === 'tiktok' ) {
2695                                    setTimeout(function() {
2696                                        getPixelBySlag(pixel).fireEvent(event.name, event);
2697                                    }, timeout * 500);
2698                                    timeout++;
2699                                } else {
2700                                    getPixelBySlag(pixel).fireEvent(event.name, event);
2701                                }
2702
2703                                // prevent event double event firing
2704                                event.fired = fired;
2705                            }
2706                        });
2707                    });
2708                }
2709            },
2710
2711            /**
2712             * Load tag's JS
2713             *
2714             * @link: https://developers.google.com/analytics/devguides/collection/gtagjs/
2715             * @link: https://developers.google.com/analytics/devguides/collection/gtagjs/custom-dims-mets
2716             */
2717            loadGoogleTag: function (id) {
2718
2719                if (!gtag_loaded) {
2720                    let dataLayerName = this.resolveGaDataLayerName();
2721                    this.dataLayerName = dataLayerName;
2722                    (function (window, document, src) {
2723                        var a = document.createElement('script'),
2724                            m = document.getElementsByTagName('script')[0];
2725                        a.async = 1;
2726                        a.src = src;
2727                        m.parentNode.insertBefore(a, m);
2728                    })(window, document, '//www.googletagmanager.com/gtag/js?id=' + id+'&l='+this.dataLayerName);
2729
2730                    window[dataLayerName] = window[dataLayerName] || [];
2731                    // Published only so custom client code and third-party snippets
2732                    // have a gtag() to call. PYS itself must never route through it:
2733                    // the `||` means an earlier definition (Site Kit, a theme, the
2734                    // GTM bootstrap) keeps ownership and would point at its own data
2735                    // layer. Use Utils.gtagPush() instead.
2736                    window.gtag = window.gtag || function gtag() {
2737                        window[dataLayerName].push(arguments);
2738                    };
2739
2740                    if ( options.google_consent_mode ) {
2741                        let data = {};
2742                        data[ 'analytics_storage' ] = options.gdpr.analytics_storage.enabled ? options.gdpr.analytics_storage.value : 'granted';
2743                        data[ 'ad_storage' ] = options.gdpr.ad_storage.enabled ? options.gdpr.ad_storage.value : 'granted';
2744                        data[ 'ad_user_data' ] = options.gdpr.ad_user_data.enabled ? options.gdpr.ad_user_data.value : 'granted';
2745                        data[ 'ad_personalization' ] = options.gdpr.ad_personalization.enabled ? options.gdpr.ad_personalization.value : 'granted';
2746
2747                        this.loadDefaultConsent( 'consent', 'default', data );
2748                    }
2749
2750                    Utils.gtagPush('js', new Date());
2751                    gtag_loaded = true;
2752
2753                }
2754
2755            },
2756
2757            /**
2758             * Name of the data layer gtag.js is loaded with, see loadGoogleTag().
2759             * Pure, so it can be resolved before the tag is loaded.
2760             */
2761            resolveGaDataLayerName: function () {
2762                if ( options.hasOwnProperty( 'GATags' ) ) {
2763                    switch ( options.GATags.ga_datalayer_type ) {
2764                        case 'default':
2765                            return 'dataLayerPYS';
2766                        case 'custom':
2767                            return options.GATags.ga_datalayer_name;
2768                        default:
2769                            return 'dataLayer';
2770                    }
2771                }
2772                return this.dataLayerName || 'dataLayer';
2773            },
2774
2775            /**
2776             * Sends a gtag command (js/config/set/event/consent) to the GA data layer.
2777             *
2778             * Deliberately does NOT call window.gtag. That global is created with
2779             * `window.gtag = window.gtag || ...`, so whoever defined it first owns it:
2780             * if a theme, Site Kit or the GTM bootstrap got there first, our config
2781             * and event commands would silently land in their data layer while the
2782             * gtag.js instance we loaded (&l=<GA layer>) never receives a config —
2783             * Tag Assistant then reports "Some hits will only be sent after a config
2784             * command is issued". Pushing the arguments object is exactly what gtag()
2785             * does internally, so the payload is identical.
2786             */
2787            gtagPush: function () {
2788                var name = this.dataLayerName || this.resolveGaDataLayerName();
2789                window[ name ] = window[ name ] || [];
2790                window[ name ].push( arguments );
2791            },
2792
2793            loadDefaultConsent: function() {
2794                this.gtagPush.apply( this, arguments );
2795            },
2796
2797            loadGTMScript: function (id = '') {
2798                // Strip trailing slash to allow both "domain.com" and "domain.com/" inputs.
2799                const domain = ( options.gtm.gtm_container_domain ?? 'www.googletagmanager.com' ).replace( /\/$/, '' );
2800                const loader = options.gtm.gtm_container_identifier ?? 'gtm';
2801                const gtm_auth = options.gtm.gtm_auth ?? '';
2802                const gtm_preview = options.gtm.gtm_preview ?? '';
2803                const datalayer_name = options.gtm.gtm_dataLayer_name ?? 'dataLayer';
2804
2805                window[ datalayer_name ] = window[ datalayer_name ] || [];
2806                window.gtag = window.gtag || function gtag() {
2807                    window[ datalayer_name ].push( arguments );
2808                };
2809
2810                // Push consent defaults synchronously before the GTM script tag is inserted.
2811                // When PYS loads the GTM snippet itself (non-dataLayer-only mode), this ensures
2812                // defaults are in the dataLayer before GTM processes any events.
2813                this.pushGTMConsentDefaults( datalayer_name );
2814
2815                (function(w, d, s, l, i) {
2816                    w[l] = w[l] || [];
2817                    w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
2818                    const f = d.getElementsByTagName(s)[0];
2819                    const j = d.createElement(s);
2820                    const dl = l !== 'dataLayer' ? '&l=' + l : '';
2821                    j.async = true;
2822
2823                    // Detect domain format: bare hostname (legacy) vs full URL / relative path (new).
2824                    // Legacy "www.googletagmanager.com" → prepe
2824nd https:// and append .js.
2825                    // New format "/metrics" or "https://custom.com" → use as-is.
2826                    //   Identifier with extension (e.g. "gtm.js")  → file:   domain/gtm.js?id=
2827                    //   Identifier without extension (e.g. "g4h25o") → path: domain/g4h25o/?id=
2828                    const isNewFormat = domain.startsWith( 'http://' )
2829                        || domain.startsWith( 'https://' )
2830                        || domain.startsWith( '/' );
2831
2832                    if ( !isNewFormat ) {
2833                        j.src = 'https://' + domain + '/' + loader + '.js?id=' + i + dl;
2834                    } else {
2835                        const hasExtension = loader.includes( '.' );
2836                        j.src = domain + '/' + loader + ( hasExtension ? '?' : '/?' ) + 'id=' + i + dl;
2837                    }
2838
2839                    if (gtm_auth && gtm_preview) {
2840                        j.src += '&gtm_auth=' + gtm_auth + '&gtm_preview=' + gtm_preview + '&gtm_cookies_win=x';
2841                    }
2842                    f.parentNode.insertBefore(j, f);
2843                })(window, document, 'script', datalayer_name, id);
2844
2845            },
2846
2847            loadDefaultGTMConsent: function() {
2848                window[ this.GTMdataLayerName ].push( arguments );
2849            },
2850
2851            // Extracted helper: push GTM consent defaults to the dataLayer.
2852            // Called from loadGTMScript (when PYS loads the snippet) and from
2853            // GTM.loadPixel when gtm_just_data_layer is enabled, so defaults are
2854            // always present regardless of which code path loads the GTM container.
2855            pushGTMConsentDefaults: function( datalayer_name ) {
2856                if ( !options.google_consent_mode ) {
2857                    return;
2858                }
2859                let data = {};
2860                data[ 'analytics_storage' ]  = options.gdpr.analytics_storage.enabled  ? options.gdpr.analytics_storage.value  : 'granted';
2861                data[ 'ad_storage' ]         = options.gdpr.ad_storage.enabled         ? options.gdpr.ad_storage.value         : 'granted';
2862                data[ 'ad_user_data' ]       = options.gdpr.ad_user_data.enabled       ? options.gdpr.ad_user_data.value       : 'granted';
2863                data[ 'ad_personalization' ] = options.gdpr.ad_personalization.enabled ? options.gdpr.ad_personalization.value : 'granted';
2864                this.GTMdataLayerName = datalayer_name;
2865                this.loadDefaultGTMConsent( 'consent', 'default', data );
2866            },
2867
2868            /**
2869             * GDPR
2870             */
2871
2872            loadPixels: function () {
2873
2874                if (options.gdpr.ajax_enabled && !options.gdpr.consent_magic_integration_enabled) {
2875
2876                    // retrieves actual PYS GDPR filters values which allow to avoid cache issues
2877                    $.get({
2878                        url: options.ajaxUrl,
2879                        dataType: 'json',
2880                        data: {
2881                            action: 'pys_get_gdpr_filters_values'
2882                        },
2883                        success: function (res) {
2884
2885                            if (res.success) {
2886
2887                                options.gdpr.all_disabled_by_api = res.data.all_disabled_by_api;
2888                                options.gdpr.facebook_disabled_by_api = res.data.facebook_disabled_by_api;
2889                                options.gdpr.tiktok_disabled_by_api = res.data.tiktok_disabled_by_api;
2890                                options.gdpr.analytics_disabled_by_api = res.data.analytics_disabled_by_api;
2891                                options.gdpr.google_ads_disabled_by_api = res.data.google_ads_disabled_by_api;
2892                                options.gdpr.pinterest_disabled_by_api = res.data.pinterest_disabled_by_api;
2893                                options.gdpr.bing_disabled_by_api = res.data.bing_disabled_by_api;
2894                                options.gdpr.reddit_disabled_by_api = res.data.reddit_disabled_by_api;
2895
2896                                options.cookie.externalID_disabled_by_api = res.data.externalID_disabled_by_api;
2897                                options.cookie.disabled_all_cookie = res.data.disabled_all_cookie;
2898                                options.cookie.disabled_advanced_form_data_cookie = res.data.disabled_advanced_form_data_cookie;
2899                                options.cookie.disabled_landing_page_cookie = res.data.disabled_landing_page_cookie || !options.enable_lading_page_param;
2900                                options.cookie.disabled_first_visit_cookie = res.data.disabled_first_visit_cookie;
2901                                options.cookie.disabled_trafficsource_cookie = res.data.disabled_trafficsource_cookie;
2902                                options.cookie.disabled_utmTerms_cookie = res.data.disabled_utmTerms_cookie;
2903                                options.cookie.disabled_utmId_cookie = res.data.disabled_utmId_cookie;
2904                            }
2905                            loadPixels();
2906
2907                        }
2908                    });
2909
2910                } else {
2911                    loadPixels();
2912                }
2913
2914            },
2915
2916            consentGiven: function (pixel) {
2917
2918                /**
2919                 * Cookiebot
2920                 */
2921                if (options.gdpr.cookiebot_integration_enabled && typeof Cookiebot !== 'undefined') {
2922
2923                    var cookiebot_consent_category = options.gdpr['cookiebot_' + pixel + '_consent_category'];
2924
2925                    if (options.gdpr[pixel + '_prior_consent_enabled']) {
2926                        if (Cookiebot.consented === true || Cookiebot.consent[cookiebot_consent_category]) {
2927                            return true;
2928                        }
2929                    } else {
2930                        if (Cookiebot.consent[cookiebot_consent_category]) {
2931                            return true;
2932                        }
2933                    }
2934                    return false;
2935
2936                }
2937
2938                /**
2939                 * Cookie Notice
2940                 */
2941                if (options.gdpr.cookie_notice_integration_enabled && typeof cnArgs !== 'undefined') {
2942
2943                    var cn_cookie = Cookies.get(cnArgs.cookieName);
2944
2945                    if (options.gdpr[pixel + '_prior_consent_enabled']) {
2946                        if (typeof cn_cookie === 'undefined' || cn_cookie === 'true') {
2947                            return true;
2948                        }
2949                    } else {
2950                        if (cn_cookie === 'true') {
2951                            return true;
2952                        }
2953                    }
2954
2955                    return false;
2956
2957                }
2958
2959                /**
2960                 * Cookie Law Info
2961                 */
2962
2963                if (options.gdpr.cookie_law_info_integration_enabled) {
2964                    var cli_cookie = Cookies.get('cookieyes-consent') ?? Cookies.get('wt_consent') ?? Cookies.get('viewed_cookie_policy');
2965                    if (options.gdpr[pixel + '_prior_consent_enabled']) {
2966                        if (typeof cli_cookie === 'undefined') return true;
2967                        if (
2968                            cli_cookie &&
2969                            (cli_cookie === Cookies.get('cookieyes-consent') || cli_cookie === Cookies.get('wt_consent'))
2970                        ) {
2971                            if (getCookieYes('analytics') === 'yes') {
2972                                return true;
2973                            }
2974                        } else if (cli_cookie && cli_cookie === Cookies.get('viewed_cookie_policy')) {
2975                            if (Cookies.get('viewed_cookie_policy') === 'yes') {
2976                                return true;
2977                            }
2978                        }
2979                    } else {
2980                        if (
2981                            cli_cookie &&
2982                            (cli_cookie === Cookies.get('cookieyes-consent') || cli_cookie === Cookies.get('wt_consent'))
2983                        ) {
2984                            if (getCookieYes('analytics') === 'yes') {
2985                                return true;
2986                            }
2987                        } else if (cli_cookie && cli_cookie === Cookies.get('viewed_cookie_policy')) {
2988                            if (Cookies.get('viewed_cookie_policy') === 'yes') {
2989                                return true;
2990                            }
2991                        }
2992                    }
2993                    return false;
2994                }
2995
2996                /**
2997                 * ConsentMagic
2998                 */
2999                if (options.gdpr.consent_magic_integration_enabled && typeof CS_Data !== "undefined" ) {
3000
3001                    let test_prefix = CS_Data.test_prefix;
3002                    if (
3003                        ( ( typeof CS_Data.cs_google_consent_mode_enabled !== "undefined" && CS_Data.cs_google_consent_mode_enabled == 1 ) && ( pixel == 'analytics' || pixel == 'google_ads' ) )
3004                        || ( typeof CS_Data.cs_meta_ldu_mode !== "undefined" && CS_Data.cs_meta_ldu_mode && pixel == 'facebook' )
3005                        || ( typeof CS_Data.cs_reddit_ldu_mode !== "undefined" && CS_Data.cs_reddit_ldu_mode && pixel == 'reddit' )
3006                        || ( typeof CS_Data.cs_bing_consent_mode !== "undefined" && CS_Data.cs_bing_consent_mode.ad_storage.enabled && pixel == 'bing' )
3007                    ) {
3008                        if ( CS_Data.cs_cache_enabled == 0 || ( CS_Data.cs_cache_enabled == 1 && window.CS_Cache && window.CS_Cache.check_status ) ) {
3009                            return true;
3010                        } else {
3011                            return false;
3012                        }
3013                    }
3014
3015                    if( pixel == 'facebook' && ( CS_Data.cs_script_cat.facebook == 0 || CS_Data.cs_script_cat.facebook == CS_Data.cs_necessary_cat_id ) ) {
3016                        return true;
3017                    } else if( pixel == 'bing' && ( CS_Data.cs_script_cat.bing == 0 || CS_Data.cs_script_cat.bing == CS_Data.cs_necessary_cat_id ) ) {
3018                        return true;
3019                    } else if( pixel == 'analytics' && ( CS_Data.cs_script_cat.analytics == 0 || CS_Data.cs_script_cat.analytics == CS_Data.cs_necessary_cat_id ) ) {
3020                        return true;
3021                    } else if( pixel == 'google_ads' && ( CS_Data.cs_script_cat.gads == 0 || CS_Data.cs_script_cat.gads == CS_Data.cs_necessary_cat_id ) ) {
3022                        return true;
3023                    } else if( pixel == 'pinterest' && ( CS_Data.cs_script_cat.pinterest == 0 || CS_Data.cs_script_cat.pinterest == CS_Data.cs_necessary_cat_id ) ) {
3024                        return true;
3025                    } else if( pixel == 'tiktok' && ( CS_Data.cs_script_cat.tiktok == 0 || CS_Data.cs_script_cat.tiktok == CS_Data.cs_necessary_cat_id ) ) {
3026                        return true;
3027                    } else if( pixel == 'reddit' && ( CS_Data.cs_script_cat?.reddit == 0 || CS_Data.cs_script_cat?.reddit == CS_Data.cs_necessary_cat_id ) ) {
3028                        return true;
3029                    }
3030
3031                    let substring = "cs_enabled_cookie_term",
3032                        theCookies = document.cookie.split( ';' );
3033
3034                    for ( let i = 1; i <= theCookies.length; i++ ) {
3035                        if ( theCookies[ i - 1 ].indexOf( substring ) !== -1 ) {
3036                            let categoryCookie = theCookies[ i - 1 ].replace( 'cs_enabled_cookie_term' + test_prefix + '_', '' );
3037                            categoryCookie = Number( categoryCookie.replace( /\D+/g, "" ) );
3038                            let cs_cookie_val = Cookies.get( 'cs_enabled_cookie_term' + test_prefix + '_' + categoryCookie );
3039
3040                            if ( categoryCookie === CS_Data.cs_script_cat.facebook && pixel == 'facebook' ) {
3041                                return cs_cookie_val == 'yes';
3042                            } else if ( categoryCookie === CS_Data.cs_script_cat.bing && pixel == 'bing' ) {
3043                                return cs_cookie_val == 'yes';
3044                            } else if ( categoryCookie === CS_Data.cs_script_cat.analytics && pixel == 'analytics' ) {
3045                                return cs_cookie_val == 'yes';
3046                            } else if ( categoryCookie === CS_Data.cs_script_cat.gads && pixel == 'google_ads' ) {
3047                                return cs_cookie_val == 'yes';
3048                            } else if ( categoryCookie === CS_Data.cs_script_cat.pinterest && pixel == 'pinterest' ) {
3049                                return cs_cookie_val == 'yes';
3050                            } else if ( categoryCookie === CS_Data.cs_script_cat.tiktok && pixel == 'tiktok' ) {
3051                                return cs_cookie_val == 'yes';
3052                            } else if ( categoryCookie === CS_Data.cs_script_cat?.reddit && pixel == 'reddit' ) {
3053                                return cs_cookie_val == 'yes';
3054                            }
3055                        }
3056                    }
3057
3058                    return false;
3059                }
3060
3061
3062                /**
3063                 * Real Cookie Banner
3064                 */
3065                if (options.gdpr.real_cookie_banner_integration_enabled) {
3066                    var consentApi = window.consentApi;
3067                    if (consentApi) {
3068                        switch (pixel) {
3069                            case "analytics":
3070                                return consentApi.consentSync("http", "_ga", "*").cookieOptIn;
3071                            case "facebook":
3072                                return consentApi.consentSync("http", "_fbp", "*").cookieOptIn;
3073                            case "pinterest":
3074                                return consentApi.consentSync("http", "_pinterest_sess", ".pinterest.com").cookieOptIn;
3075                            case "bing":
3076                                return consentApi.consentSync("http", "_uetsid", "*").cookieOptIn;
3077                            case "google_ads":
3078                                return consentApi.consentSync("http", "1P_JAR", ".google.com").cookieOptIn;
3079                            case 'tiktok':
3080                                return consentApi.consentSync("http", "tt_webid_v2", ".tiktok.com").cookieOptIn;
3081                            default:
3082                                return true;
3083                        }
3084                    }
3085                }
3086
3087                return true;
3088
3089            },
3090
3091            setupGdprCallbacks: function () {
3092
3093                let isConcent = false;
3094
3095                /**
3096                 * Cookiebot
3097                 */
3098                if ( options.gdpr.cookiebot_integration_enabled && typeof Cookiebot !== 'undefined' ) {
3099                    isConcent = true;
3100                    window.addEventListener( "CookiebotOnConsentReady", function () {
3101
3102                        let consent = {
3103                            facebook: true,
3104                            ga: true,
3105                            google_ads: true,
3106                            tiktok: true,
3107                            bing: true,
3108                            pinterest: true,
3109                            gtm: true,
3110                        };
3111                        Utils.initializeVideoAPIs( options );
3112
3113                        Utils.manageCookies();
3114                        if ( Cookiebot.consent.marketing ) {
3115                            Facebook.loadPixel();
3116                            Bing.loadPixel();
3117                            Pinterest.loadPixel();
3118                            GAds.loadPixel();
3119                            TikTok.loadPixel();
3120
3121                            consent.facebook = true;
3122                            consent.bing = true;
3123                            consent.google_ads = true;
3124                            consent.pinterest = true;
3125                            consent.tiktok = true;
3126                        }
3127                        if ( Cookiebot.consent.statistics ) {
3128                            Analytics.loadPixel()
3128;
3129
3130                            consent.ga = true;
3131                            consent.gtm = true;
3132                        }
3133                        if ( !Cookiebot.consent.marketing ) {
3134                            Facebook.disable();
3135                            Pinterest.disable();
3136                            Bing.disable()
3137                            GAds.disable();
3138                            TikTok.disable();
3139
3140                            consent.facebook = false;
3141                            consent.bing = false;
3142                            consent.google_ads = false;
3143                            consent.pinterest = false;
3144                            consent.tiktok = false;
3145                        }
3146                        if ( !Cookiebot.consent.statistics ) {
3147                            Analytics.disable();
3148
3149                            consent.ga = false;
3150                            consent.gtm = false;
3151                        }
3152
3153                        Utils.setupGDPRData( consent );
3154                    } );
3155                }
3156
3157                /**
3158                 * Cookie Notice
3159                 */
3160                if ( options.gdpr.cookie_notice_integration_enabled ) {
3161                    isConcent = true;
3162                    $( document ).onFirst( 'click', '.cn-set-cookie', function () {
3163
3164                        let consent = {};
3165
3166                        if ( $( this ).data( 'cookie-set' ) === 'accept' ) {
3167
3168                            Utils.initializeVideoAPIs( options );
3169
3170                            Facebook.loadPixel();
3171                            Analytics.loadPixel();
3172                            GAds.loadPixel();
3173                            Pinterest.loadPixel();
3174                            Bing.loadPixel();
3175                            TikTok.loadPixel();
3176
3177                            consent = {
3178                                facebook: true,
3179                                ga: true,
3180                                google_ads: true,
3181                                tiktok: true,
3182                                bing: true,
3183                                pinterest: true,
3184                                gtm: true,
3185                            };
3186                        } else {
3187                            Facebook.disable();
3188                            Analytics.disable();
3189                            GAds.disable();
3190                            Pinterest.disable();
3191                            Bing.disable();
3192                            TikTok.disable();
3193
3194                            consent = {
3195                                facebook: false,
3196                                ga: false,
3197                                google_ads: false,
3198                                tiktok: false,
3199                                bing: false,
3200                                pinterest: false,
3201                                gtm: false,
3202                            };
3203                        }
3204                        Utils.setupGDPRData( consent );
3205
3206                    } );
3207
3208                    $( document ).onFirst( 'click', '.cn-revoke-cookie', function () {
3209                        Facebook.disable();
3210                        Analytics.disable();
3211                        GAds.disable();
3212                        Pinterest.disable();
3213                        Bing.disable();
3214                        TikTok.disable();
3215
3216                        let consent = {
3217                            facebook: false,
3218                            ga: false,
3219                            google_ads: false,
3220                            tiktok: false,
3221                            bing: false,
3222                            pinterest: false,
3223                            gtm: false,
3224                        };
3225                        Utils.setupGDPRData( consent );
3226
3227                    } );
3228                }
3229
3230                /**
3231                 * Cookie Law Info
3232                 */
3233                if ( options.gdpr.cookie_law_info_integration_enabled ) {
3234                    isConcent = true;
3235                    $( document ).onFirst( 'click', '#wt-cli-accept-all-btn,#cookie_action_close_header, .cky-btn-accept', function () {
3236                        Utils.initializeVideoAPIs( options );
3237                        setTimeout( function () {
3238                            let cli_cookie = Cookies.get( 'cookieyes-consent' ) ?? Cookies.get( 'wt_consent' ) ?? Cookies.get( 'viewed_cookie_policy' );
3239                            if ( typeof cli_cookie !== 'undefined' ) {
3240                                if (
3241                                    cli_cookie &&
3242                                    (cli_cookie === Cookies.get('cookieyes-consent') || cli_cookie === Cookies.get('wt_consent'))
3243                                ) {
3244                                    if (getCookieYes('analytics') === 'yes') {
3245                                        Utils.manageCookies();
3246                                    }
3247                                }
3247 else if ( cli_cookie === Cookies.get( 'viewed_cookie_policy' ) && cli_cookie == 'yes' ) {
3248                                    Utils.manageCookies();
3249                                }
3250                            }
3251                        }, 1000 )
3252                        Facebook.loadPixel();
3253                        Analytics.loadPixel();
3254                        GAds.loadPixel();
3255                        Pinterest.loadPixel();
3256                        Bing.loadPixel();
3257                        TikTok.loadPixel();
3258
3259                        let consent = {
3260                            facebook: true,
3261                            ga: true,
3262                            google_ads: true,
3263                            tiktok: true,
3264                            bing: true,
3265                            pinterest: true,
3266                            gtm: true,
3267                        };
3268                        Utils.setupGDPRData( consent );
3269                    } );
3270
3271                    $( document ).onFirst( 'click', '#cookie_action_close_header_reject, .cky-btn-reject', function () {
3272                        Facebook.disable();
3273                        Analytics.disable();
3274                        GAds.disable();
3275                        Pinterest.disable();
3276                        Bing.disable();
3277                        TikTok.disable();
3278
3279                        let consent = {
3280                            facebook: false,
3281                            ga: false,
3282                            google_ads: false,
3283                            tiktok: false,
3284                            bing: false,
3285                            pinterest: false,
3286                            gtm: false,
3287                        };
3288                        Utils.setupGDPRData( consent );
3289                    } );
3290                }
3291
3292                /**
3293                 * ConsentMagic
3294                 */
3295                if ( options.gdpr.consent_magic_integration_enabled && typeof CS_Data !== "undefined" ) {
3296                    isConcent = true;
3297                    let test_prefix = CS_Data.test_prefix,
3298                        cs_refresh_after_consent = false,
3299                        substring = "cs_enabled_cookie_term";
3300
3301                    if ( CS_Data.cs_refresh_after_consent == 1 ) {
3302                        cs_refresh_after_consent = CS_Data.cs_refresh_after_consent;
3303                    }
3304
3305                    let consent_actions = function () {
3306                        let theCookies = document.cookie.split( ';' );
3307
3308                        let consent = {
3309                            facebook: true,
3310                            ga: true,
3311                            google_ads: true,
3312                            tiktok: true,
3313                            bing: true,
3314                            pinterest: true,
3315                            gtm: true,
3316                            reddit: true,
3317                        };
3318
3319                        for ( let i = 1; i <= theCookies.length; i++ ) {
3320                            if ( theCookies[ i - 1 ].indexOf( substring ) !== -1 ) {
3321                                let categoryCookie = theCookies[ i - 1 ].replace( 'cs_enabled_cookie_term' + test_prefix + '_', '' );
3322                                categoryCookie = Number( categoryCookie.replace( /\D+/g, "" ) );
3323                                let cs_cookie_val = Cookies.get( 'cs_enabled_cookie_term' + test_prefix + '_' + categoryCookie );
3324                                if ( cs_cookie_val == 'yes' ) {
3325
3326                                    Utils.initializeVideoAPIs( options );
3327
3328                                    if ( ( categoryCookie === CS_Data.cs_script_cat.facebook ) || ( typeof CS_Data.cs_meta_ldu_mode !== "undefined" && CS_Data.cs_meta_ldu_mode ) ) {
3329                                        Facebook.loadPixel();
3330                                    }
3331
3332                                    if ( categoryCookie === CS_Data.cs_script_cat.bing || ( typeof CS_Data.cs_bing_consent_mode !== "undefined" && CS_Data.cs_bing_consent_mode.ad_storage.enabled ) ) {
3333                                        Bing.loadPixel();
3334                                    }
3335
3336                                    if ( categoryCookie === CS_Data.cs_script_cat.analytics || ( typeof CS_Data.cs_google_analytics_consent_mode !== "undefined" && CS_Data.cs_google_analytics_consent_mode == 1 ) ) {
3337                                        Analytics.loadPixel();
3338                                    }
3339                                    if ( categoryCookie === CS_Data.cs_script_cat.gads || ( typeof CS_Data.cs_google_ads_consent_mode !== "undefined" && CS_Data.cs_google_ads_consent_mode == 1 ) ) {
3340                                        GAds.loadPixel();
3341                                    }
3342
3343                                    if ( categoryCookie === CS_Data.cs_script_cat.pinterest ) {
3344                                        Pinterest.loadPixel();
3345                                    }
3346
3347                                    if ( categoryCookie === CS_Data.cs_script_cat.tiktok ) {
3348                                        TikTok.loadPixel();
3349                                    }
3350
3351                                    if ( ( categoryCookie === CS_Data.cs_script_cat.reddit ) || ( typeof CS_Data.cs_reddit_ldu_mode !== "undefined" && CS_Data.cs_reddit_ldu_mode ) ) {
3352                                        Reddit.loadPixel();
3353                                    }
3354                                } else {
3355                                    if ( ( categoryCookie === CS_Data.cs_script_cat.facebook ) && ( typeof CS_Data.cs_meta_ldu_mode == "undefined" || !CS_Data.cs_meta_ldu_mode ) ) {
3356                                        Facebook.disable();
3357                                        consent.facebook = false;
3358                                    }
3359
3360                                    if ( categoryCookie === CS_Data.cs_script_cat.bing && ( typeof CS_Data.cs_bing_consent_mode == "undefined" || !CS_Data.cs_bing_consent_mode.ad_storage.enabled ) ) {
3361                                        Bing.disable();
3362                                        consent.bing = false;
3363                                    }
3364
3365                                    if ( categoryCookie === CS_Data.cs_script_cat.analytics && ( typeof CS_Data.cs_google_analytics_consent_mode == "undefined" || CS_Data.cs_google_analytics_consent_mode == 0 ) ) {
3366                                        Analytics.disable();
3367                                        consent.ga = false;
3368                                        consent.gtm = false;
3369                                    }
3370
3371                                    if ( categoryCookie === CS_Data.cs_script_cat.gads && ( typeof CS_Data.cs_google_ads_consent_mode == "undefined" || CS_Data.cs_google_ads_consent_mode == 0 ) ) {
3372                                        GAds.disable();
3373                                        consent.google_ads = false;
3374                                    }
3375
3376                                    if ( categoryCookie === CS_Data.cs_script_cat.pinterest ) {
3377                                        Pinterest.disable();
3378                                        consent.pinterest = false;
3379                                    }
3380
3381                                    if ( categoryCookie === CS_Data.cs_script_cat.tiktok ) {
3382                                        TikTok.disable();
3383                                        consent.tiktok = false;
3384                                    }
3385
3386                                    if ( ( categoryCookie === CS_Data.cs_script_cat.reddit ) && ( typeof CS_Data.cs_reddit_ldu_mode == "undefined" || !CS_Data.cs_reddit_ldu_mode ) ) {
3387                                        Reddit.disable();
3388                                        consent.reddit = false;
3389                                    }
3390                                }
3391                                if ( Cookies.get( 'cs_enabled_advanced_matching' ) == 'yes' ) {
3392                                    Facebook.loadPixel();
3393                                }
3394                            }
3395                        }
3396
3397                        Utils.setupGDPRData( consent );
3398                    }
3399
3400                    if ( !cs_refresh_after_consent ) {
3401                        consent_actions();
3402
3403                        $( document ).on( 'click', '.cs_action_btn', function ( e ) {
3404                            e.preventDefault();
3405
3406                            let consent = {
3407                                facebook: true,
3408                                ga: true,
3409                                google_ads: true,
3410                                tiktok: true,
3411                                bing: true,
3412                                pinterest: true,
3413                                gtm: true,
3414                                reddit: true,
3415                            };
3416
3417                            let elm = $( this ),
3418                                button_action = elm.attr( 'data-cs_action' );
3419
3420                            if ( button_action === 'allow_all' ) {
3421
3422                                Utils.initializeVideoAPIs( options );
3423
3424                                Facebook.loadPixel();
3425                                Bing.loadPixel();
3426                                Analytics.loadPixel();
3427                                GAds.loadPixel();
3428                                Pinterest.loadPixel();
3429                                TikTok.loadPixel();
3430                                Reddit.loadPixel();
3431
3432                                consent.facebook = true;
3433                                consent.bing = true;
3434                                consent.ga = true;
3435                                consent.google_ads = true;
3436                                consent.pinterest = true;
3437                                consent.tiktok = true;
3438                                consent.reddit = true;
3439                                consent.gtm = true;
3440
3441                                Utils.setupGDPRData( consent );
3442
3443                            } else if ( button_action === 'disable_all' ) {
3444
3445                                if (typeof CS_Data.cs_meta_ldu_mode == "undefined" || CS_Data.cs_meta_ldu_mode == 0 ) {
3446                                    Facebook.disable();
3447                                    consent.facebook = false;
3448                                }
3449
3450                                if (typeof CS_Data.cs_bing_consent_mode == "undefined" || CS_Data.cs_bing_consent_mode.ad_storage.enabled == 0 ) {
3451                                    Bing.disable();
3452                                    consent.bing = false;
3453                                }
3454
3455                                if ( typeof CS_Data.cs_google_analytics_consent_mode == "undefined" || CS_Data.cs_google_analytics_consent_mode == 0 ) {
3456                                    Analytics.disable();
3457                                    consent.ga = false;
3458                                    consent.gtm = false;
3459                                }
3460
3461                                if ( typeof CS_Data.cs_google_ads_consent_mode == "undefined" || CS_Data.cs_google_ads_consent_mode == 0 ) {
3462                                    GAds.disable();
3463                                    consent.google_ads = false;
3464                                }
3465
3466                                if ( typeof CS_Data.cs_reddit_ldu_mode == "undefined" || CS_Data.cs_reddit_ldu_mode == 0 ) {
3467                                    Reddit.disable();
3468                                    consent.reddit = false;
3469                                }
3470
3471                                Pinterest.disable();
3472                                TikTok.disable();
3473
3474                                consent.pinterest = false;
3475                                consent.tiktok = false;
3476
3477                                Utils.setupGDPRData( consent );
3478                            } else if ( button_action === 'cs_confirm' ) {
3479                                consent_actions();
3480                            }
3481                        } );
3482                    }
3483                }
3484
3485                /**
3486                 * Real Cookie Banner
3487                 */
3488                if ( options.gdpr.real_cookie_banner_integration_enabled ) {
3489                    isConcent = true;
3490                    let consentApi = window.consentApi;
3491                    if ( consentApi ) {
3492                        consentApi.consent( "http", "_ga", "*" )
3493                            .then( Analytics.loadPixel.bind( Analytics ), Analytics.disable.bind( Analytics ) );
3494                        consentApi.consent( "http", "1P_JAR", ".google.com" )
3495                            .then( GAds.loadPixel.bind( GAds ), GAds.disable.bind( GAds ) );
3496                        consentApi.consent( "http", "_fbp", "*" )
3497                            .then( Facebook.loadPixel.bind( Facebook ), Facebook.disable.bind( Facebook ) );
3498                        consentApi.consent( "http", "_pinterest_sess", ".pinterest.com" )
3499                            .then( Pinterest.loadPixel.bind( Pinterest ), Pinterest.disable.bind( Pinterest ) );
3500                        consentApi.consent( "http", "_uetsid", "*" )
3501                            .then( Bing.loadPixel.bind( Bing ), Bing.disable.bind( Bing ) );
3502                        consentApi.consent( "http", "tt_webid_v2", ".tiktok.com" )
3503                            .then( TikTok.loadPixel.bind( TikTok ), TikTok.disable.bind( TikTok ) );
3504
3505                        let consent = {
3506                            facebook: true,
3507                            ga: true,
3508                            google_ads: true,
3509                            tiktok: true,
3510                            bing: true,
3511                            pinterest: true,
3512                            gtm: true,
3513                        };
3514
3515                        if (!consentApi.consentSync("http", "_ga", "*").cookieOptIn) {
3516                            consent.ga = false;
3517                            consent.gtm = false;
3518                        }
3519                        if (!consentApi.consentSync("http", "_fbp", "*").cookieOptIn) {
3520                            consent.facebook = false;
3521                        }
3522                        if (!consentApi.consentSync("http", "_pinterest_sess", ".pinterest.com").cookieOptIn) {
3523                            consent.pinterest = false;
3524                        }
3525                        if (!consentApi.consentSync("http", "_uetsid", "*").cookieOptIn) {
3526                            consent.bing = false;
3527                        }
3528                        if (!consentApi.consentSync("http", "1P_JAR", ".google.com").cookieOptIn) {
3529                            consent.google_ads = false;
3530                        }
3531                        if (!consentApi.consentSync("http", "tt_webid_v2", ".tiktok.com").cookieOptIn) {
3532                            consent.tiktok = false;
3533                        }
3534                        Utils.setupGDPRData( consent );
3535
3536                        // load WatchVideo event APIs for Auto
3537                        /**
3538                         * Real Cookie Banner.
3539                         */
3540                        if ( options.hasOwnProperty( 'automatic' ) && options.automatic.enable_video && options.automatic.enable_youtube ) {
3541                            consentApi.consent( "http", "CONSENT", ".youtube.com" ).then( Utils.initYouTubeAPI );
3542                        }
3543
3544                        if ( options.hasOwnProperty( 'automatic' ) && options.automatic.enable_video && options.automatic.enable_vimeo ) {
3545                            consentApi.consent( "http", "player", ".vimeo.com" ).then( Utils.initVimeoAPI );
3546                        }
3547                    }
3548                }
3549
3550                if ( !isConcent ) {
3551                    Utils.initializeVideoAPIs( options );
3552                }
3553            },
3554
3555            setupGDPRData: function ( consent ) {
3556                consent = window.btoa( JSON.stringify( consent ) );
3557                Cookies.set( 'pys_consent', consent, { expires: 365, path: '/', domain: 
3557domain } );
3558            },
3559
3560            /**
3561             * Enrich
3562             */
3563            isCheckoutPage: function () {
3564                return $('body').hasClass('woocommerce-checkout') || document.querySelector('.woocommerce-checkout') ||
3565                    $('body').hasClass('edd-checkout');
3566            },
3567            addCheckoutFields : function() {
3568                var utm = "";
3569                var utms = getUTMs()
3570                $.each(utmTerms, function (index, name) {
3571                    if(index > 0) {
3572                        utm+="|";
3573                    }
3574                    utm+=name+":"+utms[name];
3575                });
3576                var utmIdList = "";
3577                var utmsIds = getUTMId()
3578                $.each(utmId, function (index, name) {
3579                    if(index > 0) {
3580                        utmIdList+="|";
3581                    }
3582                    utmIdList+=name+":"+utmsIds[name];
3583                });
3584                var utmIdListLast = "";
3585                var utmsIdsLast = getUTMId(true)
3586                $.each(utmId, function (index, name) {
3587                    if(index > 0) {
3588                        utmIdListLast+="|";
3589                    }
3590                    utmIdListLast+=name+":"+utmsIdsLast[name];
3591                });
3592
3593
3594                var utmLast = "";
3595                var utmsLast = getUTMs(true)
3596                $.each(utmTerms, function (index, name) {
3597                    if(index > 0) {
3598                        utmLast+="|";
3599                    }
3600                    utmLast+=name+":"+utmsLast[name];
3601                });
3602
3603                var dateTime = getDateTime();
3604                var landing = Cookies.get('pys_landing_page');
3605                var lastLanding = Cookies.get('last_pys_landing_page');
3606                var trafic = Cookies.get('pysTrafficSource');
3607                var lastTrafic = Cookies.get('last_pysTrafficSource');
3608
3609                var $form = null;
3610                if($('body').hasClass('woocommerce-checkout')) {
3611                    $form = $("form.woocommerce-checkout");
3612                } else {
3613                    $form = $("#edd_purchase_form");
3614                }
3615                var inputs = {'pys_utm':utm,
3616                    'pys_utm_id':utmIdList,
3617                    'pys_browser_time':dateTime.join("|"),
3618                    'pys_landing':landing,
3619                    'pys_source':trafic,
3620                    'pys_order_type': $(".wcf-optin-form").length > 0 ? "wcf-optin" : "normal",
3621
3622                    'last_pys_landing':lastLanding,
3623                    'last_pys_source':lastTrafic,
3624                    'last_pys_utm':utmLast,
3625                    'last_pys_utm_id':utmIdListLast,
3626                }
3627
3628                Object.keys(inputs).forEach(function(key,index) {
3629                    $form.append("<input type='hidden' name='"+key+"' value='"+inputs[key]+"' /> ");
3630                });
3631
3632
3633            },
3634
3635            /**
3636             * Advanced From Data
3637             */
3638            saveAdvancedFormData: function ( email, phone, firstName, lastName, override = true ) {
3639
3640                if ( !options.cookie.disabled_advanced_form_data_cookie ) {
3641                    let data = Utils.getAdvancedFormData();
3642                    // Ensure data["address"] is an object
3643
3644                    if ( email != null ) {
3645                        if ( !override ) {
3646                            if ( typeof data[ "email" ] === 'undefined' || !data[ "email" ] ) {
3647                                data[ "email" ] = email;
3648                            }
3649                        } else {
3650                            data[ "email" ] = email;
3651                        }
3652
3653                        if ( typeof data[ "emails" ] === 'object' ) {
3654                            if ( override ) {
3655                                data[ "emails" ] = [ email, ...Object.values( data[ "emails" ] ) ];
3656                            } else {
3657                                data[ "emails" ] = [ ...Object.values( data[ "emails" ] ), email ];
3658                            }
3659                        } else {
3660                            data[ "emails" ] = [ email ];
3661                        }
3662                        data[ "emails" ] = [ ...new Set( data[ "emails" ] ) ];
3663                        data[ "emails" ] = data[ "emails" ].slice( 0, 3 );
3664                    }
3665
3666                    if ( phone != null ) {
3667                        if ( !override ) {
3668                            if ( typeof data[ "phone" ] === 'undefined' || !data[ "phone" ] ) {
3669                                data[ "phone" ] = phone;
3670                            }
3671                        } else {
3672                            data[ "phone" ] = phone;
3673                        }
3674
3675                        if ( typeof data[ "phones" ] === 'object' ) {
3676                            if ( override ) {
3677                                data[ "phones" ] = [ phone, ...Object.values( data[ "phones" ] ) ];
3678                            } else {
3679                                data[ "phones" ] = [ ...Object.values( data[ "phones" ] ), phone ];
3680                            }
3681                        } else {
3682                            data[ "phones" ] = [ phone ];
3683                        }
3684                        data[ "phones" ] = [ ...new Set( data[ "phones" ] ) ];
3685                        data[ "phones" ] = data[ "phones" ].slice( 0, 3 );
3686                    }
3687
3688                    if ( firstName != null ) {
3689                        if ( !override ) {
3690                            if ( typeof data[ "first_name" ] === 'undefined' || !data[ "first_name" ] ) {
3691                                data[ "first_name" ] = firstName;
3692                            }
3693                        } else {
3694                            data[ "first_name" ] = firstName;
3695                        }
3696
3697                        if ( typeof data[ "fns" ] === 'object' ) {
3698                            if ( override ) {
3699                                data[ "fns" ] = [ firstName, ...Object.values( data[ "fns" ] ) ];
3700                            } else {
3701                                data[ "fns" ] = [ ...Object.values( data[ "fns" ] ), firstName ];
3702                            }
3703                        } else {
3704                            data[ "fns" ] = [ firstName ];
3705                        }
3706                        data[ "fns" ] = [ ...new Set( data[ "fns" ] ) ];
3707                        data[ "fns" ] = data[ "fns" ].slice( 0, 2 );
3708                    }
3709
3710                    if ( lastName != null ) {
3711                        if ( !override ) {
3712                            if ( typeof data[ "last_name" ] === 'undefined' || !data[ "last_name" ] ) {
3713                                data[ "last_name" ] = lastName;
3714                            }
3715                        } else {
3716                            data[ "last_name" ] = lastName;
3717                        }
3718
3719                        if ( typeof data[ "lns" ] === 'object' ) {
3720                            if ( override ) {
3721                                data[ "lns" ] = [ lastName, ...Object.values( data[ "lns" ] ) ];
3722                            } else {
3723                                data[ "lns" ] = [ ...Object.values( data[ "fns" ] ), lastName ];
3724                            }
3725                        } else {
3726                            data[ "lns" ] = [ lastName ];
3727                        }
3728                        data[ "lns" ] = [ ...new Set( data[ "lns" ] ) ];
3729                        data[ "lns" ] = data[ "lns" ].slice( 0, 2 );
3730                    }
3731
3732                    Cookies.set( 'pys_advanced_form_data', JSON.stringify( data ), { expires: 300,path: '/',domain: domain } );
3733                } else {
3734                    Cookies.remove( 'pys_advanced_form_data', { path: '/',domain: domain } )
3735                }
3736                if(GTM.isEnabled()){
3737                    GTM.updateEnhancedConversionData();
3738                }
3739                if(Analytics.isEnabled()){
3740                    Analytics.updateEnhancedConversionData();
3741                } else if ( GAds.isEnabled() ) {
3742                    GAds.updateEnhancedConversionData();
3743                }
3744                if ( Bing.isEnabled() && typeof Bing.updateEnhancedConversionData === 'function' ) {
3745                    Bing.updateEnhancedConversionData();
3746                }
3747            },
3748            getAdvancedMergeFormData: function (allways_provided_data = false) {
3749                const advanced = Utils.getAdvancedFormData();
3750                let mergedData = {},
3751                    limit = options.tracking_analytics.use_multiple_provided_data || allways_provided_data ? 3 : 1,
3752                    address_limit = options.tracking_analytics.use_multiple_provided_data || allways_provided_data ? 2 : 1;
3753
3754                if ( options.tracking_analytics.use_encoding_provided_data || allways_provided_data) {
3755                    mergedData.sha256_email_address = [];
3756                    mergedData.sha256_phone_number = [];
3757                    mergedData.address = [];
3758                    if ( options.tracking_analytics.hasOwnProperty( "userData" ) ) {
3759                        if ( options.tracking_analytics.userData.hasOwnProperty( "emails" ) && Object.keys( options.tracking_analytics.userData.emails ).length > 0 ) {
3760                            Object.keys( options.tracking_analytics.userData.emails ).forEach( ( key ) => {
3761                                mergedData.sha256_email_address.push( sha256( options.tracking_analytics.userData.emails[ key ] ) );
3762                            } )
3763                        }
3764                        if ( options.tracking_analytics.userData.hasOwnProperty( "phones" ) && Object.keys( options.tracking_analytics.userData.phones ).length > 0 ) {
3765                            Object.keys( options.tracking_analytics.userData.phones ).forEach( ( key ) => {
3766                                mergedData.sha256_phone_number.push( sha256( options.tracking_analytics.userData.phones[ key ] ) );
3767                            } )
3768                        }
3769                        if ( options.tracking_analytics.userData.hasOwnProperty( "addresses" ) && Object.keys( options.tracking_analytics.userData.addresses ).length > 0 ) {
3770                            Object.keys( options.tracking_analytics.userData.addresses ).forEach( ( key ) => {
3771                                let address = Object.assign( {}, options.tracking_analytics.userData.addresses[ key ] );
3772
3773                                if ( address.first_name ) {
3774                                    address.sha256_first_name = address.first_name !== '' ? sha256( address.first_name ) : '';
3775                                    delete address.first_name;
3776                                }
3777                                if ( address.last_name ) {
3778                                    address.sha256_last_name = address.last_name !== '' ? sha256( address.last_name ) : '';
3779                                    delete address.last_name;
3780                                }
3781                                mergedData.address.push( address );
3782                            } )
3783                        }
3784                    }
3785
3786                    let data_persistency = options.data_persistency;
3787
3788                    //emails
3789                    if ( advanced.emails && Object.keys( advanced.emails ).length > 0 ) {
3790                        Object.keys( advanced.emails ).forEach( ( ke
3790y ) => {
3791                            if ( data_persistency === 'recent_data' ) {
3792                                mergedData.sha256_email_address.push( sha256( advanced.emails[ key ] ) );
3793                            } else {
3794                                mergedData.sha256_email_address.unshift( sha256( advanced.emails[ key ] ) );
3795                            }
3796                        } )
3797                    }
3798                    if ( advanced.email && advanced.email !== '' ) {
3799                        if ( data_persistency === 'recent_data' ) {
3800                            mergedData.sha256_email_address.push( sha256( advanced.email ) );
3801                        } else {
3802                            mergedData.sha256_email_address.unshift( sha256( advanced.email ) );
3803                        }
3804                    }
3805                    mergedData.sha256_email_address = [ ...new Set( mergedData.sha256_email_address ) ];
3806                    mergedData.sha256_email_address = mergedData.sha256_email_address.slice( 0, limit );
3807
3808                    //phones
3809                    if ( advanced.phones && Object.keys( advanced.phones ).length > 0 ) {
3810                        Object.keys( advanced.phones ).forEach( ( key ) => {
3811                            if ( data_persistency === 'recent_data' ) {
3812                                mergedData.sha256_phone_number.push( sha256( advanced.phones[ key ] ) );
3813                            } else {
3814                                mergedData.sha256_phone_number.unshift( sha256( advanced.phones[ key ] ) );
3815                            }
3816                        } )
3817                    }
3818                    if ( advanced.phone && advanced.phone !== '' ) {
3819                        if ( data_persistency === 'recent_data' ) {
3820                            mergedData.sha256_phone_number.push( sha256( advanced.phone ) );
3821                        } else {
3822                            mergedData.sha256_phone_number.unshift( sha256( advanced.phone ) );
3823                        }
3824                    }
3825                    mergedData.sha256_phone_number = [ ...new Set( mergedData.sha256_phone_number ) ];
3826                    mergedData.sha256_phone_number = mergedData.sha256_phone_number.slice( 0, limit );
3827
3828                    //first and last names
3829                    if ( advanced.fns && Object.keys( advanced.fns ).length > 0 ) {
3830                        Object.entries( advanced.fns ).forEach( ( item ) => {
3831                            if ( mergedData.address.length < address_limit ) {
3832                                let first_name = item[ 1 ];
3833                                let last_name = ( advanced.lns && Object.keys( advanced.lns ).length > 0 ) ? ( advanced.lns[ item[ 0 ] ] ? advanced.lns[ item[ 0 ] ] : advanced.lns[ 0 ] ) : '';
3834                                let address = {
3835                                    sha256_first_name: first_name !== '' ? sha256( first_name ) : '',
3836                                    sha256_last_name: last_name !== '' ? sha256( last_name ) : '',
3837                                    // Add other address properties here if they exist in advanced
3838                                };
3839                                mergedData.address.push( address );
3840                            }
3841                        } )
3842                    }
3843
3844                    if ( advanced.first_name || advanced.last_name ) {
3845                        if ( mergedData.address.length < address_limit ) {
3846                            let address = {
3847                                sha256_first_name: advanced.first_name !== '' ? sha256( advanced.first_name ) : '',
3848                                sha256_last_name: advanced.last_name !== '' ? sha256( advanced.last_name ) : '',
3849                                // Add other address properties here if they exist in advanced
3850                            };
3851                            mergedData.address.push( address );
3852                        }
3853                    }
3854                } else {
3855                    mergedData.email = [];
3856                    mergedData.phone_number = [];
3857                    mergedData.address = [];
3858                    if ( options.tracking_analytics.hasOwnProperty( "userData" ) ) {
3859                        if ( options.tracking_analytics.userData.hasOwnProperty( "emails" ) && Object.keys( options.tracking_analytics.userData.emails ).length > 0 ) {
3860                            Object.keys( options.tracking_analytics.userData.emails ).forEach( ( ke
3860y ) => {
3861                                mergedData.email.push( options.tracking_analytics.userData.emails[ key ] );
3862                            } )
3863                        }
3864                        if ( options.tracking_analytics.userData.hasOwnProperty( "phones" ) && Object.keys( options.tracking_analytics.userData.phones ).length > 0 ) {
3865                            Object.keys( options.tracking_analytics.userData.phones ).forEach( ( key ) => {
3866                                mergedData.phone_number.push( options.tracking_analytics.userData.phones[ key ] );
3867                            } )
3868                        }
3869                        if ( options.tracking_analytics.userData.hasOwnProperty( "addresses" ) && Object.keys( options.tracking_analytics.userData.addresses ).length > 0 ) {
3870                            Object.keys( options.tracking_analytics.userData.addresses ).forEach( ( key ) => {
3871                                mergedData.address.push( options.tracking_analytics.userData.addresses[ key ] );
3872                            } )
3873                        }
3874                    }
3875
3876                    let data_persistency = options.data_persistency;
3877
3878                    //emails
3879                    if ( advanced.emails && Object.keys( advanced.emails ).length > 0 ) {
3880                        Object.keys( advanced.emails ).forEach( ( key ) => {
3881                            if ( data_persistency === 'recent_data' ) {
3882                                mergedData.email.push( advanced.emails[ key ] );
3883                            } else {
3884                                mergedData.email.unshift( advanced.emails[ key ] );
3885                            }
3886                        } )
3887                    }
3888                    if ( advanced.email && advanced.email !== '' ) {
3889                        if ( data_persistency === 'recent_data' ) {
3890                            mergedData.email.push( advanced.email );
3891                        } else {
3892                            mergedData.email.unshift( advanced.email );
3893                        }
3894                    }
3895                    mergedData.email = [ ...new Set( mergedData.email ) ];
3896                    mergedData.email = mergedData.email.slice( 0, limit );
3897
3898                    //phones
3899                    if ( advanced.phones && Object.keys( advanced.phones ).length > 0 ) {
3900                        Object.keys( advanced.phones ).forEach( ( key ) => {
3901                            if ( data_persistency === 'recent_data' ) {
3902                                mergedData.phone_number.push( advanced.phones[ key ] );
3903                            } else {
3904                                mergedData.phone_number.unshift( advanced.phones[ key ] );
3905                            }
3906                        } )
3907                    }
3908                    if ( advanced.phone && advanced.phone !== '' ) {
3909                        if ( data_persistency === 'recent_data' ) {
3910                            mergedData.phone_number.push( advanced.phone );
3911                        } else {
3912                            mergedData.phone_number.unshift( advanced.phone );
3913                        }
3914                    }
3915                    mergedData.phone_number = [ ...new Set( mergedData.phone_number ) ];
3916                    mergedData.phone_number = mergedData.phone_number.slice( 0, limit );
3917
3918                    //first and last names
3919                    if ( advanced.fns && Object.keys( advanced.fns ).length > 0 ) {
3920                        Object.entries( advanced.fns ).forEach( ( item ) => {
3921                            if ( mergedData.address.length < address_limit ) {
3922                                let first_name = item[ 1 ];
3923                                let last_name = ( advanced.lns && Object.keys( advanced.lns ).length > 0 ) ? ( advanced.lns[ item[ 0 ] ] ? advanced.lns[ item[ 0 ] ] : advanced.lns[ 0 ] ) : '';
3924                                let address = {
3925                                    first_name: first_name || '',
3926                                    last_name: last_name || '',
3927                                    // Add other address properties here if they exist in advanced
3928                                };
3929                                mergedData.address.push( address );
3930                            }
3931                        } )
3932                    }
3933
3934                    if ( advanced.first_name || advanced.last_name ) {
3935                        if ( mergedData.address.length < address_limit ) {
3936                            let address = {
3937                                first_name: advanced.first_name || '',
3938                                last_name: advanced.last_name || '',
3939                                // Add other address properties here if they exist in advanced
3940                            };
3941                            mergedData.address.push( address );
3942                        }
3943                    }
3944                }
3945
3946                Utils.removeEmptyProperties( mergedData );
3947                return mergedData;
3948            },
3949            removeEmptyProperties : function(obj) {
3950
3951                if (obj.hasOwnProperty('address')) {
3952                    // Iterate over each array in 'address'
3953                    for (let i = 0; i < obj['address'].length; i++) {
3954                        let address1 = obj['address'][i];
3955
3956                        // Compare with every other array in 'address'
3957                        for (let j = i + 1; j < obj['address'].length; j++) {
3958                            let address2 = obj['address'][j];
3959
3960                            // If duplicate properties are found, remove them from address2
3961                            for (let prop in address1) {
3962                                if (address1[prop] === address2[prop]) {
3963                                    delete address2[prop];
3964                                }
3965                            }
3966                        }
3967                    }
3968                }
3969
3970                for (let key in obj) {
3971                    if (obj[key] === null || obj[key] === undefined || obj[key] === '') {
3972                        delete obj[key];
3973                    } else if (Array.isArray(obj[key])) {
3974                        obj[key] = [...new Set(obj[key])]; // Remove duplicates from the array
3975                        obj[key] = obj[key].filter(item => item !== null && item !== undefined && item !== '');
3976                        // Process objects inside arrays
3977                        obj[key] = obj[key].map(item => {
3978                            if (item === null) {
3979                                return; // Skip null elements
3980                            } else if (Array.isArray(item)) {
3981                                return this.removeEmptyProperties(item);
3982                            } else if (typeof item === 'object') {
3983                                let cleanedItem = this.removeEmptyProperties(item);
3984                                if (Object.keys(cleanedItem).length > 0) {
3985                                    return cleanedItem;
3986                                }
3987                            } else {
3988                                return item;
3989                            }
3990                        });
3991                        // Remove empty objects and null values from the array
3992                        obj[key] = obj[key].filter(item => item !== undefined && !(typeof item === 'object' && Object.keys(item).length === 0));
3993                        if (obj[key].length === 0) {
3994                            delete obj[key];
3995                        }
3996                    } else if (typeof obj[key] === 'object') {
3997                        this.removeEmptyProperties(obj[key]);
3998                        if (Object.keys(obj[key]).length === 0) {
3999                            delete obj[key];
4000                        }
4001                    }
4002                }
4003                return obj;
4004            },
4005            getAdvancedFormData: function () {
4006                let dataStr = Cookies.get("pys_advanced_form_data");
4007                if(dataStr === undefined) {
4008                    return {'first_name':"",'last_name':"",'email':"",'phone':"",'fns':[],'lns':[],'emails':[],'phones':[]};
4009                } else {
4010                    return JSON.parse(dataStr);
4011                }
4012            },
4013
4014            getFormFilledData: function ( event ) {
4015                // First, resolve static/dynamic parameters
4016                if (event.params && Object.keys(event.params).length > 0) {
4017                    Object.entries(event.params).forEach(([key, value]) => {
4018                        // Resolve parameter value (static or dynamic)
4019                        const resolvedValue = resolveParamValue(value, key);
4020                        if (resolvedValue !== null) {
4021                            event.params[key] = resolvedValue;
4022                        }
4023                        else {
4024                            delete event.params[key];
4025                        }
4026                    });
4027                }
4028
4029                // Then, handle dynamic fields from cookies (existing logic)
4030
4031                if ( Object.keys(options.track_dynamic_fields).length > 0 && Object.keys(event.params).length > 0 ) {
4032                    Object.entries(event.params).forEach((item) => {
4033
4034                        if ( options.track_dynamic_fields.hasOwnProperty(item[0]) ) {
4035                            let fieldData = Cookies.get('pys_dyn_field_' + item[1] );
4036
4037                            if(fieldData !== undefined && fieldData !== '') {
4038                                event.params[item[0]] = fieldData;
4039                            } else {
4040                                delete event.params[item[0]];
4041                            }
4042                        }
4043                    })
4044                }
4045                return event;
4046            },
4047
4048            registerGadsPhoneConversion( conversion_id ) {
4049                // Ads phone call conversion
4050                if ( options.triggerEventTypes.hasOwnProperty( 'gads_phone_conversion' ) ) {
4051                    Object.entries( options.triggerEventTypes.gads_phone_conversion ).forEach( function ( [ trigger_id, data ] ) {
4052                        if ( data.enabled && Object.entries( data.numbers ).length > 0 ) {
4053
4054                            let allowed_pixels = [];
4055                            Object.entries( options.triggerEvents[ trigger_id ] ).forEach( function ( [ pixel, pixel_data ] ) {
4056                                if ( pixel !== 'google_ads' ) {
4057                                    return;
4058                                }
4059
4060                                allowed_pixels.push( ...pixel_data.conversion_ids );
4061                            } )
4062
4063                            if ( !allowed_pixels.includes( conversion_id ) ) {
4064                                return;
4065                            }
4066
4067                            data.numbers.forEach( function ( pair ) {
4068                                if ( !pair.number || !pair.label ) {
4069                                    return;
4070                                }
4071
4072                                let phoneCb = function () {
4073                                    Utils.fireTriggerEvent( trigger_id )
4074                                };
4075
4076                                Utils.gtagPush( 'config', conversion_id + '/' + pair.label, {
4077                                    phone_conversion_number: pair.number,
4078                                    phone_conversion_callback: phoneCb
4079                                } );
4080                            } )
4081                        }
4082                    } );
4083                }
4084            }
4085        }
4086
4087
4088    }(options);
4089
4090    var TikTok = function (options) {
4091
4092        var initialized = false;
4093
4094        function fireEvent(name, event) {
4095
4096            if(typeof window.pys_event_data_filter === "function" && window.pys_disable_event_filter(name,'tiktok')) {
4097                return;
4098            }
4099
4100            var data = event.params;
4101
4102            var ids = event.pixelIds.filter(function (pixelId) {
4103                return !Utils.hideMatchingPixel(pixelId, 'tiktok');
4104            })
4105            var params = {};
4106            Utils.copyProperties(data, params);
4107
4108            params.eventID = Utils.generateUniqueId(event);
4109
4110            if(ids.length > 0){
4111                TikTok.fireEventAPI(name, event, params);
4112            }
4113            ids.forEach(function(pixelId){
4114                if (options.debug) {
4115                    console.log('[TikTok] ' + name, params,"pixel_id",pixelId);
4116                }
4117
4118                ttq.instance(pixelId).track(name,params)
4119
4120            });
4121
4122            var customEvent = new CustomEvent('tiktok_event_sent', {
4123                detail: {
4124                    eventName: name,
4125                    params: params,
4126                    pixel_ids: ids
4127                }
4128            });
4129            window.dispatchEvent(customEvent);
4130        }
4131
4132        return {
4133            tag: function() {
4134                return "tiktok";
4135            },
4136            isEnabled: function () {
4137                return options.hasOwnProperty('tiktok');
4138            },
4139            getHidePixel: function(){
4140                if(this.isEnabled() && options.tiktok.hasOwnProperty('hide_pixels'))
4141                {
4142                    return options.tiktok.hide_pixels;
4143                }
4144                return [];
4145            },
4146            disable: function () {
4147                initialized = false;
4148            },
4149
4150            loadPixel:function () {
4151                if (initialized || !this.isEnabled() || !Utils.consentGiven('tiktok')) {
4152                    return;
4153                }
4154                for (var i = 0; i < options.tiktok.pixelIds.length; i++) {
4155                    var trackingId = options.tiktok.pixelIds[i];
4156                    if (!Utils.hideMatchingPixel(trackingId, 'tiktok')) {
4157                        !function (w, d, t) {
4158                            w.TiktokAnalyticsObject=t;
4159                            var ttq=w[t]=w[t]||[];
4160                            ttq.methods=["page","track","identify","instances","debug","on","off","once","ready","alias","group","enableCookie","disableCookie"];
4161                            ttq.setAndDefer=function(t,e){t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}};
4162                            for(var i=0;i<ttq.methods.length;i++)
4163                                ttq.setAndDefer(ttq,ttq.methods[i]);
4164                            ttq.instance=function(t){for(var e=ttq._i[t]||[],n=0;n<ttq.methods.length;n++)ttq.setAndDefer(e,ttq.methods[n]);return e};
4165                            ttq.load=function(e,n){var i="https://analytics.tiktok.com/i18n/pixel/events.js";ttq._i=ttq._i||{},ttq._i[e]=[],ttq._i[e]._u=i,ttq._t=ttq._t||{},ttq._t[e]=+new Date,ttq._o=ttq._o||{},ttq._o[e]=n||{};var o=document.createElement("script");o.type="text/javascript",o.async=!0,o.src=i+"?sdkid="+e+"&lib="+t;var a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(o,a)};
4166
4167                            //ttq.load('C60QSCQRVDG9JAKNPK2G');
4168                            //ttq.page();
4169                        }(window, document, 'ttq');
4170                        break;
4171                    }
4172                }
4173
4174
4175                var ids = options.tiktok.pixelIds.filter(function (pixelId) {
4176                    return !Utils.hideMatchingPixel(pixelId, 'tiktok');
4177                })
4178                ids.forEach(function (pixelId) {
4179
4180                    ttq.load(pixelId);
4181                    ttq.page();
4182                    let advancedMatching = {};
4183                    if(options.tiktok.hasOwnProperty('advanced_matching')
4184                        && Object.keys(options.tiktok.advanced_matching).length > 0) {
4185                        advancedMatching = options.tiktok.advanced_matching;
4186                        if(!advancedMatching.hasOwnProperty("external_id")){
4187                            if (Cookies.get('pbid')) {
4188                                advancedMatching["external_id"] = Cookies.get('pbid');
4189                            }
4190                        }
4191                        else if(advancedMatching.hasOwnProperty("external_id") && advancedMatching.external_id != Cookies.get('pbid'))
4192                        {
4193                            advancedMatching["external_id"] = Cookies.get('pbid') ? Cookies.get('pbid') : advancedMatching.external_id;
4194                        }
4195
4196                        ttq.instance(pixelId).identify(advancedMatching)
4197                    }
4198                });
4199                initialized = true;
4200
4201                if (options.staticEvents.hasOwnProperty('tiktok')) {
4202                    var timeout = 1;
4203                    setTimeout(function (timeout) {
4204                        Utils.fireStaticEvents('tiktok', timeout);
4205                    }, 1500, timeout)
4206                }
4207            },
4208
4209            fireEvent: function (name, data) {
4210                if (!initialized || !this.isEnabled()) {
4211                    return false;
4212                }
4213                data.delay = data.delay || 0;
4214
4215                if (data.delay === 0) {
4216                    fireEvent(name, data);
4217                } else {
4218                    setTimeout(function (name, params) {
4219                        fireEvent(name, params);
4220                    }, data.delay * 1000, name, data);
4221                }
4222                return true;
4223            },
4224
4225            fireEventAPI: function (name, event, params) {
4226
4227                var ids = event.pixelIds.filter(function (pixelId) {
4228                    return !Utils.hideMatchingPixel(pixelId, 'tiktok');
4229                })
4230                var notCachedEventsIds = new Array();
4231                var isAddToCartFromJs =  options.woo.hasOwnProperty("addToCartCatchMethod")
4232                    && options.woo.addToCartCatchMethod === "add_cart_js";
4233                if(!isAddToCartFromJs) {
4234                    notCachedEventsIds.push('woo_add_to_cart_on_button_click')
4235                }
4236
4237                if(options.tiktok.serverApiEnabled) {
4238                    if(!notCachedEventsIds.includes(event.e_id)) {
4239
4240                        var isApiDisabled = options.gdpr.all_disabled_by_api ||
4241                            options.gdpr.tiktok_disabled_by_api ||
4242                            options.gdpr.cookiebot_integration_enabled ||
4243                            options.gdpr.cookie_notice_integration_enabled ||
4244                            options.gdpr.consent_magic_integration_enabled ||
4245                            options.gdpr.cookie_law_info_integration_enabled;
4246                        // Update eventID
4247
4248                        // send event from server if they were block by gdpr or need send with delay
4249                        if( options.ajaxForServerEvent || isApiDisabled || event.delay > 0 || event.type !== "static" ){
4250
4251                            var json = {
4252                                action: 'pys_tiktok_api_event',
4253                                pixel: TikTok.tag(),
4254                                event: name,
4255                                event_slug: event.e_id,
4256                                ids: ids,
4257                                data:params,
4258                                url:window.location.href,
4259                                event_id:params.eventID,
4260                                ajax_event:options.ajax_event
4261                            };
4262
4263                            let delay = 0;
4264                            try {
4265                                if (sessionStorage) {
4266                                    Object.keys(sessionStorage)
4267                                        .filter(key => key.startsWith('wc_fragments_'))
4268                                        .forEach((key) => {
4269                                            let value = sessionStorage.getItem(key);
4270                                            if (event.e_id === 'woo_add_to_cart_on_button_click') {
4271                                                delay = 2000;
4272                                            }
4273                                        });
4274                                }
4275                            } catch (error) {
4276                                console.error('sessionStorage is not accessible:', error);
4277                            }
4278
4279
4280                            if(event.hasOwnProperty('woo_order')) {
4281                                json['woo_order'] = event.woo_order;
4282                            }
4283
4284                            if(event.hasOwnProperty('edd_order')) {
4285                                json['edd_order'] = event.edd_order;
4286                            }
4287
4288                            // Forward the order key so the server can bind the
4289                            // purchase event to its specific order (RestPurchaseGuard).
4290                            if(event.hasOwnProperty('order_key') && event.order_key) {
4291                                json['order_key'] = event.order_key;
4292                            }
4293                            if (event.e_id === "automatic_event_internal_link" || event.e_id === "automatic_event_outbound_link" || name == 'PageView') {
4294                                delay = delay === 0 ? 500 : delay;
4295                                setTimeout(() => Utils.sendServerAjaxRequest(options.ajaxUrl, json), delay);
4296                            } else if (event.type != 'static') {
4297                                setTimeout(() => Utils.sendServerAjaxRequest(options.ajaxUrl, json), delay);
4298                            }
4299
4300                            if ( ( event.type == 'static' && options.ajaxForServerStaticEvent ) || ( event.hasOwnProperty( 'ajaxFire' ) && event.ajaxFire ) ) {
4301                                setTimeout( () => Utils.sendServerAjaxRequest( options.ajaxUrl, json ), delay );
4302                            }
4303                        }
4304                    }
4305                }
4306            },
4307
4308            onClickEvent: function (event) {
4309                this.fireEvent(event.name, event);
4310            },
4311
4312            onWooAddToCartOnSingleEvent: function (product_id, qty, product_type, is_external, $form) {
4313
4314                window.pysWooProductData = window.pysWooProductData || [];
4315                if (!options.dynamicEvents.woo_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
4316                    return;
4317
4318                if (window.pysWooProductData.hasOwnProperty(product_id)) {
4319                    if (window.pysWooProductData[product_id].hasOwnProperty(this.tag())) {
4320
4321                        var event = Utils.clone(options.dynamicEvents.woo_add_to_cart_on_button_click[this.tag()]);
4322
4323                        Utils.copyProperties(window.pysWooProductData[product_id][this.tag()]['params'], event.params);
4324
4325                        // maybe customize value option
4326                        if (options.woo.addToCartOnButtonValueEnabled && options.woo.addToCartOnButtonValueOption !== 'global') {
4327
4328                            if (product_type === Utils.PRODUCT_BUNDLE) {
4329                                var data = $(".bundle_form .bundle_data").data("bundle_form_data");
4330                                var items_sum = getBundlePriceOnSingleProduct(data);
4331                                event.params.value = (parseFloat(data.base_price) + items_sum) * qty;
4332                            } else {
4333                                event.params.value = event.params.value * qty;
4334                            }
4335                        }
4336
4337                        event.params.quantity = qty;
4338
4339                        this.fireEvent(event.name, event);
4340
4341                    }
4342                }
4343            },
4344
4345            onWooAddToCartOnButtonEvent: function (product_id) {
4346                if(!options.dynamicEvents.woo_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
4347                    return;
4348
4349                if (window.pysWooProductData.hasOwnProperty(product_id)) {
4350                    if (window.pysWooProductData[product_id].hasOwnProperty(this.tag())) {
4351                        var productData = window.pysWooProductData[product_id][this.tag()]
4352                        var event = Utils.clone(options.dynamicEvents.woo_add_to_cart_on_button_click[this.tag()])
4353
4354                        Utils.copyProperties(productData['params'], event.params)
4355                        event.pixelIds = productData['pixelIds'];
4356                        this.fireEvent(event.name, event);
4357                    }
4358                }
4359            },
4360
4361            onEddAddToCartOnButtonEvent : function (download_id, price_index, qty) {
4362                if(!options.dynamicEvents.edd_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
4363                    return;
4364                var event = Utils.clone(options.dynamicEvents.edd_add_to_cart_on_button_click[this.tag()]);
4365
4366
4367                if (window.pysEddProductData && window.pysEddProductData.hasOwnProperty(download_id)) {
4368
4369                    var index;
4370
4371                    if (price_index) {
4372                        index = download_id + '_' + price_index;
4373                    } else {
4374                        index = download_id;
4375                    }
4376
4377                    if (window.pysEddProductData[download_id].hasOwnProperty(index)) {
4378                        if (window.pysEddProductData[download_id][index].hasOwnProperty(this.tag())) {
4379
4380                            Utils.copyProperties(window.pysEddProductData[download_id][index][this.tag()].params, event.params);
4381                            this.fireEvent(event.name,event);
4382
4383                        }
4384                    }
4385
4386                }
4387            },
4388
4389            onPageScroll: function (event) {
4390                if (initialized && this.isEnabled()) {
4391                    this.fireEvent(event.name, event);
4392                }
4393            },
4394
4395            onWatchVideo: function (event) {
4396                if (initialized && this.isEnabled() && !event.fired) {
4397                    this.fireEvent(event.name, event);
4398                }
4399            },
4400
4401            onCommentEvent: function (event) {
4402                if (initialized && this.isEnabled()) {
4403                    this.fireEvent(event.name, event);
4404                }
4405            },
4406
4407            onAdSenseEvent: function (event) {
4408                if (initialized && this.isEnabled()) {
4409                    this.fireEvent(event.name, event);
4410                }
4411            },
4412
4413            onTime: function (event) {
4414                if (initialized && this.isEnabled()) {
4415                    this.fireEvent(event.name, event);
4416                }
4417            },
4418        }
4419    }(options);
4420
4421    var Facebook = function (options) {
4422
4423        var defaultEventTypes = [
4424            'PageView',
4425            'ViewContent',
4426            'Search',
4427            'AddToCart',
4428            'AddToWishlist',
4429            'InitiateCheckout',
4430            'AddPaymentInfo',
4431            'Purchase',
4432            'Lead',
4433
4434            'Subscribe',
4435            'CustomizeProduct',
4436            'FindLocation',
4437            'StartTrial',
4438            'SubmitApplication',
4439            'Schedule',
4440            'Contact',
4441            'Donate'
4442        ];
4443
4444        var notCachedEventsIds = new Array();
4445        var isAddToCartFromJs =  options.woo.hasOwnProperty("addToCartCatchMethod")
4446            && options.woo.addToCartCatchMethod === "add_cart_js";
4447        if(!isAddToCartFromJs) {
4448            notCachedEventsIds.push('woo_add_to_cart_on_button_click')
4449        }
4450
4451        var initialized = false;
4452
4453        var genereateFbp = function (){
4454            return !Cookies.get('_fbp') ? 'fb.1.'+Date.now()+'.'+Math.floor(1000000000 + Math.random() * 9000000000) : Cookies.get('_fbp');
4455        };
4456        var genereateFbc = function (){
4457            return getUrlParameter('fbclid') ? 'fb.1.'+Date.now()+'.'+getUrlParameter('fbclid') : ''
4458        };
4459
4460        var configuredPixels = new Array();
4461        function fireEvent(name, event) {
4462
4463
4464            if(typeof window.pys_event_data_filter === "function" && window.pys_disable_event_filter(name,'facebook')) {
4465                return;
4466            }
4467
4468            var data = event.params;
4469            var ids = event.pixelIds.filter(function (pixelId) {
4470                return !Utils.hideMatchingPixel(pixelId, 'facebook');
4471            });
4472
4473            var actionType = defaultEventTypes.includes(name) ? 'trackSingle' : 'trackSingleCustom';
4474
4475            var params = {};
4476
4477            Utils.copyProperties(data, params);
4478            Utils.copyProperties(Utils.getRequestParams(), params);
4479            Utils.copyProperties(Utils.getRequestParams(), data);
4480
4481            params = Facebook.filterParams(params);
4482
4483
4484            if(options.facebook.serverApiEnabled) {
4485                if(event.e_id === "woo_remove_from_cart" ) {
4486                    Facebook.updateEventId(event.name);
4487                    event.eventID = Facebook.getEventId(event.name);
4488                } else if(!notCachedEventsIds.includes(event.e_id)) {
4489                    var isApiDisabled = options.gdpr.all_disabled_by_api ||
4490                        options.gdpr.facebook_disabled_by_api ||
4491                        options.gdpr.cookiebot_integration_enabled ||
4492                        options.gdpr.cookie_notice_integration_enabled ||
4493                        options.gdpr.consent_magic_integration_enabled ||
4494                        options.gdpr.cookie_law_info_integration_enabled;
4495                    // Update eventID
4496
4497                    event.eventID = Utils.generateUniqueId(event);
4498
4499                    if(Cookies.get('_fbp')){
4500                        params._fbp = Cookies.get('_fbp');
4501                    }
4502                    if(Cookies.get('_fbc')){
4503                        params._fbc = Cookies.get('_fbc');
4504                    }
4505                    // send event from server if they was bloc by gdpr or need send with delay
4506                    if( options.ajaxForServerEvent || isApiDisabled ){
4507                        var json = {
4508                            action: 'pys_api_event',
4509                            pixel: 'facebook',
4510                            event: name,
4511                            event_slug: event.e_id,
4512                            ids: ids,
4513                            data:params,
4514                            url:window.location.href,
4515                            eventID:event.eventID,
4516                            ajax_event:options.ajax_event
4517                        };
4518
4519                        if(event.hasOwnProperty('woo_order')) {
4520                            json['woo_order'] = event.woo_order;
4521                        }
4522
4523                        if(event.hasOwnProperty('edd_order')) {
4524                            json['edd_order'] = event.edd_order;
4525                        }
4526
4527                        // Forward the order key so the server can bind the
4528                        // purchase event to its specific order (RestPurchaseGuard).
4529                        if(event.hasOwnProperty('order_key') && event.order_key) {
4530                            json['order_key'] = event.order_key;
4531                        }
4532
4533                        if (pageReferrer) {
4534                            json.referrer_url = pageReferrer;
4535                        }
4536
4537                        let delay = 0;
4538                        try {
4539                            if (sessionStorage) {
4540                                Object.keys(sessionStorage)
4541                                    .filter(key => key.startsWith('wc_fragments_'))
4542                                    .forEach((key) => {
4543                                        let value = sessionStorage.getItem(key);
4544                                        if (event.e_id === 'woo_add_to_cart_on_button_click') {
4545                                            delay = 2000;
4546                                        }
4547                                    });
4548                            }
4549                        } catch (error) {
4550                            console.error('sessionStorage is not accessible:', error);
4551                        }
4552
4553                        if (event.e_id === "automatic_event_internal_link" || event.e_id === "automatic_event_outbound_link") {
4554                            delay = delay === 0 ? 500 : delay;
4555                            setTimeout(() => Utils.sendServerAjaxRequest(options.ajaxUrl, json), delay);
4556                        } else if (event.type != 'static') {
4557                            setTimeout(() => Utils.sendServerAjaxRequest(options.ajaxUrl, json), delay);
4558                        }
4559
4560                        if ( ( event.type == 'static' && options.ajaxForServerStaticEvent ) || ( event.hasOwnProperty( 'ajaxFire' ) && event.ajaxFire ) ) {
4561                            setTimeout( () => Utils.sendServerAjaxRequest( options.ajaxUrl, json ), delay );
4562                        }
4563                    }
4564
4565                    if( event.e_id !== "automatic_event_signup" && name == "CompleteRegistration" && options.facebook.wooCRSendFromServer ) {
4566                        return;
4567                    }
4568                }
4569
4570            }
4571            delete params._fbp;
4572            delete params._fbc;
4573            if (options.debug) {
4574                console.log('[Facebook] ' + name, params,"pixel_ids",ids,"eventID",event.eventID);
4575            }
4576            // fire event for each pixel id
4577            ids.forEach(function (pixelId) {
4578                // add eventID for deduplicate events @see https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events/
4579                var args = {};
4580                if(options.facebook.serverApiEnabled && event.hasOwnProperty('eventID')) {
4581                    args.eventID = event.eventID;
4582                }
4583                Facebook.maybeInitPixel(pixelId);
4584                fbq(actionType,pixelId, name, params,args);
4585            });
4586            var customEvent = new CustomEvent('facebook_event_sent', {
4587                detail: {
4588                    eventName: name,
4589                    params: params,
4590                    pixel_ids: ids
4591                }
4592            });
4593            window.dispatchEvent(customEvent);
4594        }
4595
4596        /**
4597         * Public API
4598         */
4599        return {
4600            tag: function() {
4601                return "facebook";
4602            },
4603            isEnabled: function () {
4604                return options.hasOwnProperty('facebook');
4605            },
4606            getHidePixel: function(){
4607                if(this.isEnabled() && options.facebook.hasOwnProperty('hide_pixels'))
4608                {
4609                    return options.facebook.hide_pixels;
4610                }
4611                return [];
4612            },
4613            initEventIdCookies: function (key) {
4614                var ids = {};
4615                ids[key] = pys_generate_token(36)
4616                Cookies.set('pys_fb_event_id', JSON.stringify(ids), {path: '/',domain: domain});
4617            },
4618
4619            updateEventId:function(key) {
4620                var cooData = Cookies.get("pys_fb_event_id")
4621                if(cooData === undefined) {
4622                    this.initEventIdCookies(key);
4623                } else {
4624                    var data = JSON.parse(cooData);
4625                    data[key] = pys_generate_token(36);
4626                    Cookies.set('pys_fb_event_id', JSON.stringify(data), {path: '/',domain: domain} );
4627                }
4628            },
4629
4630            getEventId:function (key) {
4631                var data = Cookies.get("pys_fb_event_id");
4632                if(data === undefined) {
4633                    this.initEventIdCookies(key);
4634                    data = Cookies.get("pys_fb_event_id");
4635                }
4636                return JSON.parse(data)[key];
4637            },
4638
4639            disable: function () {
4640                initialized = false;
4641            },
4642
4643            filterParams: function (params) {
4644                var filteredParams = {};
4645
4646                if (options.facebook.hasOwnProperty('enabled_medical') && options.facebook.enabled_medical && options.facebook.hasOwnProperty('do_not_track_medical_param')) {
4647                    // Remove empty strings from the array
4648                    options.facebook.do_not_track_medical_param = options.facebook.do_not_track_medical_param.filter(Boolean);
4649                    for (const key in params) {
4650                        if (Array.isArray(options.facebook.do_not_track_medical_param)) {
4651                            if (!options.facebook.do_not_track_medical_param.includes(key)) {
4652                                filteredParams[key] = params[key];
4653                            }
4654                        }
4655                    }
4656                }
4657                else {
4658                    Utils.copyProperties(params, filteredParams);
4659                }
4660                return filteredParams;
4661            },
4662
4663            /**
4664             * Load pixel's JS
4665             */
4666            loadPixel: function () {
4667                if (initialized || !this.isEnabled() || !Utils.consentGiven('facebook')) {
4668                    return;
4669                }
4670
4671                ! function (f, b, e, v, n, t, s) {
4672                    if (f.fbq) return;
4673                    n = f.fbq = function () {
4674                        n.callMethod ?
4675                            n.callMethod.apply(n, arguments) : n.queue.push(arguments)
4676                    };
4677                    if (!f._fbq) f._fbq = n;
4678                    n.push = n;
4679                    n.loaded = !0;
4680                    n.version = '2.0';
4681                    n.agent = 'dvpixelyoursite';
4682                    n.queue = [];
4683                    t = b.createElement(e);
4684                    t.async = !0;
4685                    t.src = v;
4686                    s = b.getElementsByTagName(e)[0];
4687                    s.parentNode.insertBefore(t, s)
4688                }(window,
4689                    document, 'script', 'https://connect.facebook.net/en_US/fbevents.js');
4690                let expires = parseInt(options.cookie_duration);
4691                if(!Cookies.get('_fbp')) {
4692                    Cookies.set('_fbp',genereateFbp(),  { expires: expires,path: '/',domain: domain });
4693                }
4694
4695                if(getUrlParameter('fbclid')) {
4696                    Cookies.set('_fbc',genereateFbc(),  { expires: expires,path: '/',domain: domain });
4697                }
4698
4699                // Now that Facebook consent is confirmed, mirror the session
4700                // entry referrer (if any) into the session cookie so PHP can
4701                // use it as a fallback for orphan server-only events.
4702                getOrInitSessionEntryReferrer(true);
4703
4704                // Refresh the per-page event referrer cookie so checkout
4705                // hooks can snapshot the buyer's prior page.
4706                writeEventReferrerCookie();
4707
4708                var ids = options.facebook.pixelIds.filter(function (pixelId) {
4709                    return !Utils.hideMatchingPixel(pixelId, 'facebook');
4710                });
4711                // initialize default pixel
4712                ids.forEach(function (pixelId) {
4713                    Facebook.maybeInitPixel(pixelId);
4714                });
4715                initialized = true;
4716
4717                Utils.fireStaticEvents('facebook');
4718
4719            },
4720            advancedMatching: function () {
4721                if(options.facebook.advancedMatchingEnabled) {
4722                    let advancedMatchingForm = Utils.getAdvancedFormData();
4723                    let advancedMatching = {};
4724                    if(Object.keys(options.facebook.advancedMatching).length > 0) {
4725                        advancedMatching = options.facebook.advancedMatching;
4726                    }
4727
4728                    if(!advancedMatching.hasOwnProperty("em")
4729                        && advancedMatchingForm.hasOwnProperty("email") && advancedMatchingForm["email"].length > 0) {
4730                        advancedMatching["em"] = advancedMatchingForm["email"];
4731                    }
4732                    if(!advancedMatching.hasOwnProperty("ph")
4733                        && advancedMatchingForm.hasOwnProperty("phone") && advancedMatchingForm["phone"].length > 0) {
4734                        advancedMatching["ph"] = advancedMatchingForm["phone"];
4735                    }
4736                    if(!advancedMatching.hasOwnProperty("fn")
4737                        && advancedMatchingForm.hasOwnProperty("first_name") && advancedMatchingForm["first_name"].length > 0) {
4738                        advancedMatching["fn"] = advancedMatchingForm["first_name"];
4739                    }
4740                    if(!advancedMatching.hasOwnProperty("ln")
4741                        && advancedMatchingForm.hasOwnProperty("last_name") && advancedMatchingForm["last_name"].length > 0) {
4742                        advancedMatching["ln"] = advancedMatchingForm["last_name"];
4743                    }
4744                    if(!advancedMatching.hasOwnProperty("external_id")){
4745                        if (Cookies.get('pbid')) {
4746                            advancedMatching["external_id"] = Cookies.get('pbid');
4747                        }
4748                    }
4749                    else if(advancedMatching.hasOwnProperty("external_id") && advancedMatching.external_id != Cookies.get('pbid'))
4750                    {
4751                        advancedMatching["external_id"] = Cookies.get('pbid') ? Cookies.get('pbid') : advancedMatching.external_id;
4752                    }
4753
4754                    if(Object.keys(advancedMatching).length > 0) {
4755                        return advancedMatching;
4756                    }
4757                }
4758                return false
4759            },
4760            maybeInitPixel: function(pixelId) {
4761
4762                if(configuredPixels.includes(pixelId)) return;
4763
4764                if (options.facebook.removeMetadata || Utils.hideMatchingPixel(pixelId, this.tag())) {
4765                    fbq('set', 'autoConfig', false, pixelId);
4766                }
4767                let advancedMatching = Facebook.advancedMatching();
4768
4769                if ( +options.facebook.meta_ldu === 1  ) {
4770                    fbq( 'dataProcessingOptions', [ 'LDU' ], 0, 0 );
4771                }
4772
4773                if (options.gdpr.consent_magic_integration_enabled && typeof CS_Data !== "undefined") {
4774                    if(!advancedMatching) {
4775                        fbq('init', pixelId);
4776                    } else {
4777                        var test_prefix = CS_Data.test_prefix;
4778                        var cs_advanced_matching = Cookies.get('cs_enabled_advanced_matching'+test_prefix);
4779                        if (jQuery('#cs_enabled_advanced_matching'+test_prefix).length > 0) {
4780                            if (cs_advanced_matching == 'yes') {
4781                                fbq('init', pixelId, advancedMatching);
4782                            } else {
4783                                fbq('init', pixelId);
4784                            }
4785                        } else {
4786                            fbq('init', pixelId, advancedMatching);
4787                        }
4788                    }
4789                } else {
4790                    if(!advancedMatching) {
4791                        fbq('init', pixelId);
4792                    }  else {
4793                        fbq('init', pixelId, advancedMatching);
4794                    }
4795                }
4796                configuredPixels.push(pixelId);
4797            },
4798
4799            fireEvent: function (name, data) {
4800
4801                if (!initialized || !this.isEnabled()) {
4802                    return false;
4803                }
4804
4805                data.delay = data.delay || 0;
4806                data.params = data.params || {};
4807
4808                if (data.delay === 0) {
4809
4810                    fireEvent(name, data);
4811
4812                } else {
4813
4814                    setTimeout(function (name, params) {
4815                        fireEvent(name, params);
4816                    }, data.delay * 1000, name, data);
4817
4818                }
4819
4820                return true;
4821
4822            },
4823
4824            onAdSenseEvent: function (event) {
4825                this.fireEvent(event.name, event);
4826            },
4827
4828            onClickEvent: function (event) {
4829                this.fireEvent(event.name, event);
4830            },
4831
4832            onWatchVideo: function (event) {
4833                this.fireEvent(event.name, event);
4834            },
4835
4836            onCommentEvent: function (event) {
4837                this.fireEvent(event.name, event);
4838            },
4839
4840            onFormEvent: function (event) {
4841                this.fireEvent(event.name, event);
4842            },
4843
4844            onDownloadEvent: function (event) {
4845                this.fireEvent(event.name, event);
4846
4847            },
4848
4849
4850            onWooAddToCartOnButtonEvent: function (product_id) {
4851
4852                window.pysWooProductData = window.pysWooProductData || [];
4853                if(!options.dynamicEvents.woo_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
4854                    return;
4855
4856                if (window.pysWooProductData.hasOwnProperty(product_id)) {
4857                    if (window.pysWooProductData[product_id].hasOwnProperty('facebook')) {
4858
4859                        var event = Utils.clone(options.dynamicEvents.woo_add_to_cart_on_button_click[this.tag()])
4860
4861                        Utils.copyProperties(window.pysWooProductData[product_id]['facebook']['params'], event.params)
4862                        event.pixelIds = window.pysWooProductData[product_id]['facebook']['pixelIds'];
4863                        this.fireEvent(event.name, event);
4864                    }
4865                }
4866            },
4867
4868            onWooAddToCartOnSingleEvent: function (product_id, qty, product_type, is_external, $form) {
4869
4870                window.pysWooProductData = window.pysWooProductData || [];
4871                if(!options.dynamicEvents.woo_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
4872                    return;
4873
4874                if (product_type === Utils.PRODUCT_VARIABLE && !options.facebook.wooVariableAsSimple) {
4875                    product_id = parseInt($form.find('input[name="variation_id"]').val());
4876                }
4877
4878                if (window.pysWooProductData.hasOwnProperty(product_id)) {
4879                    if (window.pysWooProductData[product_id].hasOwnProperty('facebook')) {
4880
4881                        var event = Utils.clone(options.dynamicEvents.woo_add_to_cart_on_button_click.facebook);
4882
4883
4884                        Utils.copyProperties(window.pysWooProductData[product_id]['facebook']['params'], event.params);
4885
4886                        var groupValue = 0;
4887                        if(product_type === Utils.PRODUCT_GROUPED ) {
4888                            $form.find(".woocommerce-grouped-product-list .qty").each(function(index){
4889                                var childId = $(this).attr('name').replaceAll("quantity[","").replaceAll("]","");
4890                                var quantity = parseInt($(this).val());
4891                                if(isNaN(quantity)) {
4892                                    quantity = 0;
4893                                }
4894                                var childItem = window.pysWooProductData[product_id]['facebook'].grouped[childId];
4895
4896                                if(quantity == 0) {
4897                                    event.params.content_ids.forEach(function(el,index,array) {
4898                                        if(el == childItem.content_id) {
4899                                            array.splice(index, 1);
4900                                        }
4901                                    });
4902                                }
4903
4904                                if(event.params.hasOwnProperty('contents')) {
4905                                    event.params.contents.forEac
4905h(function(el,index,array) {
4906                                        if(el.id == childItem.content_id) {
4907                                            if(quantity > 0){
4908                                                el.quantity = quantity;
4909                                            } else {
4910                                                array.splice(index, 1);
4911                                            }
4912                                        }
4913                                    });
4914                                }
4915
4916
4917                                groupValue += childItem.price * quantity;
4918                            });
4919                            if(groupValue == 0) return; // skip if no items selected
4920                        }
4921
4922
4923
4924                        // maybe customize value option
4925                        if (options.woo.addToCartOnButtonValueEnabled && options.woo.addToCartOnButtonValueOption !== 'global') {
4926
4927                            if(product_type === Utils.PRODUCT_GROUPED) {
4928                                event.params.value = groupValue;
4929                            } else if(product_type === Utils.PRODUCT_BUNDLE) {
4930                                var data = $(".bundle_form .bundle_data").data("bundle_form_data");
4931                                var items_sum = getBundlePriceOnSingleProduct(data);
4932                                event.params.value = (parseFloat(data.base_price) + items_sum )* qty;
4933                            } else {
4934                                event.params.value = event.params.value * qty;
4935                            }
4936                        }
4937
4938                        // only when non Facebook for WooCommerce logic used
4939                        if (event.params.hasOwnProperty('contents') && product_type !== Utils.PRODUCT_GROUPED) {
4940                            event.params.contents[0].quantity = qty;
4941
4942                        }
4943
4944                        var event_name = is_external ? options.woo.affiliateEventName : event.name;
4945
4946                        this.fireEvent(event_name, event);
4947
4948                    }
4949                }
4950
4951            },
4952
4953            onWooRemoveFromCartEvent: function (event) {
4954                this.fireEvent(event.name, event);
4955            },
4956
4957            onWooAffiliateEvent: function (product_id) {
4958                if(!options.dynamicEvents.woo_affiliate.hasOwnProperty(this.tag()))
4959                    return;
4960                var event = Utils.clone(options.dynamicEvents.woo_affiliate[this.tag()]);
4961
4962
4963                if (window.pysWooProductData.hasOwnProperty(product_id)) {
4964                    if (window.pysWooProductData[product_id].hasOwnProperty('facebook')) {
4965
4966                        Utils.copyProperties(window.pysWooProductData[product_id][this.tag()].params, event.params)
4967                        this.fireEvent(options.woo.affiliateEventName, event);
4968
4969                    }
4970                }
4971
4972            },
4973
4974            onWooPayPalEvent: function (event) {
4975                this.fireEvent(event.name, event);
4976            },
4977
4978            onEddAddToCartOnButtonEvent: function (download_id, price_index, qty) {
4979                if(!options.dynamicEvents.edd_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
4980                    return;
4981                var event = Utils.clone(options.dynamicEvents.edd_add_to_cart_on_button_click[this.tag()]);
4982
4983                if (window.pysEddProductData && window.pysEddProductData.hasOwnProperty(download_id)) {
4984
4985                    var index;
4986
4987                    if (price_index) {
4988                        index = download_id + '_' + price_index;
4989                    } else {
4990                        index = download_id;
4991                    }
4992
4993                    if (window.pysEddProductData[download_id].hasOwnProperty(index)) {
4994                        if (window.pysEddProductData[download_id][index].hasOwnProperty('facebook')) {
4995
4996                            Utils.copyProperties(window.pysEddProductData[download_id][index]['facebook']["params"], event.params)
4997
4998                            // maybe customize value option
4999                            if (options.edd.addToCartOnButtonValueEnabled && options.edd.addToCartOnButtonValueOption !== 'global') {
5000                                event.params.value = event.params.value * qty;
5001                            }
5002
5003                            // update contents qty param
5004                            var contents = event.params.contents;
5005                            contents[0].quantity = qty;
5006                            event.params.contents = contents;
5007
5008                            this.fireEvent(event.name,event);
5009
5010                        }
5011                    }
5012
5013                }
5014
5015            },
5016
5017            onEddRemoveFromCartEvent: function (event) {
5018                this.fireEvent(event.name, event);
5019            },
5020            onPageScroll: function (event) {
5021                this.fireEvent(event.name, event);
5022            },
5023            onTime: function (event) {
5024                this.fireEvent(event.name, event);
5025            },
5026        };
5027
5028    }(options);
5029
5030    var Analytics = function (options) {
5031
5032        var initialized = false;
5033        var isAllowEnhancedConversions = false;
5034
5035        /**
5036         * Fires event
5037         *
5038         * @link: https://developers.google.com/analytics/devguides/collection/gtagjs/sending-data
5039         * @link: https://developers.google.com/analytics/devguides/collection/gtagjs/events
5040         * @link: https://developers.google.com/gtagjs/reference/event
5041         * @link: https://developers.google.com/gtagjs/reference/parameter
5042         *
5043         * @link: https://developers.google.com/analytics/devguides/collection/gtagjs/custom-dim
5043s-mets
5044         *
5045         * @param name
5046         * @param data
5047         */
5048        function fireEvent(name, event) {
5049            if(typeof window.pys_event_data_filter === "function" && window.pys_disable_event_filter(name,'ga')) {
5050                return;
5051            }
5052
5053            var eventParams = event.params;
5054            var valuesArray = Object.values(event.trackingIds);
5055            var ids = valuesArray.filter(function (pixelId) {
5056                return loadTags.some(loadTag => pixelId.startsWith(loadTag)) && !Utils.hideMatchingPixel(pixelId, 'ga') && !Utils.hideMatchingPixel(pixelId, 'google_ads');
5057            })
5058
5059            // Generate unique event_id for deduplication
5060            eventParams.event_id = Utils.generateUniqueId(event);
5061            event.eventID = Utils.generateUniqueId(event);
5062
5063            Utils.copyProperties(Utils.getRequestParams(), eventParams);
5064
5065            var copyParams = Utils.copyProperties(eventParams, {}); // copy params because mapParamsTov4 can modify 
5065it
5066            var params = mapParamsTov4(ids,name,copyParams)
5067
5068            var _fireEvent = function (tracking_ids,name,params) {
5069
5070                // Firing without a destination would park the hit inside gtag.js
5071                // instead of sending it. Skip, but keep the reason visible: an
5072                // empty list means the event's trackingIds and the tags actually
5073                // configured on this page went out of sync.
5074                if (!Utils.hasSendToTarget(tracking_ids)) {
5075                    if (options.debug) {
5076                        console.warn('[Google Analytics] ' + name + ' skipped: no tracking ID left after filtering', {
5077                            eventTrackingIds: Object.values(event.trackingIds),
5078                            loadedTags: loadTags
5079                        });
5080                    }
5081                    return;
5082                }
5083
5084                params['send_to'] = tracking_ids;
5085
5086                if (options.debug) {
5087                    console.log('[Google Analytics #' + tracking_ids + '] ' + name, params);
5088                }
5089
5090                // Send via gtag
5091                Utils.gtagPush('event', name, params);
5092
5093                var customEvent = new CustomEvent('gtag_event_sent', {
5094                    detail: {
5095                        eventName: name,
5096                        params: params,
5097                        pixel_ids: tracking_ids
5098                    }
5099                });
5100                window.dispatchEvent(customEvent);
5101            };
5102
5103
5104            delete params.analytics_storage;
5105            delete params.ad_storage;
5106            delete params.ad_user_data;
5107            delete params.ad_personalization;
5108
5109            if(options.hasOwnProperty('google_ads')){
5110                var conversion_event_name = event.e_id;
5111                switch ( conversion_event_name ) {
5112                    case "woo_add_to_cart_on_cart_page":
5113                    case "woo_add_to_cart_on_checkout_page":
5114                    case "woo_add_to_cart_on_button_click":
5115                        conversion_event_name = 'woo_add_to_cart';
5116                        break;
5117                    case "edd_add_to_cart_on_cart_page":
5118                    case "edd_add_to_cart_on_checkout_page":
5119                    case "edd_add_to_cart_on_button_click":
5120                        conversion_event_name = 'edd_add_to_cart';
5121                        break;
5122                    case "automatic_event_adsense":
5123                    case "automatic_event_comment":
5124                    case "automatic_event_download":
5125                    case "automatic_event_email_link":
5126                    case "automatic_event_form":
5127                    case "automatic_event_internal_link":
5128                    case "automatic_event_outbound_link":
5129                    case "automatic_event_scroll":
5130                    case "automatic_event_tel_link":
5131                    case "automatic_event_time_on_page":
5132                    case "automatic_event_video":
5133                        conversion_event_name = 'automatic_event';
5134                        break;
5135                }
5136
5137                if (conversion_event_name !== 'automatic_event') {
5138                    // We replace the event name for the settings key if it is woo_view_item_list
5139                    const trackEventName = conversion_event_name === 'woo_view_item_list'
5140                        ? 'woo_view_category'
5141                        : conversion_event_name;
5142
5143                    if (typeof options.google_ads[trackEventName + '_conversion_track'] !== 'undefined') {
5144                        const conversionTrack = options.google_ads[trackEventName + '_conversion_track'];
5145
5146                        if (conversionTrack === 'conversion') {
5147                            ids = ids.map(id => id.split('/')[0]);
5148                        }
5149                    }
5150                }
5151            }
5152
5153
5154            _fireEvent(ids, name, params);
5155            if(event.hasOwnProperty('unify') && event.unify){
5156                isTrackEventForGA.push(name);
5157            }
5158        }
5159
5160        function normalizeEventName(eventName) {
5161
5162            var matches = {
5163                ViewContent: 'view_item',
5164                AddToCart: 'add_to_cart',
5165                AddToWishList: 'add_to_wishlist',
5166                InitiateCheckout: 'begin_checkout',
5167                Purchase: 'purchase',
5168                Lead: 'generate_lead',
5169                CompleteRegistration: 'sign_up',
5170                AddPaymentInfo: 'set_checkout_option'
5171            };
5172
5173            return matches.hasOwnProperty(eventName) ? matches[eventName] : eventName;
5174
5175        }
5176
5177        function mapParamsTov4(tag,name,param) {
5178            //GA4 automatically collects a number of parameters for all events
5179            var hasGA4Tag = false;
5180
5181            // end
5182            if (Array.isArray(tag)) {
5183                hasGA4Tag = tag.some(function (element) {
5184                    return isv4(element);
5185                });
5186            } else if(isv4(tag)) {
5187                // tag is a string and matches GA4
5188                hasGA4Tag = true;
5189            }
5190            if(hasGA4Tag) {
5191                delete param.event_category;
5192                delete param.event_label;
5193                delete param.ecomm_prodid;
5194                delete param.ecomm_pagetype;
5195                delete param.ecomm_totalvalue;
5196                if(name === 'search') {
5197                    param['search'] = param.search_term;
5198                    delete param.search_term;
5199                    delete param.dynx_itemid;
5200                    delete param.dynx_pagetype;
5201                    delete param.dynx_totalvalue;
5202                }
5203            }
5204            return param;
5205        }
5206
5207        function isv4(tag) {
5208            return tag.indexOf('G') === 0;
5209        }
5210
5211        /**
5212         * Public API
5213         */
5214        return {
5215            tag: function() {
5216                return "ga";
5217            },
5218            isEnabled: function () {
5219                return options.hasOwnProperty('ga');
5220            },
5221            getHidePixel: function(){
5222                if(this.isEnabled() && options.ga.hasOwnProperty('hide_pixels'))
5223                {
5224                    return options.ga.hide_pixels;
5225                }
5226                return [];
5227            },
5228            disable: function () {
5229                initialized = false;
5230            },
5231            updateEnhancedConversionData : function () {
5232                if (!initialized || !this.isEnabled()) {
5233                    return;
5234                }
5235                if(options.hasOwnProperty("tracking_analytics") && options.tracking_analytics.hasOwnProperty("userDataEnable") && options.tracking_analytics.userDataEnable) {
5236                    var advanced = Utils.getAdvancedMergeFormData();
5237                    if (Object.keys(advanced).length > 0) {
5238                        Utils.gtagPush('set', 'user_data', advanced);
5239                    }
5240                }
5241            },
5242            loadPixel: function () {
5243                if (initialized || !this.isEnabled() || !Utils.consentGiven('analytics')) {
5244                    return;
5245                }
5246
5247
5248                for (var i = 0; i < options.ga.trackingIds.length; i++) {
5249                    var trackingId = options.ga.trackingIds[i];
5250                    if (!Utils.hideMatchingPixel(trackingId, 'ga')) {
5251                        Utils.loadGoogleTag(trackingId);
5252                        break;
5253                    }
5254                }
5255
5256
5257                if(options.hasOwnProperty("tracking_analytics") && options.tracking_analytics.hasOwnProperty("userDataEnable") && options.tracking_analytics.userDataEnable){
5258                    var advanced = Utils.getAdvancedMergeFormData();
5259                    if(Object.keys(advanced).length > 0){
5260                        Utils.gtagPush('set', 'user_data', advanced);
5261                    }
5262                }
5263
5264                var config = {};
5265
5266                if(options.user_id && options.user_id != 0) {
5267                    config.user_id = options.user_id;
5268                }
5269                config.url_passthrough = options.ga.url_passthrough;
5270                // Cross-Domain tracking
5271                if (options.ga.crossDomainEnabled) {
5272                    config.linker = {
5273                        accept_incoming: options.ga.crossDomainAcceptIncoming,
5274                        domains: options.ga.crossDomainDomains
5275                    };
5276                }
5277
5278                var ids = options.ga.trackingIds.filter(function (pixelId) {
5279                    return !Utils.hideMatchingPixel(pixelId, 'ga');
5280                });
5281                loadTags.push(...ids);
5282                ids.forEach(function (trackingId,index) {
5283
5284                    var obj = options.ga.isDebugEnabled;
5285                    var searchValue = "index_"+index;
5286                    var config_for_tag = Object.assign({}, config);
5287                    config_for_tag.debug_mode = false;
5288                    config_for_tag.send_page_view = !options.ga.custom_page_view_event;
5289                    for (var key in obj) {
5290                        if (obj[key] === searchValue) {
5291                            config_for_tag.debug_mode = true;
5292                            break;
5293                        }
5294                    }
5295                    if(!config_for_tag.debug_mode)
5296                    {
5297                        delete config_for_tag.debug_mode;
5298                    }
5299
5300                    if(isv4(trackingId)) {
5301                        if(options.ga.disableAdvertisingPersonalization) {
5302                            config_for_tag.allow_ad_personalization_signals = false
5303                        }
5304                    }
5305                    if(options.ga.hasOwnProperty('additionalConfig')){
5306                        if(options.ga.additionalConfig.hasOwnProperty(trackingId) && options.ga.additionalConfig[trackingId]){
5307                            config_for_tag.first_party_collection = options.ga.additionalConfig[trackingId].first_party_collection;
5308                        }
5309
5310                    }
5311                    if(options.ga.hasOwnProperty('serverContainerUrls')){
5312                        if(options.ga.serverContainerUrls.hasOwnProperty(trackingId) && options.ga.serverContainerUrls[trackingId].enable_server_container != false){
5313                            if(options.ga.serverContainerUrls[trackingId].server_container_url != ''){
5314                                config_for_tag.server_container_url = options.ga.serverContainerUrls[trackingId].server_container_url;
5315                            }
5316                            if(options.ga.serverContainerUrls[trackingId].transport_url != ''){
5317                                config_for_tag.transport_url = options.ga.serverContainerUrls[trackingId].transport_url;
5318                            }
5319                        }
5320                    }
5321                    if (options.gdpr.cookiebot_integration_enabled && typeof Cookiebot !== 'undefined') {
5322                        var cookiebot_consent_category = options.gdpr['cookiebot_analytics_consent_category'];
5323                        if (options.gdpr['analytics_prior_consent_enabled']) {
5324                            if (Cookiebot.consented === true && Cookiebot.consent[cookiebot_consent_category]) {
5325                                Utils.gtagPush('config', trackingId, config_for_tag);
5326                            }
5327                        } else {
5328                            if (Cookiebot.consent[cookiebot_consent_category]) {
5329                                Utils.gtagPush('config', trackingId, config_for_tag);
5330                            }
5331                        }
5332                    }
5333                    else
5334                    {
5335                        Utils.gtagPush('config', trackingId, config_for_tag);
5336                    }
5337                });
5338                if(!isAdsLoad && GAds.isEnabled() && options.google_ads.conversion_ids.length > 0 && Utils.consentGiven('google_ads')) {
5339                    for (var i = 0; i < options.google_ads.conversion_ids.length; i++) {
5340                        var trackingId = options.google_ads.conversion_ids[i];
5341                        if (!Utils.hideMatchingPixel(trackingId, 'google_ads')) {
5342                            Utils.loadGoogleTag(trackingId);
5343                            break;
5344                        }
5345                    }
5346                    var ids = options.google_ads.conversion_ids.filter(function (pixelId) {
5347                        return !Utils.hideMatchingPixel(pixelId, 'google_ads');
5348                    });
5349                    loadTags.push(...ids);
5350                    // configure conversion ids
5351                    ids.forEach(function (conversion_id,index) {
5352
5353                        Utils.gtagPush('config', conversion_id);
5354
5355                        if(options.google_ads.enhanced_conversion.includes("index_"+index)) {
5356                            isAllowEnhancedConversions = true
5357                            Utils.gtagPush('config', conversion_id,{ 'allow_enhanced_conversions':true });
5358                        }
5359
5360                        Utils.registerGadsPhoneConversion( conversion_id );
5361
5362                    });
5363                    isAdsLoad = true;
5364                }
5365
5366                initialized = true;
5367
5368                Utils.fireStaticEvents('ga');
5369                $( document).trigger( "analytics_initialized")
5370            },
5371
5372            fireEvent: function (name, data) {
5373
5374                if (!initialized || !this.isEnabled()) {
5375                    return false;
5376                }
5377
5378                data.delay = data.delay || 0;
5379                data.params = data.params || {};
5380
5381                if (data.delay === 0) {
5382
5383                    fireEvent(name, data);
5384
5385                } else {
5386
5387                    setTimeout(function (name, params) {
5388                        fireEvent(name, params);
5389                    }, data.delay * 1000, name, data);
5390
5391                }
5392
5393                return true;
5394
5395            },
5396
5397            onAdSenseEvent: function (event) {
5398                this.fireEvent(event.name, event);
5399            },
5400
5401            onClickEvent: function (event) {
5402                this.fireEvent(event.name, event);
5403            },
5404
5405            onWatchVideo: function (event) {
5406                if(!event.hasOwnProperty("youtube_disabled")
5407                    || !event.youtube_disabled
5408                    || event.params.video_type !== "youtube") {
5409                    this.fireEvent(event.name, event);
5410                }
5411            },
5412
5413            onCommentEvent: function (event) {
5414
5415                this.fireEvent(event.name, event);
5416
5417            },
5418
5419            onFormEvent: function (event) {
5420
5421                this.fireEvent(event.name, event);
5422
5423            },
5424
5425            onDownloadEvent: function (event) {
5426
5427                this.fireEvent(event.name, event);
5428
5429            },
5430
5431            onWooAddToCartOnButtonEvent: function (product_id, prod_info = null) {
5432                if(!options.dynamicEvents.woo_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
5433                    return;
5434
5435                if (window.pysWooProductData.hasOwnProperty(product_id)) {
5436                    if (window.pysWooProductData[product_id].hasOwnProperty('ga')) {
5437                        var event = Utils.clone(options.dynamicEvents.woo_add_to_cart_on_button_click[this.tag()]);
5438                        Utils.copyProperties(window.pysWooProductData[product_id]['ga'].params, event.params)
5439                        event.trackingIds = window.pysWooProductData[product_id]['ga']['trackingIds'];
5440                        if(prod_info)
5441                        {
5442                            if(prod_info['pys_list_name_productlist_id'])
5443                            {
5444                                event.params.items[0]['item_list_id'] = prod_info['pys_list_name_productlist_id']
5445                            }
5446                            if(prod_info['pys_list_name_productlist_name'])
5447                            {
5448                                event.params.items[0]['item_list_name'] =prod_info['pys_list_name_productlist_name']
5449                            }
5450                        }
5451                        this.fireEvent(event.name, event);
5452                    }
5453                }
5454
5455            },
5456
5457            onWooAddToCartOnSingleEvent: function (product_id, qty, product_type, is_external, $form, prod_info) {
5458
5459                window.pysWooProductData = window.pysWooProductData || [];
5460
5461                if(!options.dynamicEvents.woo_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
5462                    return;
5463                var event = Utils.clone(options.dynamicEvents.woo_add_to_cart_on_button_click[this.tag()]);
5464
5465                if (product_type === Utils.PRODUCT_VARIABLE && !options.ga.wooVariableAsSimple) {
5466                    product_id = parseInt($form.find('input[name="variation_id"]').val());
5467                }
5468
5469                if (window.pysWooProductData.hasOwnProperty(product_id)) {
5470                    if (window.pysWooProductData[product_id].hasOwnProperty('ga')) {
5471
5472                        Utils.copyProperties(window.pysWooProductData[product_id]['ga'].params, event.params);
5473
5474
5475                        if(product_type === Utils.PRODUCT_GROUPED ) {
5476                            var groupValue = 0;
5477                            $form.find(".woocommerce-grouped-product-list .qty").each(function(index){
5478                                var childId = $(this).attr('name').replaceAll("quantity[","").replaceAll("]","");
5479                                var quantity = parseInt($(this).val());
5480                                if(isNaN(quantity)) {
5481                                    quantity = 0;
5482                                }
5483                                var childItem = window.pysWooProductData[product_id]['ga'].grouped[childId];
5484                                event.params.items.forEach(function(el,index,array) {
5485                                    if(el.id == childItem.content_id) {
5486                                        if(quantity > 0){
5487                                            el.quantity = quantity;
5488                                            el.price = childItem.price;
5489                                        } else {
5490                                            array.splice(index, 1);
5491                                        }
5492                                    }
5493                                });
5494                                groupValue += childItem.price * quantity;
5495                            });
5496
5497                            if(options.woo.addToCartOnButtonValueEnabled &&
5498                                options.woo.addToCartOnButtonValueOption !== 'global' &&
5499                                event.params.hasOwnProperty('value')) {
5500                                event.params.value = groupValue;
5501                            }
5502
5503                            if(groupValue == 0) return; // skip if no items selected
5504                        } else {
5505                            // update items qty param
5506                            event.params.items[0].quantity = qty;
5507                        }
5508
5509                        // maybe customize value option
5510                        if (options.woo.addToCartOnButtonValueEnabled &&
5511                            options.woo.addToCartOnButtonValueOption !== 'global' &&
5512                            product_type !== Utils.PRODUCT_GROUPED)
5513                        {
5514                            if(event.params.hasOwnProperty('value')) {
5515                                event.params.value = event.params.items[0].price * qty;
5516                            }
5517                        }
5518
5519                        if(prod_info)
5520                        {
5521                            if(prod_info['pys_list_name_productlist_id'])
5522                            {
5523                                event.params.items[0]['item_list_id'] = prod_info['pys_list_name_productlist_id']
5524                            }
5525                            if(prod_info['pys_list_name_productlist_name'])
5526                            {
5527                                event.params.items[0]['item_list_name'] =prod_info['pys_list_name_productlist_name']
5528                            }
5529                        }
5530
5531                        var eventName = is_external ? options.woo.affiliateEventName : event.name;
5532                        eventName = normalizeEventName(eventName);
5533
5534                        this.fireEvent(eventName, event);
5535
5536                    }
5537                }
5538
5539            },
5540
5541            onWooCheckoutProgressStep: function (event) {
5542                this.fireEvent(event.name, event);
5543            },
5544
5545            onWooSelectContent: function (event) {
5546                const select_prod_list = {};
5547
5548                if (event.params.items[0].item_list_name !== undefined) {
5549                    select_prod_list.list_name = event.params.items[0].item_list_name;
5550                }
5551
5552                if (event.params.items[0].item_list_id !== undefined) {
5553                    select_prod_list.list_id = event.params.items[0].item_list_id;
5554                }
5555                const url = new URL(window.location.href);
5556                select_prod_list.url = url.origin + url.pathname;
5557                Cookies.set('select_prod_list', JSON.stringify(select_prod_list), { expires: 1,path: '/' });
5558                this.fireEvent(event.name, event);
5559            },
5560
5561            onWooRemoveFromCartEvent: function (event) {
5562                this.fireEvent(event.name, event);
5563            },
5564
5565            onWooAffiliateEvent: function (product_id) {
5566                if(!options.dynamicEvents.woo_affiliate.hasOwnProperty(this.tag()))
5567                    return;
5568                var event = options.dynamicEvents.woo_affiliate[this.tag()];
5569
5570                if (window.pysWooProductData.hasOwnProperty(product_id)) {
5571                    if (window.pysWooProductData[product_id].hasOwnProperty('ga')) {
5572
5573                        event = Utils.clone(event );
5574                        Utils.copyProperties(window.pysWooProductData[product_id][this.tag()], event.params)
5575                        this.fireEvent(normalizeEventName(options.woo.affiliateEventName), event);
5576
5577                    }
5578                }
5579
5580            },
5581
5582            onWooPayPalEvent: function (event) {
5583                this.fireEvent(event.name, event);
5584            },
5585
5586            onEddAddToCartOnButtonEvent: function (download_id, price_index, qty) {
5587                if(!options.dynamicEvents.edd_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
5588                    return;
5589                var event = Utils.clone(options.dynamicEvents.edd_add_to_cart_on_button_click[this.tag()]);
5590
5591
5592                if (window.pysEddProductData && window.pysEddProductData.hasOwnProperty(download_id)) {
5593
5594                    var index;
5595
5596                    if (price_index) {
5597                        index = download_id + '_' + price_index;
5598                    } else {
5599                        index = download_id;
5600                    }
5601
5602                    if (window.pysEddProductData[download_id].hasOwnProperty(index)) {
5603                        if (window.pysEddProductData[download_id][index].hasOwnProperty('ga')) {
5604
5605                            Utils.copyProperties(window.pysEddProductData[download_id][index]['ga'].params, event.params);
5606
5607                            // update items qty param
5608                            event.params.items[0].quantity = qty;
5609
5610                            this.fireEvent(event.name,event);
5611
5612                        }
5613                    }
5614
5615                }
5616
5617            },
5618
5619            onEddRemoveFromCartEvent: function (event) {
5620                this.fireEvent(event.name, event);
5621            },
5622
5623            onPageScroll: function (event) {
5624                if (initialized && this.isEnabled()) {
5625                    this.fireEvent(event.name, event);
5626                }
5627            },
5628            onTime: function (event) {
5629                if (initialized && this.isEnabled()) {
5630                    this.fireEvent(event.name, event);
5631                }
5632            },
5633        };
5634
5635    }(options);
5636
5637    var GAds = function (options) {
5638
5639        var initialized = false;
5640        var isAllowEnhancedConversions = false
5641        /**
5642         * Fires event
5643         *
5644         * @link: https://developers.google.com/analytics/devguides/collection/gtagjs/sending-data
5645         * @link: https://developers.google.com/analytics/devguides/collection/gtagjs/events
5646         * @link: https://developers.google.com/gtagjs/reference/event
5647         * @link: https://developers.google.com/gtagjs/reference/parameter
5648         */
5649        function fireEvent(name, data) {
5650            if(typeof window.pys_event_data_filter === "function" && window.pys_disable_event_filter(event_name,'google_ads')) {
5651                return;
5652            }
5653            var _params = Utils.copyProperties(data.params,{});
5654
5655            _params.event_id = Utils.generateUniqueId(data);
5656
5657            var ids = data.ids.filter(function (pixelId) {
5658                return !Utils.hideMatchingPixel(pixelId, 'google_ads');
5659            });
5660
5661            var conversionIds = data.hasOwnProperty('conversion_ids') ? data.conversion_ids.filter(function (conversion_id) {
5662                return !Utils.hideMatchingPixel(conversion_id, 'google_ads');
5663            }) : [];
5664            var conversion_labels = data.hasOwnProperty('conversion_labels') ? data.conversion_labels.filter(function (conversion_label) {
5665                conversion_id = conversion_label.split('/')[0];
5666                return !Utils.hideMatchingPixel(conversion_id, 'google_ads');
5667            }) : [];
5668
5669            Utils.copyProperties(Utils.getRequestParams(), _params);
5670            var _fireEvent = function (conversion_id,event_name) {
5671
5672                // Same rule as in the Analytics module: no destination means the
5673                // hit is parked inside gtag.js, not sent. Reaching here with an
5674                // empty list means every configured conversion ID was filtered
5675                // out (hidden pixel, or the conversionIds fallback was empty).
5676                if (!Utils.hasSendToTarget(conversion_id)) {
5677                    if (options.debug) {
5678                        console.warn('[Google Ads] ' + event_name + ' skipped: no conversion ID left after filtering', {
5679                            ids: data.ids,
5680                            conversionIds: data.conversion_ids,
5681                            conversionLabels: data.conversion_labels
5682                        });
5683                    }
5684                    return;
5685                }
5686
5687                params = Utils.copyProperties(_params, { send_to: conversion_id });
5688
5689                if("conversion" === event_name) {
5690                    delete params.items;
5691                    delete params.ecomm_pagetype;
5692                    delete params.ecomm_prodid;
5693                    delete params.ecomm_totalvalue;
5694                }
5695
5696                if (options.debug) {
5697                    console.log('[Google Ads #' + conversion_id + '] ' + event_name, params);
5698                }
5699                Utils.gtagPush('event', event_name, params);
5700                var customEvent = new CustomEvent('gtag_event_sent', {
5701                    detail: {
5702                        eventName: event_name,
5703                        params: params,
5704                        pixel_ids: conversion_id
5705                    }
5706                });
5707                window.dispatchEvent(customEvent);
5708
5709            };
5710
5711
5712            if ( conversion_labels.length > 0 ) {
5713                ids = conversion_labels;
5714                if ( !isTrackEventForGA.includes( name ) ) {
5715                    _fireEvent( ids, name );
5716                }
5717            } else {
5718                var conversion_event_name = data.e_id;
5719                switch ( conversion_event_name ) {
5720                    case "woo_add_to_cart_on_cart_page":
5721                    case "woo_add_to_cart_on_checkout_page":
5722                    case "woo_add_to_cart_on_button_click":
5723                        conversion_event_name = 'woo_add_to_cart';
5724                        break;
5725                    case "edd_add_to_cart_on_cart_page":
5726                    case "edd_add_to_cart_on_checkout_page":
5727                    case "edd_add_to_cart_on_button_click":
5728                        conversion_event_name = 'edd_add_to_cart';
5729                        break;
5730                    case "automatic_event_adsense":
5731                    case "automatic_event_comment":
5732                    case "automatic_event_download":
5733                    case "automatic_event_email_link":
5734                    case "automatic_event_form":
5735                    case "automatic_event_internal_link":
5736                    case "automatic_event_outbound_link":
5737                    case "automatic_event_scroll":
5738                    case "automatic_event_tel_link":
5739                    case "automatic_event_time_on_page":
5740                    case "automatic_event_video":
5741                        conversion_event_name = 'automatic_event';
5742                        break;
5743                }
5744
5745                if (ids.length) {
5746                    if (conversion_event_name !== 'automatic_event' && options.hasOwnProperty('google_ads')) {
5747                        // In the same way, we determine the correct name for the settings key.
5748                        const trackEventName = conversion_event_name === 'woo_view_item_list'
5749                            ? 'woo_view_category'
5750                            : conversion_event_name;
5751
5752                        const conversionTrack = options.google_ads[trackEventName + '_conversion_track'];
5753
5754                        if (conversionTrack === 'conversion') {
5755                            _fireEvent(ids, "conversion");
5756                            ids = conversionIds;
5757                        }
5758                    }
5759                } else {
5760                    ids = conversionIds;
5761                }
5762
5763                if ( !isTrackEventForGA.includes( name ) ) {
5764                    _fireEvent( ids, name );
5765                }
5766            }
5767        }
5768
5769        function normalizeEventName(eventName) {
5770
5771            var matches = {
5772                ViewContent: 'view_item',
5773                AddToCart: 'add_to_cart',
5774                AddToWishList: 'add_to_wishlist',
5775                InitiateCheckout: 'begin_checkout',
5776                Purchase: 'purchase',
5777                Lead: 'generate_lead',
5778                CompleteRegistration: 'sign_up',
5779                AddPaymentInfo: 'set_checkout_option'
5780            };
5781
5782            return matches.hasOwnProperty(eventName) ? matches[eventName] : eventName;
5783
5784        }
5785
5786        /**
5787         * Public API
5788         */
5789        return {
5790            tag: function() {
5791                return "google_ads";
5792            },
5793            isEnabled: function () {
5794                return options.hasOwnProperty('google_ads');
5795            },
5796            getHidePixel: function(){
5797                if(this.isEnabled() && options.google_ads.hasOwnProperty('hide_pixels'))
5798                {
5799                    return options.google_ads.hide_pixels;
5800                }
5801                return [];
5802            },
5803            disable: function () {
5804                initialized = false;
5805            },
5806
5807            updateEnhancedConversionData : function () {
5808
5809                if(options.hasOwnProperty("tracking_analytics") && options.tracking_analytics.hasOwnProperty("userDataEnable") && options.tracking_analytics.userDataEnable){
5810                    var advanced = Utils.getAdvancedMergeFormData()
5811                    Utils.gtagPush('set', 'user_data', advanced);
5812                }
5813            },
5814
5815            loadPixel: function () {
5816
5817                if (initialized || !this.isEnabled() || !Utils.consentGiven('google_ads')) {
5818                    return;
5819                }
5820                var ids = options.google_ads.conversion_ids.filter(function (pixelId) {
5821                    return !Utils.hideMatchingPixel(pixelId, 'google_ads');
5822                });
5823                loadTags.push(...ids);
5824                if(!isAdsLoad && options.google_ads.conversion_ids.length > 0){
5825                    for (var i = 0; i < options.google_ads.conversion_ids.length; i++) {
5826                        var trackingId = options.google_ads.conversion_ids[i];
5827                        if (!Utils.hideMatchingPixel(trackingId, 'google_ads')) {
5828                            Utils.loadGoogleTag(trackingId);
5829                            break;
5830                        }
5831                    }
5832
5833                    // configure conversion ids
5834                    ids.forEach(function (conversion_id,index) {
5835
5836                        let config = {};
5837
5838                        if(options.google_ads.enhanced_conversion.includes("index_"+index)) {
5839                            isAllowEnhancedConversions = true;
5840                            config.allow_enhanced_conversions = true
5841                        }
5842
5843                        // Cross-Domain tracking
5844                        if (options.google_ads.crossDomainEnabled) {
5845                            config.linker = {
5846                                accept_incoming: options.google_ads.crossDomainAcceptIncoming,
5847                                domains: options.google_ads.crossDomainDomains
5848                            };
5849                        }
5850
5851                        Utils.gtagPush('config', conversion_id, config );
5852
5853                        Utils.registerGadsPhoneConversion( conversion_id );
5854
5855                        if(options.hasOwnProperty("tracking_analytics") && options.tracking_analytics.hasOwnProperty("userDataEnable") && options.tracking_analytics.userDataEnableuserDataEnable){
5856                            var advanced = Utils.getAdvancedMergeFormData()
5857                            Utils.gtagPush('set', 'user_data', advanced);
5858                        }
5859
5860                    });
5861                    isAdsLoad = true;
5862                }
5863
5864                initialized = true;
5865
5866                Utils.fireStaticEvents('google_ads');
5867
5868            },
5869
5870            fireEvent: function (name, data) {
5871
5872                if (!initialized || !this.isEnabled()) {
5873                    return false;
5874                }
5875
5876                data.delay = data.delay || 0;
5877                data.params = data.params || {};
5878                data.ids = data.ids || [];
5879                data.conversion_labels = data.conversion_labels || [];
5880
5881                if (data.delay === 0) {
5882                    fireEvent(name, data);
5883                } else {
5884                    setTimeout(function (name, data) {
5885                        fireEvent(name, data);
5886                    }, data.delay * 1000, name, data);
5887                }
5888
5889                return true;
5890
5891            },
5892
5893            onAdSenseEvent: function (event) {
5894                this.fireEvent(event.name, event);
5895            },
5896
5897            onClickEvent: function (action, params) {
5898                //disabled
5899            },
5900
5901            onWatchVideo: function (event) {
5902                this.fireEvent(event.name, event);
5903            },
5904
5905            onCommentEvent: function (event) {
5906                this.fireEvent(event.name, event);
5907            },
5908
5909            onFormEvent: function (event) {
5910                this.fireEvent(event.name, event);
5911            },
5912
5913            onDownloadEvent: function (event) {
5914                this.fireEvent(event.name, event);
5915            },
5916
5917
5918
5919
5920            onWooAddToCartOnButtonEvent: function (product_id) {
5921
5922                if(!options.dynamicEvents.woo_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
5923                    return;
5924
5925                if (window.pysWooProductData.hasOwnProperty(product_id)) {
5926                    if (window.pysWooProductData[product_id].hasOwnProperty('google_ads')) {
5927
5928                        var event = Utils.clone(options.dynamicEvents.woo_add_to_cart_on_button_click[this.tag()])
5929                        Utils.copyProperties(window.pysWooProductData[product_id]['google_ads']['params'].params, event.params)
5930                        event["ids"] = window.pysWooProductData[product_id]['google_ads']['ids']
5931                        event["conversion_labels"] = window.pysWooProductData[product_id]['google_ads']['conversion_labels']
5932                        this.fireEvent(event.name, event);
5933                    }
5934                }
5935
5936            },
5937
5938            onWooAddToCartOnSingleEvent: function (product_id, qty, product_type, is_external, $form) {
5939
5940                window.pysWooProductData = window.pysWooProductData || [];
5941                if(!options.dynamicEvents.woo_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
5942                    return;
5943                var event = Utils.clone(options.dynamicEvents.woo_add_to_cart_on_button_click[this.tag()]);
5944
5945                if (product_type === Utils.PRODUCT_VARIABLE && !options.google_ads.wooVariableAsSimple) {
5946                    product_id = parseInt($form.find('input[name="variation_id"]').val());
5947                }
5948
5949                if (window.pysWooProductData.hasOwnProperty(product_id)) {
5950                    if (window.pysWooProductData[product_id].hasOwnProperty('google_ads')) {
5951
5952                        Utils.copyProperties(window.pysWooProductData[product_id]['google_ads']["params"], event.params);
5953                        event["ids"] = window.pysWooProductData[product_id]['google_ads']['ids']
5954                        event["conversion_labels"] = window.pysWooProductData[product_id]['google_ads']['conversion_labels']
5955
5956                        var groupValue = 0;
5957                        if(product_type === Utils.PRODUCT_GROUPED ) {
5958                            $form.find(".woocommerce-grouped-product-list .qty").each(function(index){
5959                                var childId = $(this).attr('name').replaceAll("quantity[","").replaceAll("]","");
5960                                var quantity = parseInt($(this).val());
5961                                if(isNaN(quantity)) {
5962                                    quantity = 0;
5963                                }
5964
5965                                var childItem = window.pysWooProductData[product_id]['google_ads'].grouped[childId];
5966
5967                                if(options.woo.addToCartOnButtonValueEnabled &&
5968                                    options.woo.addToCartOnButtonValueOption !== 'global') {
5969
5970                                    event.params.items.forEach(function(el,index,array) {
5971                                        if(el.id == childItem.content_id) {
5972                                            if(quantity > 0){
5973                                                el.quantity = quantity;
5974                                                el.price = childItem.price;
5975                                            } else {
5976                                                array.splice(index, 1);
5977                                            }
5978                                        }
5979                                    });
5980                                }
5981                                groupValue += childItem.price * quantity;
5982                            });
5983                            if(groupValue == 0) return;
5984                            event.params.value = groupValue;
5985                        } else {
5986                            // update items qty param
5987                            event.params.items[0].quantity = qty;
5988                        }
5989
5990
5991
5992                        // maybe customize value option
5993                        if (options.woo.addToCartOnButtonValueEnabled &&
5994                            options.woo.addToCartOnButtonValueOption !== 'global' &&
5995                            product_type !== Utils.PRODUCT_GROUPED) {
5996                            event.params.value =  event.params.value * qty;
5997                        }
5998
5999
6000
6001                        var eventName = is_external ? options.woo.affiliateEventName : event.name;
6002                        eventName = normalizeEventName(eventName);
6003
6004                        this.fireEvent(eventName, event);
6005
6006                    }
6007                }
6008
6009            },
6010
6011            onWooRemoveFromCartEvent: function (event) {
6012                this.fireEvent(event.name, event);
6013            },
6014
6015            onWooAffiliateEvent: function (product_id) {
6016                if(!options.dynamicEvents.woo_affiliate.hasOwnProperty(this.tag()))
6017                    return;
6018                var event = options.dynamicEvents.woo_affiliate[this.tag()];
6019
6020                if (window.pysWooProductData.hasOwnProperty(product_id)) {
6021                    if (window.pysWooProductData[product_id].hasOwnProperty('google_ads')) {
6022
6023                        event = Utils.clone(event)
6024                        Utils.copyProperties(window.pysWooProductData[product_id][this.tag()], event.params)
6025                        this.fireEvent(normalizeEventName(options.woo.affiliateEventName), event);
6026
6027                    }
6028                }
6029
6030            },
6031
6032            onWooPayPalEvent: function (event) {
6033                this.fireEvent(event.name, event);
6034            },
6035
6036            onEddAddToCartOnButtonEvent: function (download_id, price_index, qty) {
6037                if(!options.dynamicEvents.edd_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
6038                    return;
6039                var event = options.dynamicEvents.edd_add_to_cart_on_button_click[this.tag()];
6040
6041
6042                if (window.pysEddProductData && window.pysEddProductData.hasOwnProperty(download_id)) {
6043
6044                    var index;
6045
6046                    if (price_index) {
6047                        index = download_id + '_' + price_index;
6048                    } else {
6049                        index = download_id;
6050                    }
6051
6052                    if (window.pysEddProductData[download_id].hasOwnProperty(index)) {
6053                        if (window.pysEddProductData[download_id][index].hasOwnProperty('google_ads')) {
6054
6055                            event = Utils.clone(event)
6056                            Utils.copyProperties(window.pysEddProductData[download_id][index]['google_ads']['params'], event.params);
6057                            event.ids = window.pysEddProductData[download_id][index]['google_ads']['ids']
6058                            // update items qty param
6059                            //params.items[0].quantity = qty;
6060
6061                            this.fireEvent(event.name, event);
6062
6063                        }
6064                    }
6065
6066                }
6067
6068            },
6069
6070            onEddRemoveFromCartEvent: function (event) {
6071                this.fireEvent(event.name, event);
6072            },
6073            onPageScroll: function (event) {
6074                if (initialized && this.isEnabled()) {
6075                    this.fireEvent(event.name, event);
6076                }
6077            },
6078            onTime: function (event) {
6079                if (initialized && this.isEnabled()) {
6080                    this.fireEvent(event.name, event);
6081                }
6082            },
6083        };
6084
6085    }(options);
6086
6087    var GTM = function (options) {
6088
6089        var initialized = false;
6090
6091        var datalayer_name = 'dataLayer';
6092        /*
6093         * @param name
6094         * @param data
6095         */
6096        function fireEvent(name, event) {
6097            if(typeof window.pys_event_data_filter === "function" && window.pys_disable_event_filter(name,'gtm')) {
6098                return;
6099            }
6100
6101
6102            var eventParams = event.params;
6103            var data = event.params;
6104            var valuesArray = Object.values(event.trackingIds);
6105            var ids = valuesArray.filter(function (pixelId) {
6106                return !Utils.hideMatchingPixel(pixelId, 'gtm');
6107            })
6108            Utils.copyProperties(Utils.getRequestParams(), eventParams);
6109            var _fireEvent = function (tracking_id,name,params, event=null) {
6110
6111                var eventData = {};
6112                var ContainerCodeHasTag = !options.gtm.gtm_just_data_layer && tracking_id.length > 0;
6113                if(ContainerCodeHasTag) {
6114                    params['send_to'] = tracking_id;
6115                }
6116                else if(!options.gtm.gtm_just_data_layer){
6117                    return
6118                }
6119
6120                if (params.hasOwnProperty('ecommerce')) {
6121                    eventData.ecommerce = params.ecommerce;
6122                    delete params.ecommerce;
6123                }
6124
6125                var automatedParams = { ...params };
6126                [params['manualName'], 'manualName', 'triggerType'].forEach(key => delete automatedParams[key]);
6127                if(event && (!event.hasOwnProperty('hasAutoParam') || (event.hasOwnProperty('hasAutoParam') && event.hasAutoParam))) {
6128                    eventData.automatedParameters = automatedParams;
6129                }
6130
6131// Move custom parameters to eventData
6132
6133                if (params.hasOwnProperty(params['manualName'])) {
6134                    eventData[params['manualName']] = params[params['manualName']];
6135                    delete params[params['manualName']];
6136                }
6137
6138                ['manualName','triggerType'].forEach(key => {
6139                    if (params.hasOwnProperty(key)) {
6140                        eventData[key] = params[key];
6141                        delete params[key];
6142                    }
6143                });
6144
6145                eventData.manualDataLayer = options.gtm.gtm_dataLayer_name ?? 'dataLayer';
6146
6147                eventData.event = name;
6148
6149                if (options.debug) {
6150                    if (ContainerCodeHasTag){
6151                        console.log('[Google GTM #' + tracking_id + '] ' + name, eventData);
6152                    }
6153                    else {
6154                        console.log('[Google GTM push to "'+datalayer_name+'"] ' + name, eventData);
6155                    }
6156                }
6157                window[datalayer_name].push(eventData);
6158
6159                var customEvent = new CustomEvent('gtm_event_sent', {
6160                    detail: {
6161                        eventName: name,
6162                        params: eventData,
6163                        pixel_ids: tracking_id
6164                    }
6165                });
6166                window.dispatchEvent(customEvent);
6167
6168            };
6169
6170            var copyParams = Utils.copyProperties(eventParams, {}); // copy params because mapParamsToGTM can modify it
6171
6172            var params = mapParamsToGTM(ids,name,copyParams)
6173
6174            params.event_id = Utils.generateUniqueId(event);
6175
6176            _fireEvent(ids, name, params, event);
6177        }
6178
6179        function normalizeEventName(eventName) {
6180
6181            var matches = {
6182                ViewContent: 'view_item',
6183                AddToCart: 'add_to_cart',
6184                AddToWishList: 'add_to_wishlist',
6185                InitiateCheckout: 'begin_checkout',
6186                Purchase: 'purchase',
6187                Lead: 'generate_lead',
6188                CompleteRegistration: 'sign_up',
6189                AddPaymentInfo: 'set_checkout_option'
6190            };
6191
6192            return matches.hasOwnProperty(eventName) ? matches[eventName] : eventName;
6193
6194        }
6195
6196        function mapParamsToGTM(tag,name,param) {
6197            var hasGTM = false;
6198
6199            // end
6200            if (Array.isArray(tag)) {
6201                hasGTM = tag.some(function (element) {
6202                    return isGTM(element);
6203                });
6204            } else if(isGTM(tag)) {
6205                hasGTM = true;
6206            }
6207            if(hasGTM) {
6208                delete param.event_category;
6209                delete param.event_label;
6210
6211                delete param.analytics_storage;
6212                delete param.ad_storage;
6213                delete param.ad_user_data;
6214                delete param.ad_personalization;
6215
6216                if(name === 'search') {
6217                    param['search'] = param.search_term;
6218                    delete param.search_term;
6219                    delete param.dynx_itemid;
6220                    delete param.dynx_pagetype;
6221                    delete param.dynx_totalvalue;
6222                }
6223            }
6224            return param;
6225        }
6226
6227        function isGTM(tag) {
6228            return tag.indexOf('GTM') === 0;
6229        }
6230        /**
6231         * Public API
6232         */
6233        return {
6234            tag: function() {
6235                return "gtm";
6236            },
6237            isEnabled: function () {
6238                return options.hasOwnProperty('gtm');
6239            },
6240            getHidePixel: function(){
6241                if(this.isEnabled() && options.gtm.hasOwnProperty('hide_pixels'))
6242                {
6243                    return options.gtm.hide_pixels;
6244                }
6245                return [];
6246            },
6247            disable: function () {
6248                initialized = false;
6249            },
6250            updateEnhancedConversionData : function () {
6251                if (!initialized || !this.isEnabled()) {
6252                    return;
6253                }
6254                if(options.hasOwnProperty("tracking_analytics") && options.tracking_analytics.hasOwnProperty("userDataEnable") && options.tracking_analytics.userDataEnable) {
6255                    var advanced = Utils.getAdvancedMergeFormData(true);
6256                    if (Object.keys(advanced).length > 0) {
6257                        window[datalayer_name].push({'user_data' : advanced});
6258                    }
6259                }
6260            },
6261            loadPixel: function () {
6262
6263                if (initialized || !this.isEnabled() || !Utils.consentGiven('analytics')) {
6264                    return;
6265                }
6266                datalayer_name = options.gtm.gtm_dataLayer_name ?? 'dataLayer';
6267
6268                for (var i = 0; i < options.gtm.trackingIds.length; i++) {
6269                    var trackingId = options.gtm.trackingIds[i];
6270                    if (!Utils.hideMatchingPixel(trackingId, 'gtm') && !options.gtm.gtm_just_data_layer) {
6271                        console.log('[PYS] Google Tag Manager container code loaded');
6272                        Utils.loadGTMScript(trackingId);
6273                        break;
6274                    }
6275                }
6276
6277                if(options.gtm.gtm_just_data_layer) {
6278                    console.warn && console.warn("[PYS] Google Tag Manager container code placement set to OFF !!!");
6279                    console.warn && console.warn("[PYS] Data layer codes are active but GTM container must be loaded using custom coding !!!");
6280                    // Push consent defaults even when GTM script loading is handled externally.
6281                    // Fixes the case where gtm_just_data_layer=true with tracking IDs configured,
6282                    // which previously skipped loadGTMScript() entirely and never pushed defaults.
6283                    Utils.pushGTMConsentDefaults( datalayer_name );
6284                    if(options.gtm.trackingIds.length == 0){
6285                        Utils.loadGTMScript();
6286                    }
6287                }
6288
6289                if(options.hasOwnProperty("tracking_analytics") && options.tracking_analytics.hasOwnProperty("userDataEnable") && options.tracking_analytics.userDataEnable){
6290                    var advanced = Utils.getAdvancedMergeFormData(true);
6291                    if(Object.keys(advanced).length > 0){
6292                        window[datalayer_name].push({'user_data' : advanced});
6293                    }
6294                }
6295
6296
6297                var config = {};
6298
6299                if(options.user_id && options.user_id != 0) {
6300                    config.user_id = options.user_id;
6301                }
6302
6303                // Cross-Domain tracking
6304
6305                var ids = options.gtm.trackingIds.filter(function (pixelId) {
6306                    return !Utils.hideMatchingPixel(pixelId, 'gtm');
6307                });
6308
6309
6310                initialized = true;
6311
6312                Utils.fireStaticEvents('gtm');
6313            },
6314
6315            fireEvent: function (name, data) {
6316
6317                if (!initialized || !this.isEnabled()) {
6318                    return false;
6319                }
6320
6321                data.delay = data.delay || 0;
6322                data.params = data.params || {};
6323
6324                if (data.delay === 0) {
6325
6326                    fireEvent(name, data);
6327
6328                } else {
6329
6330                    setTimeout(function (name, params) {
6331                        fireEvent(name, params);
6332                    }, data.delay * 1000, name, data);
6333
6334                }
6335
6336                return true;
6337
6338            },
6339
6340            onAdSenseEvent: function (event) {
6341                this.fireEvent(event.name, event);
6342            },
6343
6344            onClickEvent: function (event) {
6345                this.fireEvent(event.name, event);
6346            },
6347
6348            onWatchVideo: function (event) {
6349                if(!event.hasOwnProperty("youtube_disabled")
6350                    || !event.youtube_disabled
6351                    || event.params.video_type !== "youtube") {
6352                    this.fireEvent(event.name, event);
6353                }
6354            },
6355
6356            onCommentEvent: function (event) {
6357
6358                this.fireEvent(event.name, event);
6359
6360            },
6361
6362            onFormEvent: function (event) {
6363
6364                this.fireEvent(event.name, event);
6365
6366            },
6367
6368            onDownloadEvent: function (event) {
6369
6370                this.fireEvent(event.name, event);
6371
6372            },
6373
6374            onWooAddToCartOnButtonEvent: function (product_id, prod_info = null) {
6375                if(!options.dynamicEvents.woo_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
6376                    return;
6377                if (window.pysWooProductData.hasOwnProperty(product_id)) {
6378                    if (window.pysWooProductData[product_id].hasOwnProperty('gtm')) {
6379                        var event = Utils.clone(options.dynamicEvents.woo_add_to_cart_on_button_click[this.tag()]);
6380                        Utils.copyProperties(window.pysWooProductData[product_id]['gtm'].params, event.params)
6381                        event.trackingIds = window.pysWooProductData[product_id]['gtm']['trackingIds'];
6382
6383
6384                        if(prod_info)
6385                        {
6386                            item = event.params.hasOwnProperty('ecommerce') ? event.params.ecommerce.items[0] : event.params.items[0];
6387
6388                            if(prod_info['pys_list_name_productlist_id'])
6389                            {
6390                                item['item_list_id'] = prod_info['pys_list_name_productlist_id']
6391                            }
6392                            if(prod_info['pys_list_name_productlist_name'])
6393                            {
6394                                item['item_list_name'] =prod_info['pys_list_name_productlist_name']
6395                            }
6396                        }
6397                        this.fireEvent(event.name, event);
6398                    }
6399                }
6400
6401            },
6402
6403            onWooAddToCartOnSingleEvent: function (product_id, qty, product_type, is_external, $form, prod_info) {
6404                window.pysWooProductData = window.pysWooProductData || [];
6405
6406                if(!options.dynamicEvents.woo_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
6407                    return;
6408                var event = Utils.clone(options.dynamicEvents.woo_add_to_cart_on_button_click[this.tag()]);
6409
6410                if (product_type === Utils.PRODUCT_VARIABLE && !options.gtm.wooVariableAsSimple) {
6411                    product_id = parseInt($form.find('input[name="variation_id"]').val());
6412                }
6413
6414                if (window.pysWooProductData.hasOwnProperty(product_id)) {
6415                    if (window.pysWooProductData[product_id].hasOwnProperty('gtm')) {
6416
6417                        Utils.copyProperties(window.pysWooProductData[product_id]['gtm'].params, event.params);
6418
6419                        params = event.params.hasOwnProperty('ecommerce') ? event.params.ecommerce : event.params;
6420
6421                        if(product_type === Utils.PRODUCT_GROUPED ) {
6422                            var groupValue = 0;
6423                            $form.find(".woocommerce-grouped-product-list .qty").each(function(index){
6424                                var childId = $(this).attr('name').replaceAll("quantity[","").replaceAll("]","");
6425                                var quantity = parseInt($(this).val());
6426                                if(isNaN(quantity)) {
6427                                    quantity = 0;
6428                                }
6429                                var childItem = window.pysWooProductData[product_id]['gtm'].grouped[childId];
6430
6431                                params.items.forEach(function(el,index,array) {
6432                                    if(el.id == childItem.content_id) {
6433                                        if(quantity > 0){
6434                                            el.quantity = quantity;
6435                                            el.price = childItem.price;
6436                                        } else {
6437                                            array.splice(index, 1);
6438                                        }
6439                                    }
6440                                });
6441                                groupValue += childItem.price * quantity;
6442                            });
6443
6444
6445
6446                            if(options.woo.addToCartOnButtonValueEnabled &&
6447                                options.woo.addToCartOnButtonValueOption !== 'global' &&
6448                                params.hasOwnProperty('value')) {
6449                                params.value = groupValue;
6450                            }
6451
6452                            if(groupValue == 0) return; // skip if no items selected
6453                        } else {
6454                            // update items qty param
6455                            params.items[0].quantity = qty;
6456                        }
6457
6458                        // maybe customize value option
6459                        if (options.woo.addToCartOnButtonValueEnabled &&
6460                            options.woo.addToCartOnButtonValueOption !== 'global' &&
6461                            product_type !== Utils.PRODUCT_GROUPED)
6462                        {
6463                            if(params.hasOwnProperty('value')) {
6464                                params.value = params.items[0].price * qty;
6465                            }
6466                        }
6467
6468                        if(prod_info)
6469                        {
6470                            if(prod_info['pys_list_name_productlist_id'])
6471                            {
6472                                params.items[0]['item_list_id'] = prod_info['pys_list_name_productlist_id']
6473                            }
6474                            if(prod_info['pys_list_name_productlist_name'])
6475                            {
6476                                params.items[0]['item_list_name'] =prod_info['pys_list_name_productlist_name']
6477                            }
6478                        }
6479
6480                        var eventName = is_external ? options.woo.affiliateEventName : event.name;
6481                        eventName = normalizeEventName(eventName);
6482
6483                        this.fireEvent(eventName, event);
6484
6485                    }
6486                }
6487
6488            },
6489
6490            onWooCheckoutProgressStep: function (event) {
6491                this.fireEvent(event.name, event);
6492            },
6493
6494            onWooSelectContent: function (event) {
6495                const select_prod_list = {};
6496
6497                if (event.params.items[0].item_list_name !== undefined) {
6498                    select_prod_list.list_name = event.params.items[0].item_list_name;
6499                }
6500
6501                if (event.params.items[0].item_list_id !== undefined) {
6502                    select_prod_list.list_id = event.params.items[0].item_list_id;
6503                }
6504                const url = new URL(window.location.href);
6505                select_prod_list.url = url.origin + url.pathname;
6506                Cookies.set('select_prod_list', JSON.stringify(select_prod_list), { expires: 1,path: '/' });
6507                this.fireEvent(event.name, event);
6508            },
6509
6510            onWooRemoveFromCartEvent: function (event) {
6511                this.fireEvent(event.name, event);
6512            },
6513
6514            onWooAffiliateEvent: function (product_id) {
6515                if(!options.dynamicEvents.woo_affiliate.hasOwnProperty(this.tag()))
6516                    return;
6517                var event = options.dynamicEvents.woo_affiliate[this.tag()];
6518
6519                if (window.pysWooProductData.hasOwnProperty(product_id)) {
6520                    if (window.pysWooProductData[product_id].hasOwnProperty('gtm')) {
6521
6522                        event = Utils.clone(event );
6523                        Utils.copyProperties(window.pysWooProductData[product_id][this.tag()], event.params)
6524                        this.fireEvent(normalizeEventName(options.woo.affiliateEventName), event);
6525
6526                    }
6527                }
6528
6529            },
6530
6531            onWooPayPalEvent: function (event) {
6532                this.fireEvent(event.name, event);
6533            },
6534
6535            onEddAddToCartOnButtonEvent: function (download_id, price_index, qty) {
6536                if(!options.dynamicEvents.edd_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
6537                    return;
6538                var event = Utils.clone(options.dynamicEvents.edd_add_to_cart_on_button_click[this.tag()]);
6539
6540
6541                if (window.pysEddProductData && window.pysEddProductData.hasOwnProperty(download_id)) {
6542
6543                    var index;
6544
6545                    if (price_index) {
6546                        index = download_id + '_' + price_index;
6547                    } else {
6548                        index = download_id;
6549                    }
6550
6551                    if (window.pysEddProductData[download_id].hasOwnProperty(index)) {
6552                        if (window.pysEddProductData[download_id][index].hasOwnProperty('gtm')) {
6553
6554                            Utils.copyProperties(window.pysEddProductData[download_id][index]['gtm'].params, event.params);
6555                            item = event.params.hasOwnProperty('ecommerce') ? event.params.ecommerce.items[0] : event.params.items[0];
6556                            // update items qty param
6557                            item.quantity = qty;
6558
6559                            this.fireEvent(event.name,event);
6560
6561                        }
6562                    }
6563
6564                }
6565
6566            },
6567
6568            onEddRemoveFromCartEvent: function (event) {
6569                this.fireEvent(event.name, event);
6570            },
6571
6572            onPageScroll: function (event) {
6573                if (initialized && this.isEnabled()) {
6574                    this.fireEvent(event.name, event);
6575                }
6576            },
6577            onTime: function (event) {
6578                if (initialized && this.isEnabled()) {
6579                    this.fireEvent(event.name, event);
6580                }
6581            },
6582        };
6583
6584    }(options);
6585
6586    var getPixelBySlag = function getPixelBySlag(slug) {
6587        switch (slug) {
6588            case "facebook": return window.pys.Facebook;
6589            case "ga": return window.pys.Analytics;
6590            case "gtm": return window.pys.GTM;
6591            case "google_ads": return window.pys.GAds;
6592            case "bing": return window.pys.Bing;
6593            case "pinterest": return window.pys.Pinterest;
6594            case "tiktok": return window.pys.TikTok;
6595            case "reddit": return window.pys.Reddit;
6596        }
6597    }
6598
6599    window.pys = window.pys || {};
6600    window.pys.Facebook = Facebook;
6601    window.pys.Analytics = Analytics;
6602    window.pys.GAds = GAds;
6603    window.pys.GTM = GTM;
6604    window.pys.Utils = Utils;
6605    window.pys.TikTok = TikTok;
6606    window.pys.getPixelBySlag = getPixelBySlag;
6607
6608    // Allow external scripts (e.g. generatePixelCode fragments) to refresh
6609    // the uniqueId cache for a given event key, so that each new add_to_cart
6610    // action gets a fresh shared ID across all pixels.
6611    window.pys.setEventUniqueId = function(idKey, newId) {
6612        uniqueId[idKey] = newId;
6613    };
6614
6615    window.pys.fireAjaxTriggerEvent = function(slug, eventData) {
6616        var pixel = getPixelBySlag(slug);
6617        if (!pixel) return;
6618
6619        // Extract the eventId the same way PHP does (custom_event_post_id takes priority over e_id).
6620        var eventId = (eventData.custom_event_post_id || eventData.e_id) || null;
6621        // Mirror fireTriggerEvent: respect "once per page" frequency setting.
6622        if (eventId && options.triggerFrequency && options.triggerFrequency[eventId] === 'once') {
6623            if (pysEventFiredOnce[eventId]) return;
6624            pysEventFiredOnce[eventId] = true;
6625        }
6626
6627        // Mirror fireTriggerEvent: respect the time-window setting (data already in eventData).
6628        if (!Utils.isEventInTimeWindow(eventData.name, eventData, 'ajax_' + slug)) return;
6629
6630        pixel.fireEvent(eventData.name, eventData);
6631    };
6632
6633    // Export getPixelBySlag globally for backward compatibility
6634    window.getPixelBySlag = getPixelBySlag;
6635
6636    function pysNormalInit() {
6637
6638        if(Cookies.get('form_track'))
6639        {
6640            Cookies.remove('form_track')
6641        }
6642
6643        if($("#pys_late_event").length > 0) {
6644            var dirAttr = $("#pys_late_event").attr("dir");
6645            if (dirAttr) {
6646                try {
6647                    var events = JSON.parse(dirAttr);
6648                } catch (e) {
6649                    console.warn("Invalid JSON in pys_late_event dir attribute:", e);
6650                }
6651            } else {
6652                console.warn("pys_late_event dir attribute is undefined or empty");
6653            }
6654            if (events) {
6655                for (var platform in events) {
6656                    if (events.hasOwnProperty(platform)) {
6657                        var platformEvents = events[platform];
6658                        platformEvents.forEach(function (event) {
6659                            var eventData = {};
6660                            eventData[event.e_id] = [event];
6661                            if (options.staticEvents.hasOwnProperty(platform)) {
6662                                Object.assign(options.staticEvents[platform], eventData);
6663                            } else {
6664                                options.staticEvents[platform] = eventData;
6665                            }
6666                        });
6667                    }
6668                }
6669            }
6670        }
6671
6672        var Pinterest = Utils.setupPinterestObject();
6673        var Bing = Utils.setupBingObject();
6674        var Reddit = Utils.setupRedditObject();
6675        if(options.hasOwnProperty('cookie'))
6676        {
6677            if(options.cookie.externalID_disabled_by_api || options.cookie.disabled_all_cookie)
6678            {
6679                Cookies.remove('pbid')
6680            }
6681            if(options.cookie.disabled_advanced_form_data_cookie || options.cookie.disabled_all_cookie)
6682            {
6683                Cookies.remove('pys_advanced_form_data')
6684            }
6685
6686            if(options.cookie.disabled_landing_page_cookie || !options.enable_lading_page_param || options.cookie.disabled_all_cookie)
6687            {
6688                Cookies.remove('pys_landing_page', { path: '/',domain: domain })
6689                Cookies.remove('last_pys_landing_page', { path: '/',domain: domain })
6690            }
6691            if(options.cookie.disabled_trafficsource_cookie || options.cookie.disabled_all_cookie)
6692            {
6693                Cookies.remove('pysTrafficSource', { path: '/',domain: domain })
6694                Cookies.remove('last_pysTrafficSource', { path: '/',domain: domain })
6695            }
6696            if(options.cookie.disabled_first_visit_cookie || options.cookie.disabled_all_cookie)
6697            {
6698                Cookies.remove('pys_first_visit', { path: '/',domain: domain })
6699
6700            }
6701            if(options.cookie.disabled_utmTerms_cookie || options.cookie.disabled_all_cookie)
6702            {
6703                $.each(Utils.utmTerms, function (index, name) {
6704                    Cookies.remove('pys_' + name, { path: '/',domain: domain })
6705                });
6706                $.each(Utils.utmTerms, function (index, name) {
6707                    Cookies.remove('last_pys_' + name, { path: '/',domain: domain })
6708                });
6709            }
6710            if(options.cookie.disabled_utmId_cookie || options.cookie.disabled_all_cookie)
6711            {
6712                $.each(Utils.utmId,function(index,name) {
6713                    Cookies.remove('pys_' + name, { path: '/',domain: domain })
6714                })
6715                $.each(Utils.utmId,function(index,name) {
6716                    Cookies.remove('last_pys_' + name, { path: '/',domain: domain })
6717                });
6718            }
6719        }
6720
6721        if (options.gdpr.cookie_law_info_integration_enabled) {
6722            var cli_cookie = Cookies.get('cookieyes-consent') ?? Cookies.get('wt_consent') ?? Cookies.get('viewed_cookie_policy');
6723
6724            if (typeof cli_cookie !== 'undefined') {
6725                if (cli_cookie === Cookies.get('cookieyes-consent') || cli_cookie === Cookies.get('wt_consent')) {
6726                    if (getCookieYes('analytics') === 'yes') {
6727                        Utils.manageCookies();
6728                    }
6729                } else if (cli_cookie === Cookies.get('viewed_cookie_policy') && cli_cookie == 'yes') {
6730                    Utils.manageCookies();
6731                }
6732            }
6733        }
6734
6735        if ( options.gdpr.consent_magic_integration_enabled && typeof CS_Data !== "undefined" ) {
6736            if ( CS_Data.cs_script_cat.pys == CS_Data.cs_necessary_cat_id || CS_Data.cs_script_cat.pys == 0 ) {
6737                Utils.manageCookies();
6738            } else if ( Cookies.get( 'cs_enabled_cookie_term' + CS_Data.test_prefix + '_' + CS_Data.cs_script_cat.pys ) == 'yes' ) {
6739                Utils.manageCookies();
6740            }
6741        } else {
6742            Utils.manageCookies();
6743        }
6744
6745        Utils.initializeRequestParams();
6746        Utils.setupGdprCallbacks();
6747
6748        if ( options.enable_auto_save_advance_matching ) {
6749            let override = options.data_persistency == 'recent_data';
6750            // Setup Advanced Form Data
6751            if ( options.advance_matching_form.enable_advance_matching_forms ) {
6752
6753                // Handle Email fields
6754                $( document ).on( "blur", "input[type='email']", function () {
6755                    // SECURITY: Ignore fields located inside the Authorize.Net payment block
6756                    if ( $( this ).closest('.payment_method_anet').length > 0 ) return;
6757
6758                    let email = $( this ).val().trim().toLowerCase();
6759                    if ( Utils.validateEmail( email ) ) {
6760                        Utils.saveAdvancedFormData( email, null, null, null, override );
6761                    }
6762                } )
6763
6764                // Handle Telephone fields (This is where Credit Card numbers were being captured)
6765                $( document ).on( "blur", "input[type='tel']", function () {
6766                    // SECURITY: Ignore fields located inside the Authorize.Net payment block
6767                    if ( $( this ).closest('.payment_method_anet').length > 0 ) return;
6768
6769                    let phone = $( this ).val().trim().replace( /\D/g, "" );
6770                    if ( phone.length > 5 ) {
6771                        Utils.saveAdvancedFormData( null, phone, null, null, override );
6772                    }
6773                } )
6774
6775                // Handle Text fields
6776                $( document ).on( "blur", "input[type='text']", function () {
6777                    // SECURITY: Ignore fields located inside the Authorize.Net payment block
6778                    if ( $( this ).closest('.payment_method_anet').length > 0 ) return;
6779
6780                    let name;
6781                    if ( $( this ).attr( "name" ) && $( this ).attr( "name" ) != '' ) {
6782                        name = $( this ).attr( "name" ).trim()
6783                    }
6784                    if ( name && options.advance_matching_form.advance_matching_fn_names.includes( name ) ) {
6785                        let value = $( this ).val().trim();
6786                        if ( value.length > 0 ) {
6787                            Utils.saveAdvancedFormData( null, null, value, null, override );
6788                        }
6789                    }
6790                    if ( name && options.advance_matching_form.advance_matching_ln_names.includes( name ) ) {
6791                        let value = $( this ).val().trim();
6792                        if ( value.length > 0 ) {
6793                            Utils.saveAdvancedFormData( null, null, null, value, override );
6794                        }
6795                    }
6796                    if ( name && options.advance_matching_form.advance_matching_tel_names.includes( name ) ) {
6797                        let value = $( this ).val().trim();
6798                        if ( value.length > 0 ) {
6799                            Utils.saveAdvancedFormData( null, value, null, null, override );
6800                        }
6801                    }
6802                    if ( name && options.advance_matching_form.advance_matching_em_names.includes( name ) ) {
6803                        let email = $( this ).val().trim().toLowerCase();
6804
6805                        if ( Utils.validateEmail( email ) ) {
6806                            Utils.saveAdvancedFormData( email, null, null, null, false );
6807                        }
6808                    }
6809                } )
6810            }
6811
6812            //Setup Advanced Url Data
6813            if ( Object.keys( options.advance_matching_url ).length > 0 && options.advance_matching_url.enable_advance_matching_url ) {
6814                const url_params = new URLSearchParams( window.location.search );
6815                url_params.forEach( ( value, key ) => {
6816                    if ( options.advance_matching_url.advance_matching_fn_names.includes( key ) ) {
6817                        Utils.saveAdvancedFormData( null, null, value.trim(), null, override );
6818                    }
6819
6820                    if ( options.advance_matching_url.advance_matching_ln_names.includes( key ) ) {
6821                        Utils.saveAdvancedFormData( null, null, null, value.trim(), override );
6822                    }
6823
6824                    if ( options.advance_matching_url.advance_matching_tel_names.includes( key ) ) {
6825                        Utils.saveAdvancedFormData( null, value.trim(), null, null, override );
6826                    }
6827
6828                    if ( options.advance_matching_url.advance_matching_em_names.includes( key ) ) {
6829                        let email = value.trim().toLowerCase()
6830                        if ( Utils.validateEmail( email ) ) {
6831                            Utils.saveAdvancedFormData( email, null, null, null, override );
6832                        }
6833                    }
6834                } );
6835            }
6836        }
6837
6838        // setup Click Event
6839        if (
6840            options.dynamicEvents.hasOwnProperty("automatic_event_internal_link") ||
6841            options.dynamicEvents.hasOwnProperty("automatic_event_outbound_link") ||
6842            options.dynamicEvents.hasOwnProperty("automatic_event_tel_link") ||
6843            options.dynamicEvents.hasOwnProperty("automatic_event_email_link") ||
6844            options.dynamicEvents.hasOwnProperty("automatic_event_download")
6845        ) {
6846
6847            $(document).onFirst('click', 'a, button, input[type="button"], input[type="submit"]', function (e) {
6848
6849                var $elem = $(this);
6850
6851
6852                // Download
6853                if(options.dynamicEvents.hasOwnProperty("automatic_event_download")) {
6854                    var isFired = false;
6855                    if ($elem.is('a')) {
6856                        var href = $elem.attr('href');
6857                        if (typeof href !== "string") {
6858                            return;
6859                        }
6860                        href = href.trim();
6861                        var extension = Utils.getLinkExtension(href);
6862                        var track_download = false;
6863
6864                        if (extension.length > 0) {
6865
6866                            if(options.dynamicEvents.hasOwnProperty("automatic_event_download") ) {
6867                                var pixels = Object.keys(options.dynamicEvents.automatic_event_download);
6868                                for (var i = 0; i < pixels.length; i++) {
6869                                    var event = Utils.clone(options.dynamicEvents.automatic_event_download[pixels[i]]);
6870                                    var extensions = event.extensions;
6871                                    if (extensions.includes(extension)) {
6872
6873                                        if(pixels[i] == "tiktok" || pixels[i] == "reddit") {
6874                                            getPixelBySlag(pixels[i]).fireEvent(event.name, event);
6875                                        } else {
6876                                            if (options.enable_remove_download_url_param) {
6877                                                href = stripUrlQuery(href);
6878                                            }
6879                                            event.params.download_url = href;
6880                                            event.params.download_type = extension;
6881                                            event.params.download_name = Utils.getLinkFilename(href);
6882                                            getPixelBySlag(pixels[i]).onDownloadEvent(event);
6883                                        }
6884
6885                                        isFired = true;
6886                                    }
6887                                }
6888                            }
6889                        }
6890                    }
6891                    if(isFired) { // prevent duplicate events on the same element
6892                        return;
6893                    }
6894                }
6895
6896
6897                if (!e.hasOwnProperty('originalEvent')) {
6898                    return;
6899                }
6900                if($elem.hasClass("add_to_cart_button") ||
6901                    $elem.hasClass("single_add_to_cart_button") ) { // add_to_cart_button fire in woo
6902                    return;
6903                }
6904
6905                if(options.dynamicEvents.hasOwnProperty("wcf_add_to_cart_on_next_step_click")
6906                    && $elem.hasClass("wcf-next-step-link")) {
6907                    return;// add_to_cart_button fire in woo cf
6908                }
6909                if(options.dynamicEvents.hasOwnProperty("wcf_add_to_cart_on_bump_click")
6910                    && $elem.hasClass("wcf-bump-order-cb")) {
6911                    return;// add_to_cart_button fire in woo cf
6912                }
6913
6914                if($elem.hasClass("remove_from_cart_button") ) { // cancel remove from cart
6915                    return;
6916                }
6917                if($elem.hasClass("remove") ) { // cancel remove from cart
6918                    if($elem.parents('.cart_item').length || $elem.parents('.mini_cart_item').length)
6919                        return;
6920                }
6921                if($elem.attr("name") == "update_cart" || $elem.attr("name") == "apply_coupon") { // cancel update  c
6921art or coupon button
6922                    return;
6923                }
6924
6925
6926                if ($elem.hasClass('pys_block')) {
6927                    return; // avoiding fake double clicks from Affiliate event
6928                }
6929                var text = "";
6930                var target_url = "";
6931                var linkType = "Internal Click";
6932
6933                if ($elem.is('a')) {
6934                    var href = $elem.attr('href');
6935
6936                    // fixes #112
6937                    if (typeof href !== "string") {
6938                        return;
6939                    }
6940                    href = href.trim();
6941
6942                    text = $elem.contents().filter(function() {
6943                        return this.nodeType === 3; // Filter text nodes only
6944                    }).text().trim();
6945                    if(options.enable_remove_target_url_param) {
6946                        target_url = stripUrlQuery(href);
6947                    } else {
6948                        target_url = href
6949                    }
6950
6951
6952                    //Email Event
6953                    if ( href.startsWith( 'mailto:' ) ) {
6954                        if ( options.triggerEventTypes.hasOwnProperty( "email_link" ) ) {
6955                            return;
6956                        } else {
6957                            setupEmailLinks();
6958                        }
6959
6960                        return; // not fire next
6961                    }
6962
6963                    // Phone
6964                    if (href.startsWith('tel:')) {
6965                        if(options.dynamicEvents.hasOwnProperty("automatic_event_tel_link")) {
6966                            var pixels = Object.keys(options.dynamicEvents.automatic_event_tel_link);
6967                            for(var i = 0;i<pixels.length;i++) {
6968                                var event = Utils.clone(options.dynamicEvents.automatic_event_tel_link[pixels[i]]);
6969                                if ( pixels[i] !== 'tiktok' && pixels[i] !== 'reddit') {
6970                                    Utils.copyProperties(Utils.getRequestParam
6970s(), event.params);
6971                                }
6972                                getPixelBySlag(pixels[i]).fireEvent(event.name, event);
6973                            }
6974                        }
6975                        return; // not fire next
6976                    }
6977
6978                    if (href.startsWith('http')) {
6979                        // link type
6980                        var host = $elem.context != undefined ? $elem.context.host : $elem[0].host;
6981                        if (document.location.host != host) {
6982                            linkType = 'External Click';
6983                        }
6984                    }
6985                } else if ($elem.is('button')) {
6986                    if( $elem.hasClass("forminator-button-submit")) {
6987                        //disable duplicate events
6988                        return;
6989                    }
6990                    text = $elem.contents().filter(function() {
6991                        return this.nodeType === 3; // Filter text nodes only
6992                    }).text().trim();
6993                } else if ($elem.is('input[type="button"]')) {
6994                    text = $elem.val();
6995                } else if ($elem.is('input[type="submit"]')) {
6996                    if ( $elem.parents("form.comment-form")) {
6997                        //disable duplicate events
6998                        return;
6999                    }
7000                    if ($elem.parents("form")) {
7001                        //disable duplicate events
7002                        return;
7003                    }
7004                    text = $elem.val();
7005                } else {
7006                    return;
7007                }
7008
7009
7010
7011                text = Utils.filterEmails(text);
7012
7013
7014                if( linkType === "Internal Click"
7015                    && options.dynamicEvents.hasOwnProperty("automatic_event_internal_link")
7016                ) {
7017                    var pixels = Object.keys(options.dynamicEvents.automatic_event_internal_link);
7018
7019                    for(var i = 0;i<pixels.length;i++) {
7020                        var event = Utils.clone(options.dynamicEvents.automatic_event_internal_link[pixels[i]]);
7021
7022                        if(pixels[i] !== "tiktok" && pixels[i] !== "reddit") { // TT and reddit don't support custom parameters
7023                            event.params["text"] = text;
7024                            if(target_url){
7025                                event.params["target_url"] = target_url;
7026                            }
7027                            Utils.copyProperties(Utils.getRequestParam
7027s(), event.params);
7028                        }
7029
7030
7031                        getPixelBySlag(pixels[i]).fireEvent(event.name, event);
7032                    }
7033                }
7034
7035                if( linkType === "External Click"
7036                    && options.dynamicEvents.hasOwnProperty("automatic_event_outbound_link")
7037                ) {
7038                    var pixels = Object.keys(options.dynamicEvents.automatic_event_outbound_link);
7039
7040                    for(var i = 0;i<pixels.length;i++) {
7041                        var event = Utils.clone(options.dynamicEvents.automatic_event_outbound_link[pixels[i]]);
7042
7043                        if(pixels[i] !== "tiktok" && pixels[i] !== "reddit") { // TT and reddit don't support custom parameters
7044                            event.params["text"] = text;
7045                            if(target_url){
7046                                event.params["target_url"] = target_url;
7047                            }
7048                            Utils.copyProperties(Utils.getRequestParams(), event.params);
7049                        }
7050
7051
7052                        getPixelBySlag(pixels[i]).fireEvent(event.name, event);
7053                    }
7054                }
7055
7056
7057
7058
7059            });
7060
7061        }
7062
7063        // setup AdSense Event
7064        if (options.dynamicEvents.hasOwnProperty("automatic_event_adsense")) {
7065
7066            var isOverGoogleAd = false;
7067
7068            $(document)
7069                .on('mouseover', 'ins iframe', function () { //class adsbygoogle adsbygoogle-noablate ??
7070                    isOverGoogleAd = true;
7071                })
7072                .on('mouseout', 'iframe', function () {
7073                    isOverGoogleAd = false;
7074                });
7075
7076            $(window)
7077                .on( "blur",function () {
7078                    if (isOverGoogleAd) {
7079                        if(options.dynamicEvents.hasOwnProperty("automatic_event_adsense")) {
7080                            var pixels = Object.keys(options.dynamicEvents.automatic_event_adsense);
7081                            for (var i = 0; i < pixels.length; i++) {
7082                                var event = Utils.clone(options.dynamicEvents.automatic_event_adsense[pixels[i]]);
7083                                if ( pixels[i] !== 'tiktok' && pixels[i] !== 'reddit') {
7084                                    Utils.copyProperties(Utils.getRequestParam
7084s(), event.params);
7085                                }
7086                                getPixelBySlag(pixels[i]).onAdSenseEvent(event);
7087                            }
7088                        }
7089
7090                        $.each(options.triggerEventTypes, function (triggerType, events) {
7091                            $.each(events, function (eventId, triggers) {
7092                                switch (triggerType) {
7093                                    case 'ad_sense_click':
7094                                        Utils.handleTriggerFired(eventId, triggerType);
7095                                        break;
7096                                }
7097                            });
7098                        });
7099                    }
7100                })
7101                .trigger("focus");
7102
7103        }
7104
7105        //setup adsense for custom events
7106        var dynamicAdsenseEventsTriggers = 0
7107        $.each(options.triggerEventTypes, function (triggerType, events) {
7108            if(triggerType == "ad_sense_click") {
7109                dynamicAdsenseEventsTriggers++;
7110            }
7111        });
7112        if (dynamicAdsenseEventsTriggers > 0) {
7113
7114            var isOverGoogleAd = false;
7115
7116            $(document)
7117                .on('mouseover', 'ins > ins > iframe', function () {
7118                    isOverGoogleAd = true;
7119                })
7120                .on('mouseout', 'iframe', function () {
7121                    isOverGoogleAd = false;
7122                });
7123
7124            $(window)
7125                .on( "blur",function () {
7126                    if (isOverGoogleAd) {
7127
7128                        $.each(options.triggerEventTypes, function (triggerType, events) {
7129                            $.each(events, function (eventId, triggers) {
7130                                switch (triggerType) {
7131                                    case 'ad_sense_click':
7132                                        Utils.handleTriggerFired(eventId, triggerType);
7133                                        break;
7134                                }
7135                            });
7136                        });
7137                    }
7138                })
7139                .trigger("focus");
7140
7141        }
7142
7143
7144        // page scroll event
7145        if (options.dynamicEvents.hasOwnProperty("automatic_event_scroll")
7146        ) {
7147
7148            var singlePageScroll = function () {
7149
7150
7151                var docHeight = $(document).height() - $(window).height();
7152                var isFired = false;
7153
7154                if (options.dynamicEvents.hasOwnProperty("automatic_event_scroll")) {
7155                    var pixels = Object.keys(options.dynamicEvents.automatic_event_scroll);
7156                    for(var i = 0;i<pixels.length;i++) {
7157                        var event = Utils.clone(options.dynamicEvents.automatic_event_scroll[pixels[i]]);
7158                        var scroll = Math.round(docHeight * event.scroll_percent / 100)// convert % to absolute positions
7159
7160                        if(scroll < $(window).scrollTop()) {
7161                            if ( pixels[i] !== 'tiktok' && pixels[i] !== 'reddit') {
7162                                Utils.copyProperties(Utils.getRequestParam
7162s(), event.params);
7163                            }
7164                            getPixelBySlag(pixels[i]).onPageScroll(event);
7165                            isFired = true
7166                        }
7167                    }
7168                }
7169                if(isFired) {
7170                    $(document).off("scroll",singlePageScroll);
7171                }
7172            }
7173            $(document).on("scroll",singlePageScroll);
7174        }
7175
7176
7177        if (options.dynamicEvents.hasOwnProperty("automatic_event_time_on_page")) {
7178            var pixels = Object.keys(options.dynamicEvents.automatic_event_time_on_page);
7179            var time = options.dynamicEvents.automatic_event_time_on_page[pixels[0]].time_on_page; // the same for all pixel
7180            setTimeout(function(){
7181                for(var i = 0;i<pixels.length;i++) {
7182                    var event = Utils.clone(options.dynamicEvents.automatic_event_time_on_page[pixels[i]]);
7183                    if ( pixels[i] !== 'tiktok' && pixels[i] !== 'reddit') {
7184                        Utils.copyProperties(Utils.getRequestParam
7184s(), event.params);
7185                    }
7186                    getPixelBySlag(pixels[i]).onTime(event);
7187                }
7188            },time*1000);
7189        }
7190
7191        // setup Dynamic events
7192
7193        $.each(options.triggerEventTypes, function (triggerType, events) {
7194
7195            $.each(events, function (eventId, triggers) {
7196                switch (triggerType) {
7197                    case 'url_click':
7198                        //@see: Utils.setupURLClickEvents()
7199                        break;
7200
7201                    case 'css_click':
7202                        var cssTriggerData  = Array.isArray(triggers) ? { selectors: triggers, click_count: 1, click_time_limit: 0 } : triggers;
7203                        var cssSelectors    = cssTriggerData.selectors || triggers;
7204                        var cssClickCount   = cssTriggerData.click_count || 1;
7205                        var cssTimeLimit    = parseInt(cssTriggerData.click_time_limit, 10) || 0;
7206                        Utils.setupCSSClickEvents(eventId, cssSelectors, triggerType, cssClickCount, cssTimeLimit);
7207                        break;
7208
7209                    case 'css_mouseover':
7210                        Utils.setupMouseOverClickEvents(eventId, triggers, triggerType);
7211                        break;
7212
7213                    case 'scroll_pos':
7214                        Utils.setupScrollPosEvents(eventId, triggers, triggerType);
7215                        break;
7216                    case 'comment':
7217                        Utils.setupCommentEvents(eventId, triggers);
7218                        break;
7219                    case 'video_view':
7220                        //@see: Utils.checkYouTubeCompletion() and Utils.addYouTubeEvents()
7221                        break;
7222
7223                    case 'form_field':
7224                        Utils.setupFormFieldEvents(eventId, triggers, triggerType);
7225                        break;
7226
7227                    case 'copy_element':
7228                        Utils.setupCopyElementEvents(eventId, triggers);
7229                        break;
7230
7231                    case 'video_speed':
7232                        Utils.setupVideoSpeedEvents(eventId, triggers);
7233                        break;
7234                }
7235
7236            });
7237
7238            if ( triggerType == "email_link" ) {
7239                Utils.setupEmailLinkEvents( events )
7240            }
7241
7242        });
7243
7244        // Global RageClick tracking
7245        if (options.dynamicEvents.hasOwnProperty('automatic_event_rage_click')) {
7246            (function() {
7247                var RAGE_THRESHOLD = Math.max(2, parseInt(options.rage_click_threshold, 10) || 3);
7248                // rage_click_time_limit from admin in seconds; allow floats (e.g. 1.5); minimum 0.5s.
7249                var RAGE_TIMEOUT   = Math.max(0.5, parseFloat(options.rage_click_time_limit) || 2) * 1000;
7250                var rageMap        = typeof WeakMap !== 'undefined' ? new WeakMap() : null;
7251
7252                if (!rageMap) return;
7253
7254                // Expanded interactive selector — includes developer-marked elements.
7255                var INTERACTIVE_SELECTOR =
7256                    'button, a, input, select, textarea, label, ' +
7257                    '[role="button"], [role="link"], [onclick], [tabindex], [data-track], [data-testid]';
7258
7259                // Walk up to the nearest meaningful interactive ancestor.
7260                // Returns null when none found — non-interactive clicks are silently ignored.
7261                // Note: having an id alone does NOT make an element interactive
7262                // (e.g. div#comments, div#reviews are structural, not clickable).
7263                function findClickTarget(el) {
7264                    while (el && el !== document.body) {
7265                        if (el.matches && el.matches(INTERACTIVE_SELECTOR)) {
7266                            return el;
7267                        }
7268                        el = el.parentElement;
7269                    }
7270                    return null;
7271                }
7272
7273                // Build a breadcrumb path: up to 3 DOM nodes ending at el.
7274                // Example: "nav#menu > ul.links > button.cta"
7275                function buildElementPath(el, depth) {
7276                    depth = depth || 3;
7277                    var parts = [];
7278                    var cur = el;
7279                    while (cur && cur !== document.body && parts.length < depth) {
7280                        var tag = cur.tagName ? cur.tagName.toLowerCase() : '';
7281                        var id  = cur.id ? '#' + cur.id : '';
7282                        var rawCls = cur.className && typeof cur.className === 'string'
7283                            ? cur.className
7284                            : (cur.className && cur.className.baseVal ? cur.className.baseVal : '');
7285                        var cls = rawCls ? '.' + rawCls.trim().split(/\s+/).slice(0, 2).join('.') : '';
7286                        parts.unshift(tag + id + cls);
7287                        cur = cur.parentElement;
7288                    }
7289                    return parts.join(' > ');
7290                }
7291
7292                function describeElement(el) {
7293                    var raw = el.className && (typeof el.className === 'string'
7294                        ? el.className
7295                        : (el.className.baseVal || ''));
7296                    var classes = raw ? raw.trim().split(/\s+/).slice(0, 3).join(' ') : '';
7297
7298                    var text = (el.innerText || el.value || el.getAttribute('aria-label') || '')
7299                        .trim().replace(/\s+/g, ' ').slice(0, 80);
7300
7301                    var href = el.tagName === 'A' ? (el.getAttribute('href') || '') : '';
7302                    var tag  = el.tagName ? el.tagName.toLowerCase() : '';
7303                    var path = buildElementPath(el);
7304
7305                    // Only include fields with non-empty values — don't pollute the event
7306                    // payload with empty strings that add noise to dashboards.
7307                    var result = {};
7308                    if (tag)     { result.element_tag = tag; result.element = tag; } // element = backward-compat alias
7309                    if (el.id)   result.element_id      = el.id;
7310                    if (classes) result.element_classes  = classes;
7311                    if (text)    result.element_text     = text;
7312                    if (href)    result.element_href     = href;
7313                    if (path)    result.element_path     = path;
7314                    return result;
7315                }
7316
7317                document.addEventListener('click', function(event) {
7318                    // Resolve to the nearest meaningful interactive ancestor.
7319                    // Non-interactive elements (bare <div>, <span>, <h2> etc.) are silently
7320                    // ignored — no fallback to event.target.
7321                    var target = findClickTarget(event.target);
7322                    if (!target) return;
7323
7324                    var now = Date.now();
7325                    var st  = rageMap.get(target) || { count: 0, last: 0, timer: null };
7326
7327                    if (now - st.last > RAGE_TIMEOUT) st.count = 0;
7328                    st.count++;
7329                    st.last = now;
7330
7331                    if (st.timer) clearTimeout(st.timer);
7332                    st.timer = setTimeout(function() { rageMap.delete(target); }, RAGE_TIMEOUT);
7333                    rageMap.set(target, st);
7334
7335                    if (st.count >= RAGE_THRESHOLD) {
7336                        rageMap.delete(target);
7337                        var params = describeElement(target); // contains only non-empty fields
7338
7339                        Object.keys(options.dynamicEvents.automatic_event_rage_click).forEach(function(slug) {
7340                            var pysEvent = Utils.clone(options.dynamicEvents.automatic_event_rage_click[slug]);
7341                            Utils.copyProperties(params, pysEvent.params);
7342                            var px = getPixelBySlag(slug);
7343                            if (px) px.fireEvent(pysEvent.name, pysEvent);
7344                        });
7345                    }
7346                }, true);
7347            })();
7348        }
7349
7350        // Global Video Speed tracking
7351        if (options.dynamicEvents.hasOwnProperty('automatic_event_video_speed')) {
7352            (function() {
7353                var pysFireVideoSpeed = function(videoEl, currRate) {
7354                    var html5Url = videoEl ? (videoEl.src || videoEl.currentSrc || '') : '';
7355                    var params = {
7356                        video_url: html5Url,
7357                        video_type: Utils.getVideoTypeFromUrl(html5Url),
7358                        video_title: Utils.getHtml5VideoTitle(html5Url),
7359                        playback_speed: currRate,
7360                        element: videoEl ? (videoEl.tagName ? videoEl.tagName.toLowerCase() : 'video') : 'video'
7361                    };
7362                    Object.keys(options.dynamicEvents.automatic_event_video_speed).forEach(function(slug) {
7363                        var pysEvent = Utils.clone(options.dynamicEvents.automatic_event_video_speed[slug]);
7364                        Utils.copyProperties(params, pysEvent.params);
7365                        var px = getPixelBySlag(slug);
7366                        if (px) px.fireEvent(pysEvent.name, pysEvent);
7367                    });
7368                };
7369
7370                // HTML5 <video>
7371                document.addEventListener('ratechange', function(event) {
7372                    var video = event.target;
7373                    if (!video || video.tagName !== 'VIDEO') return;
7374                    var prev = video._pysGlobalSpeedRate !== undefined ? video._pysGlobalSpeedRate : 1;
7375                    var curr = video.playbackRate;
7376                    if (prev === curr) return;
7377                    video._pysGlobalSpeedRate = curr;
7378                    pysFireVideoSpeed(video, curr);
7379                }, true);
7380            })();
7381        }
7382
7383        // setup WooCommerce events
7384        if (options.woo.enabled) {
7385
7386            // Woo CartFlow AddToCart
7387            if (options.dynamicEvents.hasOwnProperty("wcf_add_to_cart_on_next_step_click")) {
7388                $("body").on("click",'.wcf-next-step-link',function () {
7389                    var pixels = Object.keys(options.dynamicEvents.wcf_add_to_cart_on_next_step_click);
7390                    for(var i = 0;i<pixels.length;i++) {
7391                        var event = Utils.clone(options.dynamicEvents.wcf_add_to_cart_on_next_step_click[pixels[i]])
7392                        getPixelBySlag(pixels[i]).fireEvent(event.name, event);
7393                    }
7394                });
7395            }
7396            // Woo CartFlow Bump AddToCart
7397            if(options.dynamicEvents.hasOwnProperty("wcf_add_to_cart_on_bump_click") ||
7398                options.dynamicEvents.hasOwnProperty("wcf_bump")) {
7399                $("body").on('change','.wcf-bump-order-cb',function () {
7400
7401                    if(this.checked) {
7402                        if(options.dynamicEvents.hasOwnProperty("wcf_add_to_cart_on_bump_click")) {
7403                            var pixels = Object.keys(options.dynamicEvents.wcf_add_to_cart_on_bump_click);
7404                            for(var i = 0;i<pixels.length;i++) {
7405                                var event = Utils.clone(options.dynamicEvents.wcf_add_to_cart_on_bump_click[pixels[i]]);
7406                                getPixelBySlag(pixels[i]).fireEvent(event.name, event);
7407                            }
7408                        }
7409
7410
7411                        if(options.dynamicEvents.hasOwnProperty("wcf_bump")) {
7412                            var pixels = Object.keys(options.dynamicEvents.wcf_bump);
7413                            for(var i = 0;i<pixels.length;i++) {
7414                                var event = Utils.clone(options.dynamicEvents.wcf_bump[pixels[i]]);
7415                                getPixelBySlag(pixels[i]).fireEvent(event.name, event);
7416                            }
7417                        }
7418
7419                    } else {
7420                        if(options.dynamicEvents.hasOwnProperty("wcf_remove_from_cart_on_bump_click")) {
7421                            var pixels = Object.keys(options.dynamicEvents.wcf_remove_from_cart_on_bump_click);
7422                            for(var i = 0;i<pixels.length;i++) {
7423                                var event = Utils.clone(options.dynamicEvents.wcf_remove_from_cart_on_bump_click[pixels[i]]);
7424                                getPixelBySlag(pixels[i]).fireEvent(event.name, event);
7425                            }
7426                        }
7427
7428                    }
7429
7430
7431                });
7432            }
7433
7434
7435            // WooCommerce AddToCart
7436            if (options.dynamicEvents.hasOwnProperty("woo_add_to_cart_on_button_click")
7437                && options.woo.hasOwnProperty("addToCartCatchMethod")
7438                && options.woo.addToCartCatchMethod === "add_cart_js"
7439            ) {
7440                // Loop, any kind of "simple" product, except external
7441                $('.add_to_cart_button:not(.product_type_variable,.product_type_bundle,.single_add_to_cart_button)').on("click",function (e) {
7442
7443                    var $button = $(this);
7444                    var $prod_info = $button.closest('.product').find('.pys_list_name_productdata').data();
7445                    var product_id = $(this).data('product_id');
7446                    Cookies.set('productlist', JSON.stringify($prod_info), { expires: 1,path: '/' })
7447                    var product_id = $(this).data('product_id');
7448                    if (typeof product_id !== 'undefined') {
7449
7450                        if(options.dynamicEvents.hasOwnProperty("woo_add_to_cart_on_button_click")){
7451                            var tmpEventID = pys_generate_token();
7452                            $.each(options.dynamicEvents.woo_add_to_cart_on_button_click, function (i, tag) {
7453                                tag.eventID = tmpEventID;
7454                                // Also refresh the uniqueId cache so server-API path
7455                                // (ajaxForServerStaticEvent) gets the same fresh ID.
7456                                var idKey = tag.hasOwnProperty('custom_event_post_id') ? tag.custom_event_post_id : tag.e_id;
7457                                uniqueId[idKey] = tmpEventID;
7458                            });
7459                        }
7460
7461                        Facebook.onWooAddToCartOnButtonEvent(product_id,$(this));
7462                        Analytics.onWooAddToCartOnButtonEvent(product_id, $prod_info);
7463                        GTM.onWooAddToCartOnButtonEvent(product_id, $prod_info);
7464                        GAds.onWooAddToCartOnButtonEvent(product_id);
7465                        Pinterest.onWooAddToCartOnButtonEvent(product_id);
7466                        Bing.onWooAddToCartOnButtonEvent(product_id);
7467                        TikTok.onWooAddToCartOnButtonEvent(product_id);
7468                        Reddit.onWooAddToCartOnButtonEvent(product_id);
7469                    }
7470
7471                });
7472
7473                // Single Product
7474                // tap try to https://stackoverflow.com/questions/30990967/on-tap-click-event-firing-twice-how-to-avoid-it
7475                //  $(document) not work
7476                $('body').onFirst('click','button.single_add_to_cart_button,.single_add_to_cart_button',function (e) {
7477
7478                    var $button = $(this);
7479                    var $prod_info = $button.siblings('.pys_list_name_productdata').data();
7480                    var product_id = $(this).data('product_id');
7481                    Cookies.set('productlist', JSON.stringify($prod_info), { expires: 1,path: '/' })
7482                    if ($button.hasClass('disabled')) {
7483                        return;
7484                    }
7485
7486                    var $form = $button.closest('form');
7487
7488                    var product_type = Utils.PRODUCT_SIMPLE;
7489                    var is_external = false;
7490
7491                    if ($form.length === 0) {
7492                        is_external = true;
7493                    } else if ($form.hasClass('variations_form')) {
7494                        product_type = Utils.PRODUCT_VARIABLE;
7495                    } else if($form.hasClass('bundle_form')) {
7496                        product_type = Utils.PRODUCT_BUNDLE;
7497                    } else if($form.hasClass('grouped_form')) {
7498                        product_type = Utils.PRODUCT_GROUPED;
7499                    }
7500
7501
7502
7503                    var product_id;
7504                    var qty = 1;
7505                    if (product_type === Utils.PRODUCT_GROUPED) {
7506                        qty = 1;
7507                        product_id = parseInt($form.find('*[name="add-to-cart"]').val());
7508                    } else if (product_type === Utils.PRODUCT_VARIABLE) {
7509                        product_id = parseInt($form.find('*[name="add-to-cart"]').val());
7510                        var qtyTag = $form.find('input[name="quantity"]');
7511                        if(qtyTag.length <= 0) {
7512                            qtyTag = $form.find('select[name="quantity"]');
7513                        }
7514                        qty = parseInt(qtyTag.val());
7515                    } else if (is_external) {
7516                        product_id = options.woo.singleProductId;
7517                        qty = 1;
7518                    } else {
7519                        product_id = parseInt($form.find('*[name="add-to-cart"]').val());
7520                        var qtyTag = $form.find('input[name="quantity"]');
7521                        if(qtyTag.length <= 0) {
7522                            qtyTag = $form.find('select[name="quantity"]');
7523                        }
7524                        qty = parseInt(qtyTag.val());
7525                    }
7526
7527                    //
7528                    if(options.dynamicEvents.hasOwnProperty("woo_add_to_cart_on_button_click")){
7529                        var tmpEventID = pys_generate_token();
7530                        $.each(options.dynamicEvents.woo_add_to_cart_on_button_click, function (i, tag) {
7531                            tag.eventID = tmpEventID;
7532                            // Also refresh the uniqueId cache so server-API path
7533                            // (ajaxForServerStaticEvent) gets the same fresh ID.
7534                            var idKey = tag.hasOwnProperty('custom_event_post_id') ? tag.custom_event_post_id : tag.e_id;
7535                            uniqueId[idKey] = tmpEventID;
7536                        });
7537                    }
7538
7539                    Facebook.onWooAddToCartOnSingleEvent(product_id, qty, product_type, is_external, $form);
7540                    Analytics.onWooAddToCartOnSingleEvent(product_id, qty, product_type, is_external, $form, $prod_info);
7541                    GTM.onWooAddToCartOnSingleEvent(product_id, qty, product_type, is_external, $form, $prod_info);
7542                    GAds.onWooAddToCartOnSingleEvent(product_id, qty, product_type, is_external, $form);
7543                    Pinterest.onWooAddToCartOnSingleEvent(product_id, qty, product_type, is_external, $form);
7544                    Bing.onWooAddToCartOnSingleEvent(product_id, qty, product_type, is_external, $form);
7545                    TikTok.onWooAddToCartOnSingleEvent(product_id, qty, product_type, is_external, $form);
7546                    Reddit.onWooAddToCartOnSingleEvent(product_id, qty, product_type, is_external, $form);
7547                });
7548
7549            } else {
7550                $('.add_to_cart_button:not(.product_type_variable,.product_type_bundle,.single_add_to_cart_button)').on("click",function (e) {
7551                    var $button = $(this);
7552                    var $prod_info = $button.siblings('.pys_list_name_productdata').data();
7553                    var product_id = $(this).data('product_id');
7554                    Cookies.set('productlist', JSON.stringify($prod_info), { expires: 1,path: '/' })
7555                });
7556
7557                // Single Product
7558                // tap try to https://stackoverflow.com/questions/30990967/on-tap-click-event-firing-twice-how-to-avoid-it
7559                //  $(document) not work
7560                $('body').onFirst('click','button.single_add_to_cart_button,.single_add_to_cart_button',function (e) {
7561
7562                    var $button = $(this);
7563                    var $prod_info = $button.siblings('.pys_list_name_productdata').data();
7564                    Cookies.set('productlist', $prod_info, { expires: 1,path: '/' })
7565                });
7566            }
7567
7568            // WooCommerce Affiliate
7569            if (options.dynamicEvents.hasOwnProperty("woo_affiliate")) {
7570
7571                // Loop, external
7572                $('.product_type_external').on("click",function (e) {
7573
7574                    var product_id = $(this).data('product_id');
7575
7576                    if (typeof product_id !== 'undefined') {
7577                        Facebook.onWooAffiliateEvent(product_id);
7578                        Analytics.onWooAffiliateEvent(product_id);
7579                        GTM.onWooAffiliateEvent(product_id);
7580                        GAds.onWooAffiliateEvent(product_id);
7581                        Pinterest.onWooAffiliateEvent(product_id);
7582                        Bing.onWooAffiliateEvent(product_id);
7583                    }
7584
7585                });
7586
7587            }
7588
7589            // WooCommerce RemoveFromCart
7590            if (options.dynamicEvents.hasOwnProperty("woo_remove_from_cart")) {
7591
7592                $('body').on('click', options.woo.removeFromCartSelector, function (e) {
7593
7594                    var $a = $(e.currentTarget),
7595                        href = $a.attr('href');
7596
7597                    // extract cart item hash from remove button URL
7598                    var regex = new RegExp("[\\?&]remove_item=([^&#]*)"),
7599                        results = regex.exec(href);
7600
7601                    if (results !== null) {
7602
7603                        var item_hash = results[1];
7604
7605                        if (options.dynamicEvents["woo_remove_from_cart"].hasOwnProperty(item_hash)) {
7606                            var events = options.dynamicEvents["woo_remove_from_cart"][item_hash];
7607                            
7607Utils.fireEventForAllPixel("onWooRemoveFromCartEvent",events)
7608                        }
7609
7610                    }
7611
7612                });
7613
7614            }
7615
7616            // WooCommerce PayPal
7617            if (options.dynamicEvents.hasOwnProperty("woo_paypal")) {
7618
7619                // Non-default binding used to avoid situations when some code in external js
7620                // stopping events propagation, eg. returns false, and our handler will never called
7621                $(document).onFirst('submit click', '#place_order', function (e) {
7622
7623                    var method = $('form[name="checkout"] input[name="payment_method"]:checked').val();
7624
7625                    if (method !== 'paypal') {
7626                        return;
7627                    }
7628                    var events = options.dynamicEvents.woo_paypal;
7629                    Utils.fireEventForAllPixel("onWooPayPalEvent",events)
7630
7631                });
7632
7633            }
7634
7635            // WooCommerce checkout progress
7636            if(options.dynamicEvents.hasOwnProperty("woo_initiate_checkout_progress_f") ) {
7637
7638                $(document).on("change",".woocommerce-validated #billing_first_name",function () {
7639                    var pixels = Object.keys(options.dynamicEvents.woo_initiate_checkout_progress_f);
7640                    for (var i = 0; i < pixels.length; i++) {
7641                        var event = Utils.clone(options.dynamicEvents.woo_initiate_checkout_progress_f[pixels[i]]);
7642                        getPixelBySlag(pixels[i]).onWooCheckoutProgressStep(event);
7643                    }
7644                });
7645            }
7646            if(options.dynamicEvents.hasOwnProperty("woo_initiate_checkout_progress_l")) {
7647
7648                $(document).on("change",".woocommerce-validated #billing_last_name",function () {
7649                    var pixels = Object.keys(options.dynamicEvents.woo_initiate_checkout_progress_l);
7650                    for (var i = 0; i < pixels.length; i++) {
7651                        var event = Utils.clone(options.dynamicEvents.woo_initiate_checkout_progress_l[pixels[i]]);
7652                        getPixelBySlag(pixels[i]).onWooCheckoutProgressStep(event);
7653                    }
7654                });
7655            }
7656
7657            if(options.dynamicEvents.hasOwnProperty("woo_initiate_checkout_progress_e")) {
7658
7659                $(document).on("change",".woocommerce-validated #billing_email",function () {
7660                    var pixels = Object.keys(options.dynamicEvents.woo_initiate_checkout_progress_e);
7661                    for (var i = 0; i < pixels.length; i++) {
7662                        var event = Utils.clone(options.dynamicEvents.woo_initiate_checkout_progress_e[pixels[i]]);
7663                        getPixelBySlag(pixels[i]).onWooCheckoutProgressStep(event);
7664                    }
7665                });
7666            }
7667            if(options.dynamicEvents.hasOwnProperty("woo_initiate_checkout_progress_o")) {
7668                $(document).onFirst('submit click', '#place_order', function () {
7669                    var shippingMethodFull = $('[name="shipping_method[0]"]:checked').val();
7670                    var shippingMethod = shippingMethodFull ? shippingMethodFull.split(':')[0] : '';
7671                    var pixels = Object.keys(options.dynamicEvents.woo_initiate_checkout_progress_o);
7672                    for (var i = 0; i < pixels.length; i++) {
7673                        var event = Utils.clone(options.dynamicEvents.woo_initiate_checkout_progress_o[pixels[i]]);
7674                        // Add shipping_method to event params
7675                        event.params = event.params || {};
7676                        event.params.checkout_option = shippingMethod;
7677                        getPixelBySlag(pixels[i]).onWooCheckoutProgressStep(event);
7678                    }
7679                });
7680            }
7681
7682
7683            // WooCommerce
7684
7685            $('.product.type-product a.woocommerce-loop-product__link').onFirst('click', function (evt) {
7686                var productId = $(this).closest('.product').find(".add_to_cart_button").attr("data-product_id");
7687                var eventTypes = ["woo_select_content_search", "woo_select_content_shop", "woo_select_content_tag", "woo_select_content_single", "woo_select_content_category"];
7688                var isEventFired = false;
7689
7690                for (var i = 0; i < eventTypes.length; i++) {
7691                    var eventType = eventTypes[i];
7692                    if (options.dynamicEvents.hasOwnProperty(eventType) && options.dynamicEvents[eventType].hasOwnProperty(productId)) {
7693                        if(Analytics.isEnabled()){
7694                            Analytics.onWooSelectContent(options.dynamicEvents[eventType][productId][Analytics.tag()]);
7695                        }
7696                        if(GTM.isEnabled()){
7697                            GTM.onWooSelectContent(options.dynamicEvents[eventType][productId][GTM.tag()]);
7698                        }
7699                        isEventFired = true;
7700                        break;
7701                    }
7702                }
7703
7704                if (!isEventFired) {
7705                    let prod_info = $(this).parent().find('.pys_list_name_productdata').data();
7706                    if (prod_info) {
7707                        const select_prod_list = {};
7708                        if (prod_info['pys_list_name_productlist_name']) {
7709                            select_prod_list.list_name = prod_info['pys_list_name_productlist_name']
7710                        }
7711                        if (prod_info['pys_list_name_productlist_id']) {
7712                            select_prod_list.list_id = prod_info['pys_list_name_productlist_id']
7713                        }
7714                        const url = new URL(window.location.href);
7715                        select_prod_list.url = url.origin + url.pathname;
7716                        Cookies.set('select_prod_list', JSON.stringify(select_prod_list), { expires: 1,path: '/' });
7717                    }
7718                }
7719            });
7720
7721        }
7722
7723        // setup EDD events
7724        if (options.edd.enabled) {
7725
7726            // EDD AddToCart
7727            if (options.dynamicEvents.hasOwnProperty("edd_add_to_cart_on_button_click")) {
7728
7729                $('form.edd_download_purchase_form .edd-add-to-cart').on("click",function (e) {
7730
7731                    var $button = $(this);
7732                    var $form = $button.closest('form');
7733                    var variable_price = $button.data('variablePrice'); // yes/no
7734                    var price_mode = $button.data('priceMode'); // single/multi
7735                    var ids = [];
7736                    var quantities = [];
7737                    var qty;
7738                    var id;
7739
7740                    if (variable_price === 'yes' && price_mode === 'multi') {
7741
7742                        id = $form.find('input[name="download_id"]').val();
7743
7744                        // get selected variants
7745                        $.each($form.find('input[name="edd_options[price_id][]"]:checked'), function (i, el) {
7746                            ids.push(id + '_' + $(el).val());
7747                        });
7748
7749                        // get qty for selected variants
7750                        $.each(ids, function (i, variant_id) {
7751
7752                            var variant_index = variant_id.split('_', 2);
7753                            qty = $form.find('input[name="edd_download_quantity_' + variant_index[1] + '"]').val();
7754
7755                            if (typeof qty !== 'undefined') {
7756                                quantities.push(qty);
7757                            } else {
7758                                quantities.push(1);
7759                            }
7760
7761                        });
7762
7763                    } else if (variable_price === 'yes' && price_mode === 'single') {
7764
7765                        id = $form.find('input[name="download_id"]').val();
7766                        ids.push(id + '_' + $form.find('input[name="edd_options[price_id][]"]:checked').val());
7767
7768                        qty = $form.find('input[name="edd_download_quantity"]').val();
7769
7770                        if (typeof qty !== 'undefined') {
7771                            quantities.push(qty);
7772                        } else {
7773                            quantities.push(1);
7774                        }
7775
7776                    } else {
7777
7778                        ids.push($button.data('downloadId'));
7779
7780                        qty = $form.find('input[name="edd_download_quantity"]').val();
7781
7782                        if (typeof qty !== 'undefined') {
7783                            quantities.push(qty);
7784                        } else {
7785                            quantities.push(1);
7786                        }
7787                    }
7788
7789                    // fire event for each download/variant
7790                    $.each(ids, function (i, download_id) {
7791
7792                        var q = parseInt(quantities[i]);
7793                        var variant_index = download_id.toString().split('_', 2);
7794                        var price_index;
7795
7796                        if (variant_index.length === 2) {
7797                            download_id = variant_index[0];
7798                            price_index = variant_index[1];
7799                        }
7800
7801                        Facebook.onEddAddToCartOnButtonEvent(download_id, price_index, q);
7802                        Analytics.onEddAddToCartOnButtonEvent(download_id, price_index, q);
7803                        GTM.onEddAddToCartOnButtonEvent(download_id, price_index, q);
7804                        GAds.onEddAddToCartOnButtonEvent(download_id, price_index, q);
7805                        Pinterest.onEddAddToCartOnButtonEvent(download_id, price_index, q);
7806                        Bing.onEddAddToCartOnButtonEvent(download_id, price_index, q);
7807                        TikTok.onEddAddToCartOnButtonEvent(download_id, price_index, q);
7808                        Reddit.onEddAddToCartOnButtonEvent(download_id, price_index, q);
7809
7810                    });
7811
7812                });
7813
7814            }
7815
7816            // EDD RemoveFromCart
7817            if (options.dynamicEvents.hasOwnProperty("edd_remove_from_cart") ) {
7818
7819                $('form#edd_checkout_cart_form .edd_cart_remove_item_btn').on("click",function (e) {
7820
7821                    var href = $(this).attr('href');
7822                    var key = href.substring(href.indexOf('=') + 1).charAt(0);
7823
7824                    if (options.dynamicEvents.edd_remove_from_cart.hasOwnProperty(key)) {
7825                        var events = options.dynamicEvents.edd_remove_from_cart[key];
7826                        Utils.fireEventForAllPixel("onEddRemoveFromCartEvent",events)
7827                    }
7828
7829                });
7830
7831            }
7832
7833        }
7834
7835        Utils.setupURLClickEvents();
7836
7837        // setup Comment Event
7838        if (options.dynamicEvents.hasOwnProperty("automatic_event_comment")
7839        ) {
7840
7841            $('form.comment-form').on("submit",function () {
7842                if (options.dynamicEvents.hasOwnProperty("automatic_event_comment")) {
7843                    var pixels = Object.keys(options.dynamicEvents.automatic_event_comment);
7844                    for (var i = 0; i < pixels.length; i++) {
7845                        var event = Utils.clone(options.dynamicEvents.automatic_event_comment[pixels[i]]);
7846                        if ( pixels[i] !== 'tiktok' && pixels[i] !== 'reddit') {
7847                            Utils.copyProperties(Utils.getRequestParam
7847s(), event.params);
7848                        }
7849                        getPixelBySlag(pixels[i]).onCommentEvent(event);
7850                    }
7851                }
7852            });
7853
7854        }
7855
7856        // setup Form Event
7857        if ( options.dynamicEvents.hasOwnProperty("automatic_event_form")) {
7858
7859            $(document).onFirst('submit', 'form', function (e) {
7860
7861                var $form = $(this);
7862
7863                // exclude WP forms
7864                const formClass = $form.attr('class') || '';
7865                const formRole = ($form.attr('role') || '').toLowerCase();
7866                const formId = $form.attr('id');
7867
7868                if (
7869                    $form.hasClass('comment-form') ||
7870                    $form.hasClass('search-form') ||
7871                    $form.hasClass('woocommerce-ordering') ||
7872                    formClass.toLowerCase().includes('search') || formClass.toLowerCase().includes('add-to-cart') ||
7873                    formRole === 'search' ||
7874                    formId === 'adminbarsearch'
7875                ) {
7876                    return;
7877                }
7878
7879                // exclude Woo forms
7880                if ($form.hasClass('woocommerce-product-search') || $form.hasClass('cart') || $form.hasClass('woocommerce-cart-form') ||
7881                    $form.hasClass('woocommerce-shipping-calculator') || $form.hasClass('checkout') || $form.hasClass('checkout_coupon')) {
7882                    return;
7883                }
7884
7885                // exclude EDD forms
7886                if ($form.hasClass('edd_form') || $form.hasClass('edd_download_purchase_form')) {
7887                    return;
7888                }
7889                // exclude CF7 forms
7890                if ($form.hasClass('wpcf7-form')) {
7891                    return;
7892                }
7893                // exclude Gravity forms
7894                if ($form.attr('id') && $form.attr('id').includes('gform')) {
7895                    var target = $form.attr('target');
7896                    if (target && target.indexOf('gform_ajax_frame') !== -1) {
7897                        return;
7898                    }
7899                }
7900                // exclude Forminator forms
7901                if ($form.hasClass('forminator-custom-form') || $form.hasClass('forminator_ajax')) {
7902                    return;
7903                }
7904                // exclude WPforms forms
7905                if ($form.hasClass('wpforms-form') || $form.hasClass('wpforms-ajax-form')) {
7906                    return;
7907                }
7908                // exclude Formidable forms
7909                /*if ($form.hasClass('frm-show-form')) {
7910                    return;
7911                }*/
7912                // exclude Ninja Forms forms
7913                if ($form.parent().hasClass('nf-form-layout')) {
7914                    return;
7915                }
7916                // exclude Fluent forms
7917                if ($form.hasClass('frm-fluent-form')) {
7918                    return;
7919                }
7920
7921                // exclude WS form forms
7922                if ($form.hasClass('wsf-form') ) {
7923                    return;
7924                }
7925
7926                // exclude WS form forms
7927                if ( $form.hasClass( 'elementor-form' ) ) {
7928                    return;
7929                }
7930
7931                if(!options.enable_success_send_form) {
7932                    var params = {
7933                        form_id: $form.attr('id'),
7934                        form_class: $form.attr('class') ? $form.attr('class') : $form.attr('id'),
7935                        text: $form.find('[type="submit"]').is('input') ?
7936                            $form.find('[type="submit"]').val() : $form.find('[type="submit"]').text()
7937                    };
7938
7939                    if (options.dynamicEvents.hasOwnProperty("automatic_event_form")) {
7940                        var pixels = Object.keys(options.dynamicEvents.automatic_event_form);
7941                        for (var i = 0; i < pixels.length; i++) {
7942                            var event = Utils.clone(options.dynamicEvents.automatic_event_form[pixels[i]]);
7943
7944                            if (pixels[i] === "tiktok" || pixels[i] === "reddit") {
7945                                getPixelBySlag(pixels[i]).fireEvent(event.name, event);
7946                            } else {
7947                                Utils.copyProperties(params, event.params,)
7948                                Utils.copyProperties(Utils.getRequestParam
7948s(), event.params);
7949                                getPixelBySlag(pixels[i]).onFormEvent(event);
7950                            }
7951                        }
7952                    }
7953                }
7954            });
7955
7956
7957        }
7958
7959        jQuery(document).on('elementor/popup/show', function(event) {
7960            for (var i = 0; i < jQuery(".wpcf7-form").length; i++) {
7961                wpcf7.init(jQuery(".wpcf7-form")[i]);
7962            }
7963        });
7964
7965        document.addEventListener( 'wpcf7mailsent', function ( event ) {
7966            let form_id = event.detail.contactFormId,
7967                sendEventId = null,
7968                disabled_form_action = false,
7969                form_submit_mode = 'both';
7970            if ( options.triggerEventTypes.hasOwnProperty( 'CF7' ) ) {
7971                $.each( options.triggerEventTypes.CF7, function ( eventId, triggers ) {
7972                    $.each( triggers, function ( index, trigger ) {
7973                        $.each( trigger.forms, function ( i, value ) {
7974                            if ( value == form_id ) {
7975                                sendEventId = eventId;
7976                                disabled_form_action = trigger.disabled_form_action;
7977                                form_submit_mode = trigger.form_submit_mode || 'both';
7978                            }
7979                        } )
7980                    } );
7981                } );
7982            }
7983            if ( sendEventId != null && ( form_submit_mode === 'both' || form_submit_mode === 'ajax_only' ) ) {
7984                Utils.handleTriggerFired( sendEventId, 'CF7' );
7985            }
7986            sendFormAction( $( event.target ), form_id, sendEventId, disabled_form_action, form_submit_mode );
7987
7988        }, false );
7989
7990        //GravityForm
7991        jQuery( document ).on( 'gform_confirmation_loaded', function ( event, formId ) {
7992            let form_id = formId,
7993                sendEventId = null,
7994                disabled_form_action = false,
7995                form_submit_mode = 'both';
7996            if ( options.triggerEventTypes.hasOwnProperty( 'gravity' ) ) {
7997                $.each( options.triggerEventTypes.gravity, function ( eventId, triggers ) {
7998                    $.each( triggers, function ( index, trigger ) {
7999                        $.each( trigger.forms, function ( i, value ) {
8000                            if ( value == form_id ) {
8001                                disabled_form_action = trigger.disabled_form_action;
8002                                sendEventId = eventId;
8003                                form_submit_mode = trigger.form_submit_mode || 'both';
8004                            }
8005                        } )
8006                    } );
8007                } );
8008            }
8009
8010            if ( sendEventId != null && ( form_submit_mode === 'both' || form_submit_mode === 'ajax_only' ) ) {
8011                Utils.handleTriggerFired( sendEventId, 'gravity' );
8012            }
8013            sendFormAction( $( event.target ), form_id, sendEventId, disabled_form_action, form_submit_mode );
8014            jQuery( document ).off( 'gform_confirmation_loaded' );
8015        } );
8016        //Forminator
8017        $( document ).on( 'forminator:form:submit:success', function ( event ) {
8018
8019            let form_id = $( event.target ).find( 'input[name="form_id"]' ).val(),
8020                sendEventId = null,
8021                disabled_form_action = false,
8022                form_submit_mode = 'both';
8023            if ( options.triggerEventTypes.hasOwnProperty( 'forminator' ) ) {
8024                $.each( options.triggerEventTypes.forminator, function ( eventId, triggers ) {
8025                    $.each( triggers, function ( index, trigger ) {
8026                        $.each( trigger.forms, function ( i, value ) {
8027                            if ( value == form_id ) {
8028                                disabled_form_action = trigger.disabled_form_action;
8029                                sendEventId = eventId;
8030                                form_submit_mode = trigger.form_submit_mode || 'both';
8031                            }
8032                        } )
8033                    } );
8034                } );
8035            }
8036            if ( sendEventId != null && ( form_submit_mode === 'both' || form_submit_mode === 'ajax_only' ) ) {
8037                Utils.handleTriggerFired( sendEventId, 'forminator' );
8038            }
8039            sendFormAction( $( event.target ), form_id, sendEventId, disabled_form_action, form_submit_mode );
8040        } );
8041
8042        //WPForm
8043        $( 'form.wpforms-form' ).on( 'wpformsAjaxSubmitSuccess', ( event ) => {
8044
8045            let form_id = $( event.target ).attr( 'data-formid' ),
8046                sendEventId = null,
8047                disabled_form_action = false,
8048                form_submit_mode = 'both';
8049
8050            if ( options.triggerEventTypes.hasOwnProperty( 'wpforms' ) ) {
8051                $.each( options.triggerEventTypes.wpforms, function ( eventId, triggers ) {
8052                    $.each( triggers, function ( index, trigger ) {
8053                        $.each( trigger.forms, function ( i, value ) {
8054                            if ( value == form_id ) {
8055                                sendEventId = eventId;
8056                                disabled_form_action = trigger.disabled_form_action;
8057                                form_submit_mode = trigger.form_submit_mode || 'both';
8058                            }
8059                        } )
8060                    } );
8061                } );
8062            }
8063            if ( sendEventId != null && ( form_submit_mode === 'both' || form_submit_mode === 'ajax_only' ) ) {
8064                Utils.handleTriggerFired( sendEventId, 'wpforms' );
8065            }
8066            sendFormAction( $( event.target ), form_id, sendEventId, disabled_form_action, form_submit_mode );
8067        } )
8068
8069        $( document ).on( 'frmFormComplete', function ( event, form, response ) {
8070            const form_id = $( form ).find( 'input[name="form_id"]' ).val();
8071            let sendEventId = null,
8072                disabled_form_action = false,
8073                form_submit_mode = 'both';
8074            if ( options.triggerEventTypes.hasOwnProperty( 'formidable' ) ) {
8075                $.each( options.triggerEventTypes.formidable, function ( eventId, triggers ) {
8076                    $.each( triggers, function ( index, trigger ) {
8077                        $.each( trigger.forms, function ( i, value ) {
8078                            if ( value == form_id ) {
8079                                disabled_form_action = trigger.disabled_form_action;
8080                                sendEventId = eventId;
8081                                form_submit_mode = trigger.form_submit_mode || 'both';
8082                            }
8083                        } )
8084                    } );
8085                } );
8086            }
8087            if ( sendEventId != null && ( form_submit_mode === 'both' || form_submit_mode === 'ajax_only' ) ) {
8088                Utils.handleTriggerFired( sendEventId, 'formidable' );
8089            }
8090            sendFormAction( $( event.target ), form_id, sendEventId, disabled_form_action, form_submit_mode );
8091
8092        } );
8093
8094        // Ninja Forms
8095        $( document ).onFirst( 'nfFormSubmitResponse', function ( event, data ) {
8096            const form_id = data.response.data.form_id;
8097            let sendEventId = null,
8098                disabled_form_action = false,
8099                form_submit_mode = 'both';
8100            if ( options.triggerEventTypes.hasOwnProperty( 'ninjaform' ) ) {
8101                $.each( options.triggerEventTypes.ninjaform, function ( eventId, triggers ) {
8102                    $.each( triggers, function ( index, trigger ) {
8103                        $.each( trigger.forms, function ( i, value ) {
8104                            if ( value == form_id ) {
8105                                disabled_form_action = trigger.disabled_form_action;
8106                                sendEventId = eventId;
8107                                form_submit_mode = trigger.form_submit_mode || 'both';
8108                            }
8109                        } )
8110                    } );
8111                } );
8112            }
8113            if ( sendEventId != null && ( form_submit_mode === 'both' || form_submit_mode === 'ajax_only' ) ) {
8114                Utils.handleTriggerFired( sendEventId, 'ninjaform' );
8115            }
8116            sendFormAction( $( event.target ), form_id, sendEventId, disabled_form_action, form_submit_mode );
8117        } );
8118
8119
8120        $(document).on('fluentform_submission_success', function ( event, data ) {
8121            if(!data || !data.form){
8122                return;
8123            }
8124            let $form = data.form; // Submitted form
8125            let config = data.config; // Form configuration
8126            let response = data.response; // Server response
8127
8128
8129            let $formItem = $form;
8130            let form_id = config.id;
8131            let sendEventId = null,
8132                disabled_form_action = false,
8133                form_submit_mode = 'both';
8134            if ( options.triggerEventTypes.hasOwnProperty( 'fluentform' ) ) {
8135                $.each( options.triggerEventTypes.fluentform, function ( eventId, triggers ) {
8136                    $.each( triggers, function ( index, trigger ) {
8137                        $.each( trigger.forms, function ( i, value ) {
8138                            if ( value == form_id ) {
8139                                disabled_form_action = trigger.disabled_form_action;
8140                                sendEventId = eventId;
8141                                form_submit_mode = trigger.form_submit_mode || 'both';
8142                            }
8143                        } )
8144                    } );
8145                } );
8146            }
8147
8148            if ( sendEventId != null && ( form_submit_mode === 'both' || form_submit_mode === 'ajax_only' ) ) {
8149                Utils.handleTriggerFired( sendEventId, 'fluentform' );
8150            }
8151            sendFormAction( $( event.target ), form_id, sendEventId, disabled_form_action, form_submit_mode );
8152        } );
8153
8154        //WSForm
8155        $( document ).on( 'wsf-submit-complete', ( event, form_object, form_id ) => {
8156            let sendEventId = null,
8157                disabled_form_action = false,
8158                form_submit_mode = 'both';
8159            if ( options.triggerEventTypes.hasOwnProperty( 'wsform' ) ) {
8160                $.each( options.triggerEventTypes.wsform, function ( eventId, triggers ) {
8161                    $.each( triggers, function ( index, trigger ) {
8162                        $.each( trigger.forms, function ( i, value ) {
8163                            if ( value == form_id ) {
8164                                disabled_form_action = trigger.disabled_form_action;
8165                                sendEventId = eventId;
8166                                form_submit_mode = trigger.form_submit_mode || 'both';
8167                            }
8168                        } )
8169                    } );
8170                } );
8171            }
8172
8173            if ( sendEventId != null && ( form_submit_mode === 'both' || form_submit_mode === 'ajax_only' ) ) {
8174                Utils.handleTriggerFired( sendEventId, 'wsform' );
8175            }
8176            sendFormAction( $( event.target ), form_id, sendEventId, disabled_form_action, form_submit_mode );
8177        } )
8178
8179        // Elementor Forms
8180        let forms = document.querySelectorAll( '.elementor-form' );
8181        // Apply the observer to each form
8182        forms.forEach( function ( form ) {
8183            observeElementorForm( form );
8184        } );
8185
8186        // watching Elementor Popup forms
8187        let observer = new MutationObserver( function ( mutationsList, observer ) {
8188            for ( let i = 0; i < mutationsList.length; i++ ) {
8189                let popupForm = document.querySelector( '[class*="elementor-popup"] form' );
8190                if ( popupForm ) {
8191                    observeElementorForm( popupForm );
8192                    observer.disconnect();
8193                    break;
8194                }
8195            }
8196        } );
8197        observer.observe( document.body, { childList: true, subtree: true } );
8198
8199        // load pixel APIs
8200        Utils.loadPixels();
8201
8202        // setup Enrich content
8203        if(Utils.isCheckoutPage()) {
8204            Utils.addCheckoutFields();
8205        }
8206        if(options.woo.hasOwnProperty('woo_view_content_variation_is_selected') && options.woo.woo_view_content_variation_is_selected) {
8207            handleWooViewContentVariation()
8208        }
8209    }
8210
8211    $(document).ready(function () {
8212        if ( options.dynamicDataUrl ) {
8213            fetch( options.dynamicDataUrl, {
8214                method: 'GET',
8215                credentials: 'same-origin',
8216                cache: 'no-store',
8217                headers: { 'Accept': 'application/json' }
8218            } )
8219                .then( function(r) { return r.ok ? r.json() : Promise.reject(r.status); } )
8220                .then( function(dynamic) {
8221                    options.ajax_event         = dynamic.ajax_event;
8222                    options.user_id            = dynamic.user_id;
8223                    options.cache_bypass       = dynamic.cache_bypass;
8224                    options.tracking_analytics = dynamic.tracking_analytics;
8225                    if ( dynamic.gdpr_dynamic && options.gdpr ) {
8226                        Object.assign( options.gdpr, dynamic.gdpr_dynamic );
8227                        if ( dynamic.gdpr_dynamic.analytics_storage_value !== null )
8228                            options.gdpr.analytics_storage.value = dynamic.gdpr_dynamic.analytics_storage_value;
8229                        if ( dynamic.gdpr_dynamic.ad_storage_value !== null )
8230                            options.gdpr.ad_storage.value = dynamic.gdpr_dynamic.ad_storage_value;
8231                        if ( dynamic.gdpr_dynamic.ad_user_data_value !== null )
8232                            options.gdpr.ad_user_data.value = dynamic.gdpr_dynamic.ad_user_data_value;
8233                        if ( dynamic.gdpr_dynamic.ad_personalization_value !== null )
8234                            options.gdpr.ad_personalization.value = dynamic.gdpr_dynamic.ad_personalization_value;
8235                    }
8236                    options.cookie = dynamic.cookie;
8237                    console.log(options)
8238                    // Per-pixel user data (advanced matching) that was emptied out of
8239                    // the cached HTML. An inline value that still holds something wins:
8240                    // on pages we never strip (order received / EDD success) the HTML
8241                    // carries the order's own data, which beats the account-level data
8242                    // this endpoint can see.
8243                    if ( dynamic.pixels ) {
8244                        for ( var slug in dynamic.pixels ) {
8245                            if ( ! dynamic.pixels.hasOwnProperty( slug ) || ! options[ slug ] ) continue;
8246                            for ( var key in dynamic.pixels[ slug ] ) {
8247                                if ( ! dynamic.pixels[ slug ].hasOwnProperty( key ) ) continue;
8248                                var inline = options[ slug ][ key ];
8249                                if ( ! inline || Object.keys( inline ).length === 0 ) {
8250                                    options[ slug ][ key ] = dynamic.pixels[ slug ][ key ];
8251                                }
8252                            }
8253                        }
8254                    }
8255                    console.log(options)
8256                    pysNormalInit();
8257                } )
8258                .catch( function(err) {
8259                    if ( options.debug ) console.warn( '[PYS] dynamic-options fetch failed:', err );
8260                    pysNormalInit();
8261                } );
8262        } else {
8263            pysNormalInit();
8264        }
8265    });
8266
8267
8268    function handleWooViewContentVariation() {
8269        var form = $('form.variations_form');
8270
8271        form.on('change', 'select', function() {
8272            var selectedAttributes = {};
8273            form.find('select').each(function() {
8274                var attributeName = $(this).data('attribute_name') || $(this).attr('name');
8275                var attributeValue = $(this).val();
8276                if (attributeValue) {
8277                    selectedAttributes[attributeName] = attributeValue;
8278                }
8279            });
8280
8281            $.ajax({
8282                url: options.ajaxUrl,
8283                type: 'POST',
8284                data: {
8285                    action: 'get_variation_info',
8286                    product_id: form.data('product_id'), // Pass the main product ID
8287                    attributes: selectedAttributes
8288                },
8289                success: function(response) {
8290                    if (response.success) {
8291                        $.each(response.data, function(slug) {
8292                            $.each(response.data[slug], function(index, event) {
8293                                event.isVariationTrigger = true;
8294                                getPixelBySlag(slug).fireEvent(event.name, event);
8295                            });
8296                        });
8297                    } else {
8298                        console.log('Error retrieving data:', response.data);
8299                    }
8300                }
8301            });
8302        });
8303    }
8304    var sendFormAction = function (form_target, formId, customEventId = null, disabled = false, form_submit_mode = 'both'){
8305        // Check submit mode - skip automatic event if reload_only
8306        if (disabled == true || form_submit_mode === 'reload_only' ) {
8307            return;
8308        }
8309
8310        var params = {
8311            form_id: formId,
8312            text: form_target.find('[type="submit"]').is('input') ? form_target.find('[type="submit"]').val() :
8313                form_target.find('.forminator-button-submit').text() != '' ? form_target.find('.forminator-button-submit').text() :
8314                    form_target.find('[type="submit"]').text()
8315        };
8316
8317        if (options.dynamicEvents.hasOwnProperty("automatic_event_form")) {
8318            var pixels = Object.keys(options.dynamicEvents.automatic_event_form);
8319            for (var i = 0; i < pixels.length; i++) {
8320
8321                if ( disabled && customEventId && typeof options.triggerEvents[customEventId][pixels[i]] !== 'undefined') {
8322                    continue;
8323                }
8324                var event = options.dynamicEvents.automatic_event_form[pixels[i]];
8325                if (pixels[i] === "tiktok" || pixels[i] === "reddit") {
8326                    getPixelBySlag(pixels[i]).fireEvent(event.name, event);
8327                } else {
8328                    Utils.copyProperties(params, event.params)
8329                    Utils.copyProperties(Utils.getRequestParams(), event.params);
8330                    getPixelBySlag(pixels[i]).onFormEvent(event);
8331                }
8332            }
8333        }
8334    }
8335
8336    var setupEmailLinks = function ( disabled_for_pixels = [] ) {
8337        if ( options.dynamicEvents.hasOwnProperty( "automatic_event_email_link" ) ) {
8338            var pixels = Object.keys( options.dynamicEvents.automatic_event_email_link );
8339            for ( var i = 0; i < pixels.length; i++ ) {
8340                if ( disabled_for_pixels.indexOf(pixels[ i ]) === -1 ) {
8341                    var event = Utils.clone( options.dynamicEvents.automatic_event_email_link[ pixels[ i ] ] );
8342                    if ( pixels[ i ] !== 'tiktok' && pixels[ i ] !== 'reddit' ) {
8343                        Utils.copyProperties( Utils.getRequestParams(), event.params );
8344                    }
8345                    getPixelBySlag( pixels[ i ] ).fireEvent( event.name, event );
8346                }
8347            }
8348        }
8349    }
8350
8351    //Elementor Forms Observer
8352    var observeElementorForm = function ( form ) {
8353        let observer = new MutationObserver( function ( mutations ) {
8354
8355            for ( let i = 0; i < mutations.length; i++ ) {
8356                let mutation = mutations[ i ];
8357
8358                if ( mutation.type === 'childList' || mutation.type === 'attributes' ) {
8359                    if ( $( form ).find( '.elementor-message-success' ).length > 0 ) {
8360
8361                        let form_id = $( form ).attr( 'id' );
8362                        if ( !form_id ) {
8363                            form_id = $( form ).find( 'input[name="form_id"]' ).val();
8364                        }
8365
8366                        let sendEventId = null,
8367                            disabled_form_action = false,
8368                            form_submit_mode = 'both';
8369                        if ( options.triggerEventTypes.hasOwnProperty( 'elementor_form' ) ) {
8370                            $.each( options.triggerEventTypes.elementor_form, function ( eventId, triggers ) {
8371                                $.each( triggers, function ( index, trigger ) {
8372                                    $.each( trigger.forms, function ( i, value ) {
8373                                        if ( value == form_id ) {
8374                                            disabled_form_action = trigger.disabled_form_action;
8375                                            sendEventId = eventId;
8376                                            form_submit_mode = trigger.form_submit_mode || 'both';
8377                                        }
8378                                    })
8379                                } );
8380                            } );
8381                        }
8382                        if ( sendEventId != null && ( form_submit_mode === 'both' || form_submit_mode === 'ajax_only' ) ) {
8383                            Utils.handleTriggerFired( sendEventId, 'elementor_form' );
8384                        }
8385                        sendFormAction( $( event.target ), form_id, sendEventId, disabled_form_action, form_submit_mode );
8386
8387                        observer.disconnect();
8388                        break;
8389                    }
8390                }
8391            }
8392        } );
8393
8394        // Start observing the form for changes
8395        observer.observe( form, {
8396            attributes: true,
8397            childList: true,
8398            subtree: true
8399        } );
8400    }
8401
8402}(jQuery, pysOptions);
8403
8404
8405function pys_generate_token() {
8406    return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
8407        (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
8408    );
8409}
8410function getBundlePriceOnSingleProduct(data) {
8411    var items_sum = 0;
8412    jQuery(".bundle_form .bundled_product").each(function(index){
8413        var id = jQuery(this).find(".cart").data("bundled_item_id");
8414        var item_price = data.prices[id];
8415        var item_quantity = jQuery(this).find(".bundled_qty").val();
8416        if(!jQuery(this).hasClass("bundled_item_optional") ||
8417            jQuery(this).find(".bundled_product_optional_checkbox input").prop('checked')) {
8418            items_sum += item_price*item_quantity;
8419        }
8420    });
8421    return items_sum;
8422}
8423function getUrlParameter(sParam) {
8424    var sPageURL = window.location.search.substring(1),
8425        sURLVariables = sPageURL.split('&'),
8426        sParameterName,
8427        i;
8428
8429    for (i = 0; i < sURLVariables.length; i++) {
8430        sParameterName = sURLVariables[i].split('=');
8431
8432        if (sParameterName[0] === sParam) {
8433            return sParameterName[1] === undefined ? true : decodeURIComponent(sParameterName[1]);
8434        }
8435    }
8436    return false;
8437};
8438function inArray(needle, haystack) {
8439    var length = haystack.length;
8440    for(var i = 0; i < length; i++) {
8441        if(haystack[i] == needle) return true;
8442    }
8443    return false;
8444}
8445
8446function getCookieYes(key) {
8447    const cookiesObj = document.cookie
8448        .split(";")
8449        .reduce((ac, cv) => {
8450            const [k, v] = cv.split("=");
8451            if (k && v) ac[k.trim()] = v;
8452            return ac;
8453        }, {});
8454    const consentCookie = cookiesObj["cookieyes-consent"] || cookiesObj["wt_consent"];
8455    if (!consentCookie) return undefined;
8456    const { [key]: value } = consentCookie
8457        .split(",")
8458        .reduce((obj, pair) => {
8459            const [k, v] = pair.split(":");
8460            if (k && v) obj[k] = v;
8461            return obj;
8462        }, {});
8463    return value;
8464}
8465
8466function getRootDomain(useSubdomain = false) {
8467    const hostname = window.location.hostname; // Get the current hostname
8468    // Check if tldjs is defined before using it
8469    if (typeof tldjs === "undefined") {
8470        console.warn("tldjs is not defined");
8471        return hostname; // Return hostname as a fallback
8472    }
8473
8474    const rootDomain = tldjs.getDomain(hostname); // Use tldjs to extract the root domain
8475
8476    // Return the root domain with or without a leading dot based on useSubdomain
8477    return rootDomain ? (useSubdomain ? '.' + rootDomain : rootDomain) : hostname;
8478}

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.