PageSourceSearch

https://www.russmarket.com/wp-content/plugins/pixelyoursite-pro/dist/scripts/public.js?ver=12.5.6

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

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.