PageSourceSearch

https://www.humanitasedu.it/wp-content/plugins/pixelyoursite-pro/dist/scripts/public.js?ver=12.6.0

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

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.