PageSourceSearch

https://www.aboriginal-art-australia.com/wp-content/plugins/pixely…/dist/scripts/public.js?ver=12.6.5

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

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.