PageSourceSearch

https://www.tuxer.se/wp-content/plugins/pixelyoursite/dist/scripts/public.js?ver=11.2.3

js tuxer.se collected 2026-09-27 02:00:25 UTC 180,366 bytes, 4,224 lines download raw bytes

1/* global pysOptions */
2
3
4! function ($, options) {
5    if (options.debug) {
6        console.log('PYS:', options);
7    }
8    var uniqueId = {};
9
10    let gtm_variables = {};
11    let gtm_datalayername = "dynamicVariable";
12
13    var domain = '';
14    if(options.hasOwnProperty("track_cookie_for_subdomains") && options.track_cookie_for_subdomains) {
15        domain = getRootDomain(true);
16    }
17
18    // Per-page referrer for Facebook CAPI events. Captured once per script
19    // load so SPA-style navigations within the same HTML document do not
20    // overwrite it. Internal/external is NOT filtered — Meta expects this
21    // field to describe the URL of the previous page for THIS event.
22    var pageReferrer = '';
23    try {
24        pageReferrer = stripUrlQuery((document.referrer || '').toString());
25    } catch (e) {}
26
27    // Session-entry referrer (fallback). Captured at session start the first
28    // time the visitor lands from an EXTERNAL site, mirrored to a session
29    // cookie so PHP can read it for orphan events (async/cron) that arrive
30    // without a per-page payload. Never the primary source.
31    var PYS_SESSION_ENTRY_REFERRER_KEY = 'pys_session_entry_referrer';
32    function stripUrlQuery(url) {
33        if (!url) return '';
34        var clean = String(url);
35        var q = clean.indexOf('?');
36        if (q !== -1) clean = clean.substring(0, q);
37        var h = clean.indexOf('#');
38        if (h !== -1) clean = clean.substring(0, h);
39        return clean;
40    }
41    var sessionEntryReferrer = '';
42    function getOrInitSessionEntryReferrer(consentGranted) {
43        var value = '';
44        try {
45            if (typeof window.sessionStorage !== 'undefined') {
46                value = window.sessionStorage.getItem(PYS_SESSION_ENTRY_REFERRER_KEY) || '';
47            }
48        } catch (e) {}
49
50        if (!value) {
51            var cookieValue = Cookies.get(PYS_SESSION_ENTRY_REFERRER_KEY);
52            if (cookieValue && cookieValue !== 'undefined') {
53                value = cookieValue;
54                try {
55                    if (typeof window.sessionStorage !== 'undefined') {
56                        window.sessionStorage.setItem(PYS_SESSION_ENTRY_REFERRER_KEY, value);
57                    }
58                } catch (e) {}
59            }
60        }
61
62        if (!value) {
63            var raw = '';
64            try {
65                raw = stripUrlQuery((document.referrer || '').toString());
66            } catch (e) {}
67            if (raw) {
68                var refHost = '';
69                try {
70                    refHost = new URL(raw).hostname;
71                } catch (e) {}
72                if (refHost && refHost !== window.location.hostname) {
73                    value = raw;
74                    try {
75                        if (typeof window.sessionStorage !== 'undefined') {
76                            window.sessionStorage.setItem(PYS_SESSION_ENTRY_REFERRER_KEY, value);
77                        }
78                    } catch (e) {}
79                }
80            }
81        }
82
83        // Mirror to a session cookie only once consent is given. The cookie
84        // is session-scoped (no expires) and dies with the browser, just
85        // like the sessionStorage entry.
86        if (value && consentGranted && options && options.cookie
87            && !options.cookie.disabled_all_cookie) {
88            var existingCookie = Cookies.get(PYS_SESSION_ENTRY_REFERRER_KEY);
89            if (!existingCookie || existingCookie === 'undefined') {
90                Cookies.set(PYS_SESSION_ENTRY_REFERRER_KEY, value, { path: '/', domain: domain });
91            }
92        }
93
94        sessionEntryReferrer = value;
95        return value;
96    }
97
98    // Populate sessionStorage / sessionEntryReferrer immediately. Cookie
99    // mirroring is deferred to the consent-gated path in Facebook.loadPixel().
100    getOrInitSessionEntryReferrer(false);
101
102    // Per-page event referrer (session cookie). Snapshot of document.referrer
103    // for the CURRENT page, refreshed on every page load. Read server-side
104    // by saveFbTagsInOrder() to capture the buyer's checkout-page referrer
105    // for orphan CAPI events (APT, gateway webhooks, manual status changes).
106    // Internal URLs are intentionally NOT filtered — the goal is the page
107    // that led the buyer to checkout, which is usually internal.
108    // If document.referrer is empty (direct navigation), the previous value
109    // is preserved so a session that began with an external referrer keeps
110    // it across same-tab navigations.
111    var PYS_EVENT_REFERRER_KEY = 'pys_event_referrer';
112    function writeEventReferrerCookie() {
113        if (!pageReferrer) {
114            return;
115        }
116        if (!options || !options.cookie || options.cookie.disabled_all_cookie) {
117            return;
118        }
119        try {
120            Cookies.set(PYS_EVENT_REFERRER_KEY, pageReferrer, { path: '/', domain: domain });
121        } catch (e) {}
122    }
123    /**
124     * Resolve parameter value based on mode (static or dynamic)
125     *
126     * @param {Object|string} param - Parameter object with { value, selector } or string value
127     * @param {string} key - Parameter key name
128     * @returns {string|null} - Resolved value
129     */
130    function resolveParamValue(param, key = '') {
131        // 1️⃣ null / undefined - skip this parameter
132        if (param === null || param === undefined) {
133            return null;
134        }
135
136        // 2️⃣ Handle primitive types (string, number, boolean)
137        if (typeof param !== 'object') {
138            return param;
139        }
140
141        // 3️⃣ Handle arrays - process each element recursively
142        if (Array.isArray(param)) {
143            return param.map(item => resolveParamValue(item, key));
144        }
145
146        // 4️⃣ Check if this is a configuration object (has value/selector/dynamic/input_type)
147        const isConfigObject =
148            ('value' in param || 'selector' in param || 'dynamic' in param || 'input_type' in param) &&
149            Object.keys(param).every(k =>
150                ['value', 'selector', 'dynamic', 'input_type', 'name'].includes(k)
151            );
152
153        if (isConfigObject) {
154            // STATIC MODE: selector is empty or dynamic is false
155            const isStatic =
156                (!param.dynamic || param.dynamic === false) ||
157                (!param.selector || param.selector.trim() === '');
158
159            if (isStatic) {
160                let value = param.value ?? null;
161
162                // Apply numeric conversion if needed
163                if (param.input_type === "float" || param.input_type === "int") {
164                    value = extractNumericValue(value, param.input_type === "int");
165                }
166
167                return value;
168            }
169
170            // DYNAMIC MODE: selector is present
171            try {
172                const el = document.querySelector(param.selector);
173                if (!el) {
174                    return null;
175                }
176
177                let value =
178                    el.value ||
179                    el.innerText ||
180                    el.textContent ||
181                    el.getAttribute("content") ||
182                    el.getAttribute("data-value") ||
183                    null;
184
185                // Apply numeric conversion if needed
186                if (param.input_type === "float" || param.input_type === "int") {
187                    value = extractNumericValue(value, param.input_type === "int");
188                }
189
190                return value;
191            } catch (e) {
192                return null;
193            }
194        }
195
196        // 5️⃣ Plain object (like ecommerce data) - recursively process nested values
197        return Object.fromEntries(
198            Object.entries(param)
199                .map(([k, v]) => [k, resolveParamValue(v, k)])
200                .filter(([k, v]) => v !== null && v !== undefined)
201        );
202    }
203
204    function extractNumericValue(value, isInt = false) {
205        if (value === null || value === undefined) {
206            return null;
207        }
208        if (isInt) {
209            return parseInt(value);
210        }
211        return parseFloat(value);
212    }
213    var dummyPinterest = function () {
214
215        /**
216         * Public API
217         */
218        return {
219            // Must mirror the real module's surface. Utils.fireEventForAllPixel()
220            // calls tag() on every pixel unconditionally, so a stand-in without it
221            // throws "Pinterest.tag is not a function" whenever the add-on is not
222            // installed and this dummy stays in place.
223            tag: function () {
224                return "pinterest";
225            },
226            isEnabled: function () {},
227            disable: function () {},
228            loadPixel: function () {},
229            fireEvent: function (name, data) {
230                return false;
231            },
232            onAdSenseEvent: function (event) {},
233            onClickEvent: function (params) {},
234            onWatchVideo: function (params) {},
235            onCommentEvent: function () {},
236            onDownloadEvent: function (params) {},
237            onFormEvent: function (params) {},
238            onWooAddToCartOnButtonEvent: function (product_id) {},
239            onWooAddToCartOnSingleEvent: function (product_id, qty, is_variable, is_external, $form) {},
240            onWooRemoveFromCartEvent: function (cart_item_hash) {},
241            onWooAffiliateEvent: function (product_id) {},
242            onWooPayPalEvent: function (event) {},
243            onEddAddToCartOnButtonEvent: function (download_id, price_index, qty) {},
244            onEddRemoveFromCartEvent: function (item) {},
245            onPageScroll: function (event) {},
246            onTime: function (event) {},
247
248        }
249
250    }();
251
252    var dummyBing = function () {
253
254        /**
255         * Public API
256         */
257        return {
258            // See dummyPinterest: tag() is required by Utils.fireEventForAllPixel().
259            tag: function () {
260                return "bing";
261            },
262            isEnabled: function () {},
263            disable: function () {},
264            loadPixel: function () {},
265            fireEvent: function (name, data) {
266                return false;
267            },
268            onAdSenseEvent: function () {},
269            onClickEvent: function (params) {},
270            onWatchVideo: function (params) {},
271            onCommentEvent: function () {},
272            onFormEvent: function (params) {},
273            onDownloadEvent: function (params) {},
274            onWooAddToCartOnButtonEvent: function (product_id) {},
275            onWooAddToCartOnSingleEvent: function (product_id, qty, is_variable, is_external, $form) {},
276            onWooRemoveFromCartEvent: function (cart_item_hash) {},
277            onWooAffiliateEvent: function (product_id) {},
278            onWooPayPalEvent: function () {},
279            onEddAddToCartOnButtonEvent: function (download_id, price_index, qty) {},
280            onEddRemoveFromCartEvent: function (item) {},
281            onPageScroll: function (event) {},
282            onTime: function (event) {},
283        }
284
285    }();
286
287	var dummyReddit = function () {
288		/**
289		 * Public API
290		 */
291		return {
292			tag: function () {
293				return "reddit";
294			},
295			isEnabled: function () {},
296			disable: function () {},
297			loadPixel: function () {},
298			fireEvent: function ( name, data ) {
299				return false;
300			},
301			onAdSenseEvent: function ( event ) {},
302			onClickEvent: function ( params ) {},
303			onWatchVideo: function ( params ) {},
304			onCommentEvent: function ( event ) {},
305			onFormEvent: function ( params ) {},
306			onDownloadEvent: function ( params ) {},
307			onWooAddToCartOnButtonEvent: function ( product_id ) {},
308			onWooAddToCartOnSingleEvent: function ( product_id, qty, product_type, is_external, $form ) {},
309			onWooRemoveFromCartEvent: function ( cart_item_hash ) {},
310			onWooAffiliateEvent: function ( product_id ) {},
311			onWooPayPalEvent: function ( event ) {},
312			onEddAddToCartOnButtonEvent: function ( download_id, price_index, qty ) {},
313			onEddRemoveFromCartEvent: function ( item ) {},
314			onPageScroll: function ( event ) {},
315			onTime: function ( event ) {},
316		}
317	}();
318
319
320	var Utils = function (options) {
321
322        var Pinterest = dummyPinterest;
323
324        var Bing = dummyBing;
325
326		var Reddit = dummyReddit;
327
328        var gtag_loaded = false;
329
330        var gtm_loaded = false;
331
332        let isNewSession = checkSession();
333
334        var utmTerms = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];
335
336        var utmId = ['fbadid', 'gadid', 'padid', 'bingid'];
337
338        let dataLayerName = 'dataLayerPYS';
339
340        let GTMdataLayerName = 'dataLayer';
341
342        function validateEmail(email) {
343            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,}))$/;
344            return re.test(email);
345        }
346        function getDomain(url) {
347
348            url = url.replace(/(https?:\/\/)?(www.)?/i, '');
349
350            if (url.indexOf('/') !== -1) {
351                return url.split('/')[0];
352            }
353
354            return url;
355        }
356        function loadPixels() {
357
358            if (!options.gdpr.all_disabled_by_api) {
359
360                if (!options.gdpr.facebook_disabled_by_api) {
361                    Facebook.loadPixel();
362                }
363
364                if (!options.gdpr.analytics_disabled_by_api) {
365                    Analytics.loadPixel();
366                }
367
368                if (!options.gdpr.analytics_disabled_by_api) {
369                    GTM.loadPixel();
370                }
371
372                if (!options.gdpr.pinterest_disabled_by_api) {
373                    Pinterest.loadPixel();
374                }
375
376                if (!options.gdpr.bing_disabled_by_api) {
377                    Bing.loadPixel();
378                }
379
380	            if (!options.gdpr.reddit_disabled_by_api) {
381		            Reddit.loadPixel();
382	            }
383            }
384            if (options.gdpr.consent_magic_integration_enabled && typeof CS_Data !== "undefined") {
385                if (typeof CS_Data.cs_google_analytics_consent_mode !== "undefined" && CS_Data.cs_google_analytics_consent_mode == 1) {
386                    Analytics.loadPixel();
387                }
388            }
389
390        }
391
392        function checkSession() {
393            if( Cookies.get('pys_start_session') === undefined ||
394                Cookies.get('pys_session_limit') === undefined) {
395                firstVisit = true;
396                return true
397            }
398            return false
399
400        }
401
402        function getTrafficSource() {
403
404            try {
405
406                let referrer = stripUrlQuery(document.referrer.toString()),
407                    source;
408
409                let direct = referrer.length === 0;
410                let internal = direct ? false : referrer.indexOf(options.siteUrl) === 0;
411                let external = !direct && !internal;
412
413                if (external === false) {
414                    source = 'direct';
415                } else {
416                    source = referrer;
417                }
418
419                if (source !== 'direct') {
420                    // leave only domain (Issue #70)
421                    return getDomain(source);
422                } else {
423                    return source;
424                }
425
426            } catch (e) {
427                console.error(e);
428                return 'direct';
429            }
430
431        }
432
433        /**
434         * Return query variables object with where property name is query variable
435         * and property value is query variable value.
436         */
437        function getQueryVars() {
438
439            try {
440
441                var result = {},
442                    tmp = [];
443
444                window.location.search
445                    .substr(1)
446                    .split("&")
447                    .forEach(function (item) {
448
449                        tmp = item.split('=');
450
451                        if (tmp.length > 1) {
452                            result[tmp[0]] = tmp[1];
453                        }
454
455                    });
456
457                return result;
458
459            } catch (e) {
460                console.error(e);
461                return {};
462            }
463
464        }
465
466
467        function getLandingPageValue() {
468            let name = "pys_landing_page"
469            if(options.visit_data_model === "last_visit") {
470                name = "last_pys_landing_page"
471            }
472            if(Cookies.get(name) && Cookies.get(name) !== "undefined") {
473                return Cookies.get(name);
474            }
475            else if(options.hasOwnProperty("tracking_analytics") && options.tracking_analytics.TrafficLanding){
476                return options.tracking_analytics.TrafficLanding;
477            } else{
478                return "";
479            }
480        }
481        function getTrafficSourceValue() {
482            let name = "pysTrafficSource"
483            if(options.visit_data_model === "last_visit") {
484                name = "last_pysTrafficSource"
485            }
486            if(Cookies.get(name) && Cookies.get(name) !== "undefined") {
487                return Cookies.get(name);
488            } else{
489                return "";
490            }
491        }
492
493        function getUTMId(useLast = false) {
494            try {
495                let cookiePrefix = 'pys_'
496                let terms = [];
497                if (useLast) {
498                    cookiePrefix = 'last_pys_'
499                }
500                $.each(utmId, function (index, name) {
501                    if (Cookies.get(cookiePrefix + name)) {
502                        terms[name] = Cookies.get(cookiePrefix + name)
503                    }
504                    else if(options.hasOwnProperty("tracking_analytics") && options.tracking_analytics.TrafficUtmsId[name]) {
505                        terms[name] = filterEmails(options.tracking_analytics.TrafficUtmsId[name])
506                    }
507                });
508                return terms;
509            } catch (e) {
510                console.error(e);
511                return [];
512            }
513        }
514        /**
515         * Return UTM terms from request query variables or from cookies.
516         */
517        function getUTMs(useLast = false) {
518
519            try {
520                let cookiePrefix = 'pys_'
521                if(useLast) {
522                    cookiePrefix = 'last_pys_'
523                }
524                let terms = [];
525                $.each(utmTerms, function (index, name) {
526                    if (Cookies.get(cookiePrefix + name)) {
527                        let value = Cookies.get(cookiePrefix + name);
528                        terms[name] = filterEmails(value); // do not allow email in request params (Issue #70)
529                    }
530                    else if(options.hasOwnProperty("tracking_analytics") && options.tracking_analytics.TrafficUtms[name]) {
531                        terms[name] = filterEmails(options.tracking_analytics.TrafficUtms[name])
532                    }
533                });
534
535                return terms;
536
537            } catch (e) {
538                console.error(e);
539                return [];
540            }
541
542        }
543
544        function getDateTime() {
545            var dateTime = new Array();
546            var date = new Date(),
547                days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
548                months = ['January', 'February', 'March', 'April', 'May', 'June',
549                    'July', 'August', 'September', 'October', 'November', 'December'
550                ],
551                hours = ['00-01', '01-02', '02-03', '03-04', '04-05', '05-06', '06-07', '07-08',
552                    '08-09', '09-10', '10-11', '11-12', '12-13', '13-14', '14-15', '15-16', '16-17',
553                    '17-18', '18-19', '19-20', '20-21', '21-22', '22-23', '23-24'
554                ];
555            dateTime.push(hours[date.getHours()]);
556            dateTime.push(days[date.getDay()]);
557            dateTime.push(months[date.getMonth()]);
558            return dateTime;
559        }
560
561        function filterEmails(value) {
562            return validateEmail(value) ? undefined : value;
563        }
564
565        /**
566         * PUBLIC API
567         */
568        return {
569            PRODUCT_SIMPLE : 0,
570            PRODUCT_VARIABLE : 1,
571            PRODUCT_BUNDLE : 2,
572            PRODUCT_GROUPED : 3,
573            utmTerms : utmTerms,
574            utmId : utmId,
575            fireEventForAllPixel:function(functionName,events){
576                if (events.hasOwnProperty(Facebook.tag()))
577                    Facebook[functionName](events[Facebook.tag()]);
578                if (events.hasOwnProperty(Analytics.tag()))
579                    Analytics[functionName](events[Analytics.tag()]);
580                if (events.hasOwnProperty(Pinterest.tag()))
581                    Pinterest[functionName](events[Pinterest.tag()]);
582                if (events.hasOwnProperty(Bing.tag()))
583                    Bing[functionName](events[Bing.tag()]);
584	            if (events.hasOwnProperty(Reddit.tag()))
585		            Reddit[functionName](events[Reddit.tag()]);
586
587                if (events.hasOwnProperty(GTM.tag()))
588                    GTM[functionName](events[GTM.tag()]);
589            },
590
591            setupPinterestObject: function () {
592                Pinterest = window.pys.Pinterest || Pinterest;
593                return Pinterest;
594            },
595
596            setupBingObject: function () {
597                Bing = window.pys.Bing || Bing;
598                return Bing;
599            },
600
601	        setupRedditObject: function () {
602		        Reddit = window.pys.Reddit || Reddit;
603		        return Reddit;
604	        },
605
606            // Clone all object members to another and return it
607            copyProperties: function (from, to) {
608                for (var key in from) {
609                    if("function" == typeof from[key]) {
610                        continue;
611                    }
612                    to[key] = from[key];
613                }
614                return to;
615            },
616
617            manageCookies: function () {
618
619                if (options.gdpr.cookiebot_integration_enabled && typeof Cookiebot !== 'undefined') {
620                    if (Cookiebot.consented === false && !Cookiebot.consent['marketing'] && !Cookiebot.consent['statistics']) {
621                        return;
622                    }
623                }
624                let cm_consent_not_expressed = false;
625                if ( options.gdpr.consent_magic_integration_enabled && window.CS_Data !== undefined && window.CS_Data.cs_refresh_after_consent == 1 ) {
626                    if ( Cookies.get( 'cs_viewed_cookie_policy' ) === undefined ) {
627                        cm_consent_not_expressed = true;
628                    }
629                }
630
631                if( !cm_consent_not_expressed && isNewSession && !options.cookie.disabled_all_cookie && !options.cookie.disabled_start_session_cookie) {
632                    let duration = options.last_visit_duration * 60000
633                    var now = new Date();
634                    now.setTime(now.getTime() + duration);
635                    Cookies.set('pys_session_limit', true,{ expires: now, path: '/',domain: domain })
636                    Cookies.set('pys_start_session', true,{path: '/',domain: domain});
637                }
638
639                if (options.ajaxForServerEvent && !Cookies.get('pbid') && Facebook.isEnabled()) {
640                    jQuery.ajax({
641                        url: options.ajaxUrl,
642                        dataType: 'json',
643                        data: {
644                            action: 'pys_get_pbid'
645                        },
646                        success: function (res) {
647                            if (res.data && res.data.pbid != false && options.send_external_id) {
648                                if(!(options.cookie.disabled_all_cookie || options.cookie.externalID_disabled_by_api)){
649                                    var expires = parseInt(options.external_id_expire || 180);
650                                    Cookies.set('pbid', res.data.pbid, { expires: expires, path: '/',domain: domain });
651                                }
652
653                                if(options.hasOwnProperty('facebook')) {
654                                    options.facebook.advancedMatching = {
655                                        ...options.facebook.advancedMatching,  // spread current advancedMatching values
656                                        external_id: res.data.pbid
657                                    };
658                                }
659                            }
660                        }
661                    });
662                } else if (Cookies.get('pbid') && Facebook.isEnabled()){
663                    if(Facebook.advancedMatching() && Facebook.advancedMatching().external_id && !(options.cookie.disabled_all_cookie || options.cookie.externalID_disabled_by_api)){
664                        let expires = parseInt(options.external_id_expire || 180);
665                        Cookies.set('pbid', Facebook.advancedMatching().external_id, { expires: expires, path: '/',domain: domain });
666                    }
667                }
668
669                let expires = parseInt(options.cookie_duration); //  days
670                let queryVars = getQueryVars();
671                let landing = window.location.href.split('?')[0];
672                try {
673                    // save data for first visit
674                    if(Cookies.get('pys_first_visit') === undefined && (!options.cookie.disabled_all_cookie)) {
675
676                        if(!options.cookie.disabled_first_visit_cookie)
677                        {
678                            Cookies.set('pys_first_visit', true, { expires: expires, path: '/',domain: domain });
679                        }
680                        else {
681                            Cookies.remove('pys_first_visit')
682                        }
683
684                        if(!options.cookie.disabled_trafficsource_cookie)
685                        {
686                            Cookies.set('pysTrafficSource', getTrafficSource(), { expires: expires,path: '/',domain: domain });
687                        }
688                        else {
689                            Cookies.remove('pysTrafficSource')
690                        }
691
692                        if(!options.cookie.disabled_landing_page_cookie)
693                        {
694                            Cookies.set('pys_landing_page',landing,{ expires: expires,path: '/',domain: domain });
695                        }
696                        else {
697                            Cookies.remove('pys_landing_page')
698                        }
699
700                        if(!options.cookie.disabled_utmTerms_cookie)
701                        {
702                            $.each(utmTerms, function (index, name) {
703                                if (queryVars.hasOwnProperty(name)) {
704                                    Cookies.set('pys_' + name, queryVars[name], { expires: expires,path: '/',domain: domain });
705                                } else {
706                                    Cookies.remove('pys_' + name)
707                                }
708                            });
709                        }
710                        else {
711                            $.each(utmTerms, function (index, name) {
712                                Cookies.remove('pys_' + name)
713                            });
714                        }
715
716                        if(!options.cookie.disabled_utmId_cookie)
717                        {
718                            $.each(utmId,function(index,name) {
719                                if (queryVars.hasOwnProperty(name)) {
720                                    Cookies.set('pys_' + name, queryVars[name], { expires: expires,path: '/',domain: domain });
721                                } else {
722                                    Cookies.remove('pys_' + name)
723                                }
724                            })
725                        }
726                        else {
727                            $.each(utmId, function (index, name) {
728                                Cookies.remove('pys_' + name)
729                            });
730                        }
731                    }
732
733                    // save data for last visit if it new session
734                    if(isNewSession && (!options.cookie.disabled_all_cookie)) {
735                        if(!options.cookie.disabled_trafficsource_cookie)
736                        {
737                            Cookies.set('last_pysTrafficSource', getTrafficSource(), { expires: expires,path: '/',domain: domain });
738                        }
739                        else {
740                            Cookies.remove('last_pysTrafficSource')
741                        }
742
743                        if(!options.cookie.disabled_landing_page_cookie)
744                        {
745                            Cookies.set('last_pys_landing_page',landing,{ expires: expires,path: '/',domain: domain });
746                        }
747                        else {
748                            Cookies.remove('last_pys_landing_page')
749                        }
750
751                        if(!options.cookie.disabled_utmTerms_cookie)
752                        {
753                            $.each(utmTerms, function (index, name) {
754                                if (queryVars.hasOwnProperty(name)) {
755                                    Cookies.set('last_pys_' + name, queryVars[name], { expires: expires,path: '/',domain: domain });
756                                } else {
757                                    Cookies.remove('last_pys_' + name)
758                                }
759                            });
760                        }
761                        else {
762                            $.each(utmTerms, function (index, name) {
763                                Cookies.remove('last_pys_' + name)
764                            });
765                        }
766
767                        if(!options.cookie.disabled_utmId_cookie)
768                        {
769                            $.each(utmId,function(index,name) {
770                                if (queryVars.hasOwnProperty(name)) {
771                                    Cookies.set('last_pys_' + name, queryVars[name], { expires: expires,path: '/',domain: domain });
772                                } else {
773                                    Cookies.remove('last_pys_' + name)
774                                }
775                            })
776                        }
777                        else {
778                            $.each(utmId, function (index, name) {
779                                Cookies.remove('last_pys_' + name)
780                            });
781                        }
782
783                    }
784                    if(options.cookie.disabled_start_session_cookie) {
785                        Cookies.remove('pys_start_session')
786                        Cookies.remove('pys_session_limit')
787                    }
788                    if(options.cookie.disabled_all_cookie)
789                    {
790                        Cookies.remove('pys_first_visit')
791                        Cookies.remove('pysTrafficSource')
792                        Cookies.remove('pys_landing_page')
793                        Cookies.remove('last_pys_landing_page')
794                        Cookies.remove('last_pysTrafficSource')
795                        Cookies.remove('pys_start_session')
796                        Cookies.remove('pys_session_limit')
797                        $.each(Utils.utmTerms, function (index, name) {
798                            Cookies.remove('pys_' + name)
799                        });
800                        $.each(Utils.utmId,function(index,name) {
801                            Cookies.remove('pys_' + name)
802                        })
803                        $.each(Utils.utmTerms, function (index, name) {
804                            Cookies.remove('last_pys_' + name)
805                        });
806                        $.each(Utils.utmId,function(index,name) {
807                            Cookies.remove('last_pys_' + name)
808                        });
809                    }
810                } catch (e) {
811                    console.error(e);
812                }
813            },
814            /**
815             * Generate unique ID
816             */
817            generateUniqueId : function (event) {
818                if(event.eventID.length == 0 || (event.type == "static" && options.ajaxForServerStaticEvent) || (event.type !== "static" && options.ajaxForServerEvent)) {
819                    let idKey = event.hasOwnProperty('custom_event_post_id') ? event.custom_event_post_id : event.e_id;
820                    if (!uniqueId.hasOwnProperty(idKey)) {
821                        uniqueId[idKey] = pys_generate_token();
822                    }
823                    return uniqueId[idKey];
824                }
825                else if(event.eventID.length !== 0)
826                {
827                    return event.eventID;
828                }
829            },
830
831            // Flatten object for sendBeacon compatibility
832            flattenObject: function (obj, prefix = '', res = {}) {
833                for (const [key, value] of Object.entries(obj)) {
834                    const prefixedKey = prefix
835                        ? (Array.isArray(obj) ? `${prefix}[${key}]` : `${prefix}[${key}]`)
836                        : key;
837                    if (value !== null && typeof value === 'object' && !(value instanceof File)) {
838                        this.flattenObject(value, prefixedKey, res);
839                    } else {
840                        res[prefixedKey] = value;
841                    }
842                }
843                return res;
844            },
845
846            sendServerAjaxRequest : function(url, data) {
847                if (data.action === 'pys_api_event' && data.pixel === 'facebook' && window.pysFacebookRest) {
848                    // Use Facebook REST API
849                    this.sendRestAPIRequest(data, 'facebook');
850                    return;
851                }
852
853                // Check if sendBeacon is enabled and supported
854                if (options.useSendBeacon && navigator.sendBeacon) {
855                    try {
856                        // Flatten the data object for sendBeacon compatibility
857                        const flattenedData = this.flattenObject(data);
858                        const formData = new URLSearchParams();
859
860                        // Convert flattened data to URLSearchParams
861                        for (const [key, value] of Object.entries(flattenedData)) {
862                            if (value !== null && value !== undefined) {
863                                formData.append(key, value);
864                            }
865                        }
866
867                        // Try to send using sendBeacon
868                        const success = navigator.sendBeacon(url, formData);
869                        if (success) {
870                            return; // Successfully sent via sendBeacon
871                        }
872                    } catch (e) {
873                        // If sendBeacon fails, fall back to jQuery.ajax
874                        if (options.debug) {
875                            console.log('PYS: sendBeacon failed, falling back to jQuery.ajax:', e);
876                        }
877                    }
878                }
879
880                // Fallback to jQuery.ajax
881                jQuery.ajax( {
882                    type: 'POST',
883                    url: url,
884                    data: data,
885                    headers: {
886                        'Cache-Control': 'no-cache'
887                    },
888                    success: function () {
889                    },
890                } );
891            },
892
893            sendRestAPIRequest: function (data, platform) {
894                let restApiUrl;
895
896                // Get platform-specific REST API configuration
897                switch (platform) {
898                    case 'facebook':
899                        restApiUrl = window.pysFacebookRest ? window.pysFacebookRest.restApiUrl : '/wp-json/pys-facebook/v1/event';
900                        break;
901                    default:
902                        console.error('PYS: Unknown platform for REST API:', platform);
903                        this.sendAjaxFallback(data);
904                        return;
905                }
906
907                // Prepare data for REST API
908                const restApiData = {
909                    event: data.event,
910                    event_slug: data.event_slug || '',
911                    data: JSON.stringify(data.data || {}),
912                    ids: JSON.stringify(data.ids || []),
913                    eventID: data.event_id || data.eventID || '',
914                    woo_order: data.woo_order || '0',
915                    edd_order: data.edd_order || '0',
916                    order_key: data.order_key || ''
917                };
918
919                const forwardedReferrer = data.referrer_url || pageReferrer;
920                if (forwardedReferrer) {
921                    restApiData.referrer_url = forwardedReferrer;
922                }
923
924                // Variant A: send the actual page URL where the event happened
925                // so the server sets event_source_url to this page, not the
926                // REST endpoint. PHP applies enable_remove_source_url_params.
927                restApiData.event_source_url = data.url || window.location.href;
928
929                // Try to send using sendBeacon first (if enabled)
930                if (options.useSendBeacon && navigator.sendBeacon) {
931                    try {
932                        const formData = new URLSearchParams();
933                        for (const [key, value] of Object.entries(restApiData)) {
934                            formData.append(key, value);
935                        }
936
937                        if (navigator.sendBeacon(restApiUrl, formData)) {
938                            return;
939                        }
940                    } catch (e) {
941                        // sendBeacon failed, continue to fetch
942                    }
943                }
944
945                // Try to send using fetch with REST API
946                if (window.fetch) {
947                    const headers = {
948                        'Content-Type': 'application/json'
949                    };
950
951                    fetch(restApiUrl, {
952                        method: 'POST',
953                        headers: headers,
954                        body: JSON.stringify(restApiData)
955                    })
956                        .then(response => {
957                            if (!response.ok) {
958                                throw new Error(platform + ' REST API request failed: ' + response.status);
959                            }
960                        })
961                        .catch(error => {
962                            // Fallback to AJAX if REST API fails
963                            if (options.debug) {
964                                console.log('PYS: ' + platform + ' REST API failed, falling back to AJAX:', error);
965                            }
966                            this.sendAjaxFallback(data);
967                        });
968                } else {
969                    // Fallback to AJAX if fetch is not supported
970                    this.sendAjaxFallback(data);
971                }
972            },
973
974
975
976            // Fallback AJAX method
977            sendAjaxFallback: function (data) {
978                jQuery.ajax({
979                    type: 'POST',
980                    url: options.ajaxUrl,
981                    data: data,
982                    headers: {
983                        'Cache-Control': 'no-cache'
984                    },
985                    success: function () {
986                    },
987                });
988            },
989            // clone object
990            clone: function(obj) {
991                var copy;
992
993                // Handle the 3 simple types, and null or undefined
994                if (null == obj || "object" != typeof obj) return obj;
995
996                // Handle Date
997                if (obj instanceof Date) {
998                    copy = new Date();
999                    copy.setTime(obj.getTime());
1000                    return copy;
1001                }
1002
1003                // Handle Array
1004                if (obj instanceof Array) {
1005                    copy = [];
1006                    for (var i = 0, len = obj.length; i < len; i++) {
1007                        if("function" == typeof obj[i]) {
1008                            continue;
1009                        }
1010                        copy[i] = Utils.clone(obj[i]);
1011                    }
1012                    return copy;
1013                }
1014
1015                // Handle Object
1016                if (obj instanceof Object) {
1017                    copy = {};
1018                    for (var attr in obj) {
1019                        if (obj.hasOwnProperty(attr)) {
1020                            if("function" == typeof obj[attr]) {
1021                                continue;
1022                            }
1023                            copy[attr] = Utils.clone(obj[attr]);
1024                        }
1025                    }
1026                    return copy;
1027                }
1028
1029                return obj;
1030            },
1031
1032            // Returns array of elements with given tag name
1033            getTagsAsArray: function (tag) {
1034                return [].slice.call(document.getElementsByTagName(tag));
1035            },
1036
1037            getRequestParams: function () {
1038                return [];
1039            },
1040
1041            /**
1042             * CUSTOM EVENTS
1043             */
1044
1045            setupMouseOverClickEvents: function (eventId, triggers) {
1046
1047                // Non-default binding used to avoid situations when some code in external js
1048                // stopping events propagation, eg. returns false, and our handler will never called
1049                document.addEventListener('mouseover', function(event) {
1050                    var matchedElements = Array.from(document.querySelectorAll(triggers));
1051                    var clickedElement = event.target;
1052                    var closestMatch = clickedElement.closest(triggers);
1053                    if (matchedElements.includes(clickedElement) || closestMatch){
1054                        if (event.target.classList.contains('pys-mouse-over-' + eventId)) {
1055                            return true;
1056                        } else {
1057                            event.target.classList.add('pys-mouse-over-' + eventId);
1058                        }
1059
1060                        Utils.fireTriggerEvent(eventId);
1061                    }
1062                });
1063
1064            },
1065
1066            setupCSSClickEvents: function (eventId, triggers) {
1067                // Non-default binding used to avoid situations when some code in external js
1068                // stopping events propagation, eg. returns false, and our handler will never called
1069                document.addEventListener('click', function(event) {
1070                    let matchedElements = Array.from(document.querySelectorAll(triggers)),
1071                        clickedElement = event.target,
1072                        closestMatch = clickedElement.closest(triggers);
1073
1074                    if (matchedElements.includes(clickedElement) || closestMatch){
1075                        Utils.fireTriggerEvent(eventId);
1076                    }
1077                }, true);
1078            },
1079
1080            setupURLClickEvents: function () {
1081
1082                if( !options.triggerEventTypes.hasOwnProperty('url_click') ) {
1083                    return;
1084                }
1085                // Non-default binding used to avoid situations when some code in external js
1086                // stopping events propagation, eg. returns false, and our handler will never called
1087                $('a[data-pys-event-id]').onFirst('click', function (evt) {
1088
1089                    $(this).attr('data-pys-event-id').split(',').forEach(function (eventId) {
1090
1091                        eventId = parseInt(eventId);
1092
1093                        if (isNaN(eventId) === false) {
1094                            Utils.fireTriggerEvent(eventId);
1095                        }
1096
1097                    });
1098
1099                });
1100
1101
1102            },
1103
1104            setupScrollPosEvents: function (eventId, triggers) {
1105
1106                var scrollPosThresholds = {},
1107                    docHeight = $(document).height() - $(window).height();
1108
1109                // convert % to absolute positions
1110                $.each(triggers, function (index, scrollPos) {
1111
1112                    // convert % to pixels
1113                    scrollPos = docHeight * scrollPos / 100;
1114                    scrollPos = Math.round(scrollPos);
1115
1116                    scrollPosThresholds[scrollPos] = eventId;
1117
1118                });
1119
1120                $(document).on("scroll",function () {
1121
1122                    var scrollPos = $(window).scrollTop();
1123
1124                    $.each(scrollPosThresholds, function (threshold, eventId) {
1125
1126                        // position has not reached yes
1127                        if (scrollPos <= threshold) {
1128                            return true;
1129                        }
1130
1131                        // fire event only once
1132                        if (eventId === null) {
1133                            return true;
1134                        } else {
1135                            scrollPosThresholds[threshold] = null;
1136                        }
1137
1138                        Utils.fireTriggerEvent(eventId);
1139
1140                    });
1141
1142                });
1143
1144
1145            },
1146            setupCommentEvents : function (eventId,triggers) {
1147                $('form.comment-form').on("submit",function () {
1148                    Utils.fireTriggerEvent(eventId);
1149                });
1150            },
1151
1152            /**
1153             * Events
1154             */
1155
1156            fireTriggerEvent: function (eventId) {
1157
1158                if (!options.triggerEvents.hasOwnProperty(eventId)) {
1159                    return;
1160                }
1161
1162                var event = {};
1163                var events = options.triggerEvents[eventId];
1164
1165                if (events.hasOwnProperty('facebook')) {
1166                    event = Utils.getFormFilledData(events.facebook);
1167                    Facebook.fireEvent(event.name, event);
1168                }
1169
1170                if (events.hasOwnProperty('ga')) {
1171                    event = Utils.getFormFilledData(events.ga);
1172                    Analytics.fireEvent(event.name, event);
1173                }
1174
1175                if (events.hasOwnProperty('pinterest')) {
1176                    event = Utils.getFormFilledData(events.pinterest);
1177                    Pinterest.fireEvent(event.name, event);
1178                }
1179
1180                if (events.hasOwnProperty('bing')) {
1181                    event = Utils.getFormFilledData(events.bing);
1182                    Bing.fireEvent(event.name, event);
1183                }
1184
1185	            if (events.hasOwnProperty('reddit')) {
1186                    event = Utils.getFormFilledData(events.reddit);
1187                    Reddit.fireEvent(event.name, event);
1188	            }
1189
1190                if (events.hasOwnProperty('gtm')) {
1191                    event = Utils.getFormFilledData(events.gtm);
1192                    GTM.fireEvent(event.name, event);
1193                }
1194            },
1195
1196            fireStaticEvents: function (pixel) {
1197
1198                if (options.staticEvents.hasOwnProperty(pixel)) {
1199
1200                    $.each(options.staticEvents[pixel], function (eventName, events) {
1201                        $.each(events, function (index, eventData) {
1202
1203                            eventData.fired = eventData.fired || false;
1204
1205                            if (!eventData.fired) {
1206                                eventData = Utils.getFormFilledData(eventData);
1207                                var fired = false;
1208
1209                                // fire event
1210                                if ('facebook' === pixel) {
1211                                    fired = Facebook.fireEvent(eventData.name, eventData);
1212                                } else if ('ga' === pixel) {
1213                                    fired = Analytics.fireEvent(eventData.name, eventData);
1214                                } else if ('pinterest' === pixel) {
1215                                    fired = Pinterest.fireEvent(eventData.name, eventData);
1216                                } else if ('bing' === pixel) {
1217                                    fired = Bing.fireEvent(eventData.name, eventData);
1218                                } else if ('gtm' === pixel) {
1219                                    fired = GTM.fireEvent(eventData.name, eventData);
1220                                } else if ('reddit' === pixel) {
1221	                                fired = Reddit.fireEvent(eventData.name, eventData);
1222                                }
1223
1224                                // prevent event double event firing
1225                                eventData.fired = fired;
1226
1227                            }
1228
1229                        });
1230                    });
1231
1232                }
1233
1234            },
1235
1236            /**
1237             * Load tag's JS
1238             *
1239             * @link: https://developers.google.com/analytics/devguides/collection/gtagjs/
1240             * @link: https://developers.google.com/analytics/devguides/collection/gtagjs/custom-dims-mets
1241             */
1242            loadGoogleTag: function (id) {
1243
1244                if (!gtag_loaded) {
1245                    let dataLayerName = this.resolveGaDataLayerName();
1246                    this.dataLayerName = dataLayerName;
1247                    (function (window, document, src) {
1248                        var a = document.createElement('script'),
1249                            m = document.getElementsByTagName('script')[0];
1250                        a.async = 1;
1251                        a.src = src;
1252                        m.parentNode.insertBefore(a, m);
1253                    })(window, document, '//www.googletagmanager.com/gtag/js?id=' + id+'&l='+this.dataLayerName);
1254
1255                    window[dataLayerName] = window[dataLayerName] || [];
1256                    // Published only so custom client code and third-party snippets
1257                    // have a gtag() to call. PYS itself must never route through it:
1258                    // the `||` means an earlier definition (Site Kit, a theme, the
1259                    // GTM bootstrap) keeps ownership and would point at its own data
1260                    // layer. Use Utils.gtagPush() instead.
1261                    window.gtag = window.gtag || function gtag() {
1262                        window[dataLayerName].push(arguments);
1263                    };
1264
1265                    if ( options.google_consent_mode ) {
1266                        let data = {};
1267                        data[ 'analytics_storage' ] = options.gdpr.analytics_storage.enabled ? options.gdpr.analytics_storage.value : 'granted';
1268                        data[ 'ad_storage' ] = options.gdpr.ad_storage.enabled ? options.gdpr.ad_storage.value : 'granted';
1269                        data[ 'ad_user_data' ] = options.gdpr.ad_user_data.enabled ? options.gdpr.ad_user_data.value : 'granted';
1270                        data[ 'ad_personalization' ] = options.gdpr.ad_personalization.enabled ? options.gdpr.ad_personalization.value : 'granted';
1271
1272                        this.loadDefaultConsent( 'consent', 'default', data );
1273                    }
1274
1275                    Utils.gtagPush('js', new Date());
1276                    gtag_loaded = true;
1277
1278                }
1279
1280            },
1281
1282            /**
1283             * Name of the data layer gtag.js is loaded with, see loadGoogleTag().
1284             * Pure, so it can be resolved before the tag is loaded.
1285             */
1286            resolveGaDataLayerName: function () {
1287                if ( options.hasOwnProperty( 'GATags' ) ) {
1288                    switch ( options.GATags.ga_datalayer_type ) {
1289                        case 'default':
1290                            return 'dataLayerPYS';
1291                        case 'custom':
1292                            return options.GATags.ga_datalayer_name;
1293                        default:
1294                            return 'dataLayer';
1295                    }
1296                }
1297                return this.dataLayerName || 'dataLayer';
1298            },
1299
1300            /**
1301             * Sends a gtag command (js/config/set/event/consent) to the GA data layer.
1302             *
1303             * Deliberately does NOT call window.gtag. That global is created with
1304             * `window.gtag = window.gtag || ...`, so whoever defined it first owns it:
1305             * if a theme, Site Kit or the GTM bootstrap got there first, our config
1306             * and event commands would silently land in their data layer while the
1307             * gtag.js instance we loaded (&l=<GA layer>) never receives a config —
1308             * Tag Assistant then reports "Some hits will only be sent after a config
1309             * command is issued". Pushing the arguments object is exactly what gtag()
1310             * does internally, so the payload is identical.
1311             */
1312            gtagPush: function () {
1313                var name = this.dataLayerName || this.resolveGaDataLayerName();
1314                window[ name ] = window[ name ] || [];
1315                window[ name ].push( arguments );
1316            },
1317
1318            /**
1319             * Whether a send_to value actually points at a destination.
1320             *
1321             * gtag('event', name, { send_to: '' }) has no destination, so gtag.js
1322             * parks the hit until a matching config command appears. Tag Assistant
1323             * reports that as "Some hits will only be sent after a config command
1324             * is issued via gtag('config')". Callers must skip the event instead.
1325             */
1326            hasSendToTarget: function (target) {
1327                if (Array.isArray(target)) {
1328                    return target.length > 0;
1329                }
1330                return typeof target === 'string' && target !== '';
1331            },
1332
1333            loadDefaultConsent: function() {
1334                this.gtagPush.apply( this, arguments );
1335            },
1336
1337            loadGTMScript: function (id = '') {
1338                // Strip trailing slash to allow both "domain.com" and "domain.com/" inputs.
1339                const domain = ( options.gtm.gtm_container_domain ?? 'www.googletagmanager.com' ).replace( /\/$/, '' );
1340                const loader = options.gtm.gtm_container_identifier ?? 'gtm';
1341                const gtm_auth = options.gtm.gtm_auth ?? '';
1342                const gtm_preview = options.gtm.gtm_preview ?? '';
1343                const datalayer_name = options.gtm.gtm_dataLayer_name ?? 'dataLayer';
1344
1345                window[ datalayer_name ] = window[ datalayer_name ] || [];
1346                window.gtag = window.gtag || function gtag() {
1347                    window[ datalayer_name ].push( arguments );
1348                };
1349
1350                // Push consent defaults synchronously before the GTM script tag is inserted.
1351                // When PYS loads the GTM snippet itself (non-dataLayer-only mode), this ensures
1352                // defaults are in the dataLayer before GTM processes any events.
1353                this.pushGTMConsentDefaults( datalayer_name );
1354
1355                (function(w, d, s, l, i) {
1356                    w[l] = w[l] || [];
1357                    w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
1358                    const f = d.getElementsByTagName(s)[0];
1359                    const j = d.createElement(s);
1360                    const dl = l !== 'dataLayer' ? '&l=' + l : '';
1361                    j.async = true;
1362
1363                    // Detect domain format: bare hostname (legacy) vs full URL / relative path (new).
1364                    // Legacy "www.googletagmanager.com" → prepe
1364nd https:// and append .js.
1365                    // New format "/metrics" or "https://custom.com" → use as-is.
1366                    //   Identifier with extension (e.g. "gtm.js")  → file:   domain/gtm.js?id=
1367                    //   Identifier without extension (e.g. "g4h25o") → path: domain/g4h25o/?id=
1368                    const isNewFormat = domain.startsWith( 'http://' )
1369                                     || domain.startsWith( 'https://' )
1370                                     || domain.startsWith( '/' );
1371
1372                    if ( !isNewFormat ) {
1373                        j.src = 'https://' + domain + '/' + loader + '.js?id=' + i + dl;
1374                    } else {
1375                        const hasExtension = loader.includes( '.' );
1376                        j.src = domain + '/' + loader + ( hasExtension ? '?' : '/?' ) + 'id=' + i + dl;
1377                    }
1378
1379                    if (gtm_auth && gtm_preview) {
1380                        j.src += '&gtm_auth=' + gtm_auth + '&gtm_preview=' + gtm_preview + '&gtm_cookies_win=x';
1381                    }
1382                    f.parentNode.insertBefore(j, f);
1383                })(window, document, 'script', datalayer_name, id);
1384
1385            },
1386
1387            loadDefaultGTMConsent: function() {
1388                window[ this.GTMdataLayerName ].push( arguments );
1389            },
1390
1391            // Extracted helper: push GTM consent defaults to the dataLayer.
1392            // Called from loadGTMScript (when PYS loads the snippet) and from
1393            // GTM.loadPixel when gtm_just_data_layer is enabled, so defaults are
1394            // always present regardless of which code path loads the GTM container.
1395            pushGTMConsentDefaults: function( datalayer_name ) {
1396                if ( !options.google_consent_mode ) {
1397                    return;
1398                }
1399                let data = {};
1400                data[ 'analytics_storage' ]  = options.gdpr.analytics_storage.enabled  ? options.gdpr.analytics_storage.value  : 'granted';
1401                data[ 'ad_storage' ]         = options.gdpr.ad_storage.enabled         ? options.gdpr.ad_storage.value         : 'granted';
1402                data[ 'ad_user_data' ]       = options.gdpr.ad_user_data.enabled       ? options.gdpr.ad_user_data.value       : 'granted';
1403                data[ 'ad_personalization' ] = options.gdpr.ad_personalization.enabled ? options.gdpr.ad_personalization.value : 'granted';
1404                this.GTMdataLayerName = datalayer_name;
1405                this.loadDefaultGTMConsent( 'consent', 'default', data );
1406            },
1407
1408            /**
1409             * GDPR
1410             */
1411
1412            loadPixels: function () {
1413
1414                if (options.gdpr.ajax_enabled && !options.gdpr.consent_magic_integration_enabled) {
1415
1416                    // retrieves actual PYS GDPR filters values which allow to avoid cache issues
1417                    $.get({
1418                        url: options.ajaxUrl,
1419                        dataType: 'json',
1420                        data: {
1421                            action: 'pys_get_gdpr_filters_values'
1422                        },
1423                        success: function (res) {
1424
1425                            if (res.success) {
1426
1427                                options.gdpr.all_disabled_by_api = res.data.all_disabled_by_api;
1428                                options.gdpr.facebook_disabled_by_api = res.data.facebook_disabled_by_api;
1429                                options.gdpr.analytics_disabled_by_api = res.data.analytics_disabled_by_api;
1430                                options.gdpr.google_ads_disabled_by_api = res.data.google_ads_disabled_by_api;
1431                                options.gdpr.pinterest_disabled_by_api = res.data.pinterest_disabled_by_api;
1432                                options.gdpr.bing_disabled_by_api = res.data.bing_disabled_by_api;
1433	                            options.gdpr.reddit_disabled_by_api = res.data.reddit_disabled_by_api;
1434
1435                                options.cookie.externalID_disabled_by_api = res.data.externalID_disabled_by_api;
1436                                options.cookie.disabled_all_cookie = res.data.disabled_all_cookie;
1437                                options.cookie.disabled_advanced_form_data_cookie = res.data.disabled_advanced_form_data_cookie;
1438                                options.cookie.disabled_landing_page_cookie = res.data.disabled_landing_page_cookie;
1439                                options.cookie.disabled_first_visit_cookie = res.data.disabled_first_visit_cookie;
1440                                options.cookie.disabled_trafficsource_cookie = res.data.disabled_trafficsource_cookie;
1441                                options.cookie.disabled_utmTerms_cookie = res.data.disabled_utmTerms_cookie;
1442                                options.cookie.disabled_utmId_cookie = res.data.disabled_utmId_cookie;
1443                            }
1444
1445                            loadPixels();
1446
1447                        }
1448                    });
1449
1450                } else {
1451                    loadPixels();
1452                }
1453
1454            },
1455
1456            consentGiven: function (pixel) {
1457
1458                /**
1459                 * ConsentMagic
1460                 */
1461                if ( options.gdpr.consent_magic_integration_enabled && typeof CS_Data !== "undefined" ) {
1462
1463                    let test_prefix = CS_Data.test_prefix;
1464                    if (
1465                        ( ( typeof CS_Data.cs_google_consent_mode_enabled !== "undefined" && CS_Data.cs_google_consent_mode_enabled == 1 ) && ( pixel == 'analytics' || pixel == 'google_ads' ) )
1466                        || ( typeof CS_Data.cs_meta_ldu_mode !== "undefined" && CS_Data.cs_meta_ldu_mode && pixel == 'facebook' )
1467                        || ( typeof CS_Data.cs_reddit_ldu_mode !== "undefined" && CS_Data.cs_reddit_ldu_mode && pixel == 'reddit' )
1468                        || ( typeof CS_Data.cs_bing_consent_mode !== "undefined" && CS_Data.cs_bing_consent_mode.ad_storage.enabled && pixel == 'bing' )
1469                    ) {
1470                        if ( CS_Data.cs_cache_enabled == 0 || ( CS_Data.cs_cache_enabled == 1 && window.CS_Cache && window.CS_Cache.check_status ) ) {
1471                            return true;
1472                        } else {
1473                            return false;
1474                        }
1475                    }
1476
1477                    if( pixel == 'facebook' && ( CS_Data.cs_script_cat.facebook == 0 || CS_Data.cs_script_cat.facebook == CS_Data.cs_necessary_cat_id ) ) {
1478                        return true;
1479                    } else if( pixel == 'bing' && ( CS_Data.cs_script_cat.bing == 0 || CS_Data.cs_script_cat.bing == CS_Data.cs_necessary_cat_id ) ) {
1480                        return true;
1481                    } else if( pixel == 'analytics' && ( CS_Data.cs_script_cat.analytics == 0 || CS_Data.cs_script_cat.analytics == CS_Data.cs_necessary_cat_id ) ) {
1482                        return true;
1483                    } else if( pixel == 'google_ads' && ( CS_Data.cs_script_cat.gads == 0 || CS_Data.cs_script_cat.gads == CS_Data.cs_necessary_cat_id ) ) {
1484                        return true;
1485                    } else if( pixel == 'pinterest' && ( CS_Data.cs_script_cat.pinterest == 0 || CS_Data.cs_script_cat.pinterest == CS_Data.cs_necessary_cat_id ) ) {
1486                        return true;
1487                    } else if( pixel == 'tiktok' && ( CS_Data.cs_script_cat.tiktok == 0 || CS_Data.cs_script_cat.tiktok == CS_Data.cs_necessary_cat_id ) ) {
1488                        return true;
1489		            } else if( pixel == 'reddit' && ( CS_Data.cs_script_cat?.reddit == 0 || CS_Data.cs_script_cat?.reddit == CS_Data.cs_necessary_cat_id ) ) {
1490		                return true;
1491		            }
1492
1493                    let substring = "cs_enabled_cookie_term",
1494                        theCookies = document.cookie.split( ';' );
1495
1496                    for ( let i = 1; i <= theCookies.length; i++ ) {
1497                        if ( theCookies[ i - 1 ].indexOf( substring ) !== -1 ) {
1498                            let categoryCookie = theCookies[ i - 1 ].replace( 'cs_enabled_cookie_term' + test_prefix + '_', '' );
1499                            categoryCookie = Number( categoryCookie.replace( /\D+/g, "" ) );
1500                            let cs_cookie_val = Cookies.get( 'cs_enabled_cookie_term' + test_prefix + '_' + categoryCookie );
1501
1502                            if ( categoryCookie === CS_Data.cs_script_cat.facebook && pixel == 'facebook' ) {
1503                                return cs_cookie_val == 'yes';
1504                            } else if ( categoryCookie === CS_Data.cs_script_cat.bing && pixel == 'bing' ) {
1505                                return cs_cookie_val == 'yes';
1506                            } else if ( categoryCookie === CS_Data.cs_script_cat.analytics && pixel == 'analytics' ) {
1507                                return cs_cookie_val == 'yes';
1508                            } else if ( categoryCookie === CS_Data.cs_script_cat.gads && pixel == 'google_ads' ) {
1509                                return cs_cookie_val == 'yes';
1510                            } else if ( categoryCookie === CS_Data.cs_script_cat.pinterest && pixel == 'pinterest' ) {
1511                                return cs_cookie_val == 'yes';
1512                            } else if ( categoryCookie === CS_Data.cs_script_cat.tiktok && pixel == 'tiktok' ) {
1513                                return cs_cookie_val == 'yes';
1514                            } else if ( categoryCookie === CS_Data.cs_script_cat?.reddit && pixel == 'reddit' ) {
1515	                            return cs_cookie_val == 'yes';
1516                            }
1517                        }
1518                    }
1519
1520                    return false;
1521                }
1522
1523                /**
1524                 * Real Cookie Banner
1525                 */
1526                if(options.gdpr.real_cookie_banner_integration_enabled) {
1527                    var consentApi = window.consentApi;
1528                    if (consentApi) {
1529                        switch (pixel) {
1530                            case "analytics":
1531                                return consentApi.consentSync("http", "_ga", "*").cookieOptIn;
1532                            case "facebook":
1533                                return consentApi.consentSync("http", "_fbp", "*").cookieOptIn;
1534                            case "pinterest":
1535                                return consentApi.consentSync("http", "_pinterest_sess", ".pinterest.com").cookieOptIn;
1536                            default:
1537                                return true;
1538                        }
1539                    }
1540                }
1541
1542                /**
1543                 * Cookiebot
1544                 */
1545                if (options.gdpr.cookiebot_integration_enabled && typeof Cookiebot !== 'undefined') {
1546
1547                    var cookiebot_consent_category = options.gdpr['cookiebot_' + pixel + '_consent_category'];
1548
1549                    if (options.gdpr[pixel + '_prior_consent_enabled']) {
1550                        if (Cookiebot.consented === true || Cookiebot.consent[cookiebot_consent_category]) {
1551                            return true;
1552                        }
1553                    } else {
1554                        if (Cookiebot.consent[cookiebot_consent_category]) {
1555                            return true;
1556                        }
1557                    }
1558
1559                    return false;
1560
1561                }
1562
1563                /**
1564                 * Cookie Notice
1565                 */
1566                if (options.gdpr.cookie_notice_integration_enabled && typeof cnArgs !== 'undefined') {
1567
1568                    var cn_cookie = Cookies.get(cnArgs.cookieName);
1569
1570                    if (options.gdpr[pixel + '_prior_consent_enabled']) {
1571                        if (typeof cn_cookie === 'undefined' || cn_cookie === 'true') {
1572                            return true;
1573                        }
1574                    } else {
1575                        if (cn_cookie === 'true') {
1576                            return true;
1577                        }
1578                    }
1579
1580                    return false;
1581
1582                }
1583
1584                /**
1585                 * Cookie Law Info
1586                 */
1587                if (options.gdpr.cookie_law_info_integration_enabled) {
1588                    var cli_cookie = Cookies.get('cookieyes-consent') ?? Cookies.get('wt_consent') ?? Cookies.get('viewed_cookie_policy');
1589                    if (options.gdpr[pixel + '_prior_consent_enabled']) {
1590                        if (typeof cli_cookie === 'undefined') return true;
1591                        if (
1592                            cli_cookie &&
1593                            (cli_cookie === Cookies.get('cookieyes-consent') || cli_cookie === Cookies.get('wt_consent'))
1594                        ) {
1595                            if (getCookieYes('analytics') === 'yes') {
1596                                return true;
1597                            }
1598                        } else if (cli_cookie && cli_cookie === Cookies.get('viewed_cookie_policy')) {
1599                            if (Cookies.get('viewed_cookie_policy') === 'yes') {
1600                                return true;
1601                            }
1602                        }
1603                    } else {
1604                        if (
1605                            cli_cookie &&
1606                            (cli_cookie === Cookies.get('cookieyes-consent') || cli_cookie === Cookies.get('wt_consent'))
1607                        ) {
1608                            if (getCookieYes('analytics') === 'yes') {
1609                                return true;
1610                            }
1611                        } else if (cli_cookie && cli_cookie === Cookies.get('viewed_cookie_policy')) {
1612                            if (Cookies.get('viewed_cookie_policy') === 'yes') {
1613                                return true;
1614                            }
1615                        }
1616                    }
1617                    return false;
1618                }
1619
1620                return true;
1621
1622            },
1623
1624            setupGdprCallbacks: function () {
1625                /**
1626                 * ConsentMagic
1627                 */
1628                if (options.gdpr.consent_magic_integration_enabled && typeof CS_Data !== "undefined") {
1629                    let test_prefix = CS_Data.test_prefix,
1630                        cs_refresh_after_consent = false,
1631                        substring = "cs_enabled_cookie_term";
1632
1633                    if (CS_Data.cs_refresh_after_consent == 1) {
1634                        cs_refresh_after_consent = CS_Data.cs_refresh_after_consent;
1635                    }
1636
1637                    let consent_actions = function () {
1638                        let theCookies = document.cookie.split( ';' );
1639
1640                        let consent = {
1641                            facebook: true,
1642                            ga: true,
1643                            google_ads: true,
1644                            tiktok: true,
1645                            bing: true,
1646                            pinterest: true,
1647                            gtm: true,
1648	                        reddit: true,
1649                        };
1650
1651                        for (let i = 1 ; i <= theCookies.length; i++) {
1652                            if (theCookies[i-1].indexOf(substring) !== -1) {
1653                                let categoryCookie = theCookies[i-1].replace('cs_enabled_cookie_term'+test_prefix+'_','');
1654                                categoryCookie = Number(categoryCookie.replace(/\D+/g,""));
1655                                let cs_cookie_val = Cookies.get('cs_enabled_cookie_term'+test_prefix+'_'+categoryCookie);
1656                                if(cs_cookie_val == 'yes') {
1657                                    if ( ( categoryCookie === CS_Data.cs_script_cat.facebook ) || ( typeof CS_Data.cs_meta_ldu_mode !== "undefined" && CS_Data.cs_meta_ldu_mode ) ) {
1658                                        Facebook.loadPixel();
1659                                    }
1660
1661                                    if ( categoryCookie === CS_Data.cs_script_cat.bing || ( typeof CS_Data.cs_bing_consent_mode !== "undefined" && CS_Data.cs_bing_consent_mode.ad_storage.enabled ) ) {
1662                                        Bing.loadPixel();
1663                                    }
1664
1665                                    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)
1665) {
1666
1667                                        Analytics.loadPixel();
1668                                    }
1669
1670                                    if (categoryCookie === CS_Data.cs_script_cat.pinterest) {
1671                                        Pinterest.loadPixel();
1672                                    }
1673
1674	                                if ( ( categoryCookie === CS_Data.cs_script_cat.reddit ) || ( typeof CS_Data.cs_reddit_ldu_mode !== "undefined" && CS_Data.cs_reddit_ldu_mode ) ) {
1675		                                Reddit.loadPixel();
1676	                                }
1677                                } else {
1678                                    if ( ( categoryCookie === CS_Data.cs_script_cat.facebook ) && ( typeof CS_Data.cs_meta_ldu_mode == "undefined" || !CS_Data.cs_meta_ldu_mode ) ) {
1679                                        Facebook.disable();
1680                                        consent.facebook = false;
1681                                    }
1682
1683                                    if ( categoryCookie === CS_Data.cs_script_cat.bing && ( typeof CS_Data.cs_bing_consent_mode == "undefined" || !CS_Data.cs_bing_consent_mode.ad_storage.enabled ) ) {
1684                                        Bing.disable();
1685                                        consent.bing = false;
1686                                    }
1687                                    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)) {
1688                                        Analytics.disable();
1689                                        consent.ga = false;
1690                                        consent.gtm = false;
1691                                    }
1692
1693                                    if (categoryCookie === CS_Data.cs_script_cat.pinterest) {
1694                                        Pinterest.disable();
1695                                        consent.pinterest = false;
1696                                    }
1697
1698	                                if ( ( categoryCookie === CS_Data.cs_script_cat.reddit ) && ( typeof CS_Data.cs_reddit_ldu_mode == "undefined" || !CS_Data.cs_reddit_ldu_mode ) ) {
1699		                                Reddit.disable();
1700		                                consent.reddit = false;
1701	                                }
1702                                }
1703                                if (Cookies.get('cs_enabled_advanced_matching') == 'yes') {
1704                                    Facebook.loadPixel();
1705                                }
1706                            }
1707                        }
1708                        Utils.setupGDPRData( consent );
1709                    }
1710
1711                    if (!cs_refresh_after_consent) {
1712                        consent_actions();
1713
1714                        $(document).on('click','.cs_action_btn',function(e) {
1715                            e.preventDefault();
1716
1717                            let consent = {
1718                                facebook: true,
1719                                ga: true,
1720                                bing: true,
1721                                pinterest: true,
1722                                gtm: true,
1723	                            reddit: true,
1724                            };
1725
1726                            let elm = $(this),
1727                                button_action = elm.attr('data-cs_action');
1728
1729                            if(button_action === 'allow_all') {
1730                                Facebook.loadPixel();
1731                                Bing.loadPixel();
1732                                Analytics.loadPixel();
1733                                Pinterest.loadPixel();
1734	                            Reddit.loadPixel();
1735
1736                                consent.facebook = true;
1737                                consent.bing = true;
1738                                consent.ga = true;
1739                                consent.pinterest = true;
1740	                            consent.reddit = true;
1741                                consent.gtm = true;
1742
1743                                Utils.setupGDPRData( consent );
1744                            } else if ( button_action === 'disable_all' ) {
1745                                if ( typeof CS_Data.cs_meta_ldu_mode == "undefined" || CS_Data.cs_meta_ldu_mode == 0 ) {
1746                                    Facebook.disable();
1747                                    consent.facebook = false;
1748                                }
1749
1750                                if ( typeof CS_Data.cs_bing_consent_mode == "undefined" || CS_Data.cs_bing_consent_mode.ad_storage.enabled == 0 ) {
1751                                    Bing.disable();
1752                                    consent.bing = false;
1753                                }
1754
1755                                if ( CS_Data.cs_google_analytics_consent_mode == 0 || typeof CS_Data.cs_google_analytics_consent_mode == "undefined" ) {
1756                                    Analytics.disable();
1757                                    consent.ga = false;
1758                                    consent.gtm = false;
1759                                }
1760
1761	                            if ( typeof CS_Data.cs_reddit_ldu_mode == "undefined" || CS_Data.cs_reddit_ldu_mode == 0 ) {
1762		                            Reddit.disable();
1763		                            consent.reddit = false;
1764	                            }
1765
1766                                Pinterest.disable();
1767                                consent.pinterest = false;
1768                                Utils.setupGDPRData( consent );
1769                            } else if ( button_action === 'cs_confirm' ) {
1770                                consent_actions();
1771                            }
1772                        });
1773                    }
1774                }
1775                /**
1776                 * Real Cookie Banner
1777                 */
1778                if(options.gdpr.real_cookie_banner_integration_enabled) {
1779                    let consentApi = window.consentApi;
1780                    if (consentApi) {
1781                        consentApi.consent("http", "_ga", "*")
1782                            .then(Analytics.loadPixel.bind(Analytics), Analytics.disable.bind(Analytics));
1783                        consentApi.consent("http", "_fbp", "*")
1784                            .then(Facebook.loadPixel.bind(Facebook), Facebook.disable.bind(Facebook));
1785                        consentApi.consent("http", "_pinterest_sess", ".pinterest.com")
1786                            .then(Pinterest.loadPixel.bind(Pinterest), Pinterest.disable.bind(Pinterest));
1787                        consentApi.consent("http", "_uetsid", "*")
1788                            .then(Bing.loadPixel.bind(Bing), Bing.disable.bind(Bing));
1789
1790                        let consent = {
1791                            facebook: true,
1792                            ga: true,
1793                            bing: true,
1794                            pinterest: true,
1795                            gtm: true,
1796                        };
1797
1798                        if (!consentApi.consentSync("http", "_ga", "*").cookieOptIn) {
1799                            consent.ga = false;
1800                            consent.gtm = false;
1801                        }
1802                        if (!consentApi.consentSync("http", "_fbp", "*").cookieOptIn) {
1803                            consent.facebook = false;
1804                        }
1805                        if (!consentApi.consentSync("http", "_pinterest_sess", ".pinterest.com").cookieOptIn) {
1806                            consent.pinterest = false;
1807                        }
1808                        if (!consentApi.consentSync("http", "_uetsid", "*").cookieOptIn) {
1809                            consent.bing = false;
1810                        }
1811                        Utils.setupGDPRData( consent );
1812                    }
1813                }
1814                /**
1815                 * Cookiebot
1816                 */
1817                if (options.gdpr.cookiebot_integration_enabled && typeof Cookiebot !== 'undefined') {
1818
1819                    window.addEventListener("CookiebotOnConsentReady", function() {
1820
1821                        let consent = {
1822                            facebook: true,
1823                            ga: true,
1824                            bing: true,
1825                            pinterest: true,
1826                            gtm: true,
1827                        };
1828
1829                        Utils.manageCookies();
1830                        if (Cookiebot.consent.marketing) {
1831                            Facebook.loadPixel();
1832                            Bing.loadPixel();
1833                            Pinterest.loadPixel();
1834
1835                            consent.facebook = true;
1836                            consent.bing = true;
1837                            consent.pinterest = true;
1838                        }
1839                        if (Cookiebot.consent.statistics) {
1840                            Analytics.loadPixel();
1841
1842                            consent.ga = true;
1843                            consent.gtm = true;
1844                        }
1845                        if (!Cookiebot.consent.marketing) {
1846                            Facebook.disable();
1847                            Pinterest.disable();
1848                            Bing.disable();
1849
1850                            consent.facebook = false;
1851                            consent.bing = false;
1852                            consent.pinterest = false;
1853                        }
1854                        if (!Cookiebot.consent.statistics) {
1855                            Analytics.disable();
1856
1857                            consent.ga = false;
1858                            consent.gtm = false;
1859                        }
1860
1861                        Utils.setupGDPRData( consent );
1862                    });
1863
1864                }
1865
1866                /**
1867                 * Cookie Notice
1868                 */
1869                if (options.gdpr.cookie_notice_integration_enabled) {
1870
1871                    $(document).onFirst('click', '.cn-set-cookie', function () {
1872
1873                        let consent = {};
1874
1875                        if ($(this).data('cookie-set') === 'accept') {
1876                            Facebook.loadPixel();
1877                            Analytics.loadPixel();
1878                            Pinterest.loadPixel();
1879                            Bing.loadPixel();
1880
1881                            consent = {
1882                                facebook: true,
1883                                ga: true,
1884                                bing: true,
1885                                pinterest: true,
1886                                gtm: true,
1887                            };
1888                        } else {
1889                            Facebook.disable();
1890                            Analytics.disable();
1891                            Pinterest.disable();
1892                            Bing.disable();
1893
1894                            consent = {
1895                                facebook: false,
1896                                ga: false,
1897                                bing: false,
1898                                pinterest: false,
1899                                gtm: false,
1900                            };
1901                        }
1902
1903                        Utils.setupGDPRData( consent );
1904                    });
1905
1906                    $(document).onFirst('click', '.cn-revoke-cookie', function () {
1907                        Facebook.disable();
1908                        Analytics.disable();
1909                        Pinterest.disable();
1910                        Bing.disable();
1911
1912                        let consent = {
1913                            facebook: false,
1914                            ga: false,
1915                            bing: false,
1916                            pinterest: false,
1917                            gtm: false,
1918                        };
1919                        Utils.setupGDPRData( consent );
1920
1921                    });
1922
1923                }
1924
1925                /**
1926                 * Cookie Law Info
1927                 */
1928                if (options.gdpr.cookie_law_info_integration_enabled) {
1929
1930                    $(document).onFirst('click', '#wt-cli-accept-all-btn,#cookie_action_close_header, .cky-btn-accept', function () {
1931                        setTimeout(function (){
1932                            let cli_cookie = Cookies.get('cookieyes-consent') ?? Cookies.get('wt_consent') ?? Cookies.get('viewed_cookie_policy');
1933                            if (typeof cli_cookie !== 'undefined') {
1934                                if (cli_cookie === Cookies.get('cookieyes-consent') || cli_cookie === Cookies.get('wt_consent')) {
1935                                    if (getCookieYes('analytics') === 'yes') {
1936                                        Utils.manageCookies();
1937                                    }
1938                                } else if (cli_cookie === Cookies.get('viewed_cookie_policy') && cli_cookie == 'yes') {
1939                                    Utils.manageCookies();
1940                                }
1941                            }
1942                        },1000)
1943                        Facebook.loadPixel();
1944                        Analytics.loadPixel();
1945                        Pinterest.loadPixel();
1946                        Bing.loadPixel();
1947
1948                        let consent = {
1949                            facebook: true,
1950                            ga: true,
1951                            bing: true,
1952                            pinterest: true,
1953                            gtm: true,
1954                        };
1955                        Utils.setupGDPRData( consent );
1956                    });
1957
1958                    $(document).onFirst('click', '#cookie_action_close_header_reject, .cky-btn-reject', function () {
1959                        Facebook.disable();
1960                        Analytics.disable();
1961                        Pinterest.disable();
1962                        Bing.disable();
1963
1964                        let consent = {
1965                            facebook: false,
1966                            ga: false,
1967                            bing: false,
1968                            pinterest: false,
1969                            gtm: false,
1970                        };
1971                        Utils.setupGDPRData( consent );
1972                    });
1973
1974                }
1975
1976            },
1977
1978            setupGDPRData: function ( consent ) {
1979                consent = window.btoa( JSON.stringify( consent ) );
1980                Cookies.set( 'pys_consent', consent, { expires: 365, path: '/', domain: domain } );
1981            },
1982
1983            /**
1984             * DOWNLOAD DOCS
1985             */
1986
1987            getLinkExtension: function (link) {
1988
1989                // Remove anchor, query string and everything before last slash
1990                link = link.substring(0, (link.indexOf("#") === -1) ? link.length : link.indexOf("#"));
1991                link = link.substring(0, (link.indexOf("?") === -1) ? link.length : link.indexOf("?"));
1992                link = link.substring(link.lastIndexOf("/") + 1, link.length);
1993
1994                // If there's a period left in the URL, then there's a extension
1995                if (link.length > 0 && link.indexOf('.') !== -1) {
1996                    link = link.substring(link.indexOf(".") + 1); // Remove everything but what's after the first period
1997                    return link;
1998                } else {
1999                    return "";
2000                }
2001            },
2002
2003            getLinkFilename: function (link) {
2004
2005                // Remove anchor, query string and everything before last slash
2006                link = link.substring(0, (link.indexOf("#") === -1) ? link.length : link.indexOf("#"));
2007                link = link.substring(0, (link.indexOf("?") === -1) ? link.length : link.indexOf("?"));
2008                link = link.substring(link.lastIndexOf("/") + 1, link.length);
2009
2010                // If there's a period left in the URL, then there's a extension
2011                if (link.length > 0 && link.indexOf('.') !== -1) {
2012                    return link;
2013                } else {
2014                    return "";
2015                }
2016            },
2017            /**
2018             * Enrich
2019             */
2020            isCheckoutPage: function () {
2021                return $('body').hasClass('woocommerce-checkout') || document.querySelector('.woocommerce-checkout') ||
2022                    $('body').hasClass('edd-checkout');
2023            },
2024            addCheckoutFields : function() {
2025                var utm = "";
2026                var utms = getUTMs()
2027
2028                $.each(utmTerms, function (index, name) {
2029                    if(index > 0) {
2030                        utm+="|";
2031                    }
2032                    utm+=name+":"+utms[name];
2033                });
2034                var utmIdList = "";
2035                var utmsIds = getUTMId()
2036                $.each(utmId, function (index, name) {
2037                    if(index > 0) {
2038                        utmIdList+="|";
2039                    }
2040                    utmIdList+=name+":"+utmsIds[name];
2041                });
2042                var utmIdListLast = "";
2043                var utmsIdsLast = getUTMId(true)
2044                $.each(utmId, function (index, name) {
2045                    if(index > 0) {
2046                        utmIdListLast+="|";
2047                    }
2048                    utmIdListLast+=name+":"+utmsIdsLast[name];
2049                });
2050
2051
2052                var utmLast = "";
2053                var utmsLast = getUTMs(true)
2054                $.each(utmTerms, function (index, name) {
2055                    if(index > 0) {
2056                        utmLast+="|";
2057                    }
2058                    utmLast+=name+":"+utmsLast[name];
2059                });
2060
2061                var dateTime = getDateTime();
2062                var landing = getLandingPageValue();
2063                var lastLanding = getLandingPageValue();
2064                var trafic = getTrafficSourceValue();
2065                var lastTrafic = getTrafficSourceValue();
2066
2067                var $form = null;
2068                if($('body').hasClass('woocommerce-checkout')) {
2069                    $form = $("form.woocommerce-checkout");
2070                } else {
2071                    $form = $("#edd_purchase_form");
2072                }
2073                var inputs = {'pys_utm':utm,
2074                    'pys_utm_id':utmIdList,
2075                    'pys_browser_time':dateTime.join("|"),
2076                    'pys_landing':landing,
2077                    'pys_source':trafic,
2078                    'pys_order_type': $(".wcf-optin-form").length > 0 ? "wcf-optin" : "normal",
2079
2080                    'last_pys_landing':lastLanding,
2081                    'last_pys_source':lastTrafic,
2082                    'last_pys_utm':utmLast,
2083                    'last_pys_utm_id':utmIdListLast,
2084                }
2085
2086                Object.keys(inputs).forEach(function(key,index) {
2087                    $form.append("<input type='hidden' name='"+key+"' value='"+inputs[key]+"' /> ");
2088                });
2089
2090
2091            },
2092            getAdvancedFormData: function () {
2093                let dataStr = Cookies.get("pys_advanced_form_data");
2094                if(dataStr === undefined) {
2095                    return {'first_name':"",'last_name':"",'email':"",'phone':""};
2096                } else {
2097                    return JSON.parse(dataStr);
2098                }
2099            },
2100            getFormFilledData: function ( event ) {
2101                // First, resolve static/dynamic parameters
2102                if (event.params && Object.keys(event.params).length > 0) {
2103                    Object.entries(event.params).forEach(([key, value]) => {
2104                        // Resolve parameter value (static or dynamic)
2105                        const resolvedValue = resolveParamValue(value, key);
2106                        if (resolvedValue !== null) {
2107                            event.params[key] = resolvedValue;
2108                        }
2109                        else {
2110                            delete event.params[key];
2111                        }
2112                    });
2113                }
2114                return event;
2115            }
2116        };
2117
2118    }(options);
2119
2120    var Facebook = function (options) {
2121
2122
2123        var defaultEventTypes = [
2124            'PageView',
2125            'ViewContent',
2126            'Search',
2127            'AddToCart',
2128            'AddToWishlist',
2129            'InitiateCheckout',
2130            'AddPaymentInfo',
2131            'Purchase',
2132            'Lead',
2133
2134            'Subscribe',
2135            'CustomizeProduct',
2136            'FindLocation',
2137            'StartTrial',
2138            'SubmitApplication',
2139            'Schedule',
2140            'Contact',
2141            'Donate'
2142        ];
2143
2144        var initialized = false;
2145        var genereateFbp = function (){
2146            return !Cookies.get('_fbp') ? 'fb.1.'+Date.now()+'.'+Math.floor(1000000000 + Math.random() * 9000000000) : Cookies.get('_fbp');
2147        };
2148        var genereateFbc = function (){
2149            return getUrlParameter('fbclid') ? 'fb.1.'+Date.now()+'.'+getUrlParameter('fbclid') : ''
2150        };
2151        // fire server side event gdpr plugin installed
2152        var isApiDisabled = options.gdpr.all_disabled_by_api ||
2153            options.gdpr.facebook_disabled_by_api ||
2154            options.gdpr.cookiebot_integration_enabled ||
2155            options.gdpr.consent_magic_integration_enabled ||
2156            options.gdpr.cookie_notice_integration_enabled ||
2157            options.gdpr.cookie_law_info_integration_enabled;
2158
2159        /**
2160         *
2161         * @param allData
2162         * @param params
2163         * @returns {string | null}
2164         */
2165        function sendFbServerEvent(allData,name,params) {
2166            let eventId = null;
2167            if(options.facebook.serverApiEnabled) {
2168
2169                if(allData.e_id === "woo_remove_from_cart") {// server event will sended from hook
2170                    Facebook.updateEventId(allData.name);
2171                    allData.eventID = Facebook.getEventId(allData.name);
2172                } else {
2173                    // send event from server if they was bloc by gdpr or need send with delay
2174                    allData.eventID = Utils.generateUniqueId(allData);
2175
2176                    if(Cookies.get('_fbp')){
2177                        params._fbp = Cookies.get('_fbp');
2178                    }
2179                    if(Cookies.get('_fbc')){
2180                        params._fbc = Cookies.get('_fbc');
2181                    }
2182
2183                    if( options.ajaxForServerEvent || isApiDisabled ){
2184                        var json = {
2185                            action: 'pys_api_event',
2186                            pixel: 'facebook',
2187                            event: name,
2188                            event_slug: allData.e_id,
2189                            data:params,
2190                            ids:options.facebook.pixelIds,
2191                            eventID:allData.eventID,
2192                            url:window.location.href,
2193                            ajax_event:options.ajax_event
2194                        };
2195                        if(allData.hasOwnProperty('woo_order')) {
2196                            json['woo_order'] = allData.woo_order;
2197                        }
2198                        if(allData.hasOwnProperty('edd_order')) {
2199                            json['edd_order'] = allData.edd_order;
2200                        }
2201                        if(allData.hasOwnProperty('order_key') && allData.order_key) {
2202                            json['order_key'] = allData.order_key;
2203                        }
2204                        if (pageReferrer) {
2205                            json.referrer_url = pageReferrer;
2206                        }
2207
2208                        if (allData.e_id === "automatic_event_internal_link" || allData.e_id === "automatic_event_outbound_link") {
2209                            setTimeout(() => Utils.sendServerAjaxRequest(options.ajaxUrl, json), 500);
2210                        } else if (allData.type != 'static') {
2211                            Utils.sendServerAjaxRequest(options.ajaxUrl, json);
2212                        }
2213
2214                        if ( ( allData.type == 'static' && options.ajaxForServerStaticEvent ) || ( allData.hasOwnProperty( 'ajaxFire' ) && allData.ajaxFire ) ) {
2215                            Utils.sendServerAjaxRequest( options.ajaxUrl, json );
2216                        }
2217                    }
2218                }
2219                delete params._fbp;
2220                delete params._fbc;
2221                eventId = allData.eventID
2222            }
2223            return eventId;
2224        }
2225
2226        function fireEvent(name, allData) {
2227
2228            if(typeof window.pys_event_data_filter === "function" && window.pys_disable_event_filter(name,'facebook')) {
2229                return;
2230            }
2231
2232            var actionType = defaultEventTypes.includes(name) ? 'track' : 'trackCustom';
2233            var data = allData.params;
2234            var params = {};
2235            var arg = {};
2236            Utils.copyProperties(data, params);
2237            let eventId = sendFbServerEvent(allData,name,params)
2238
2239
2240            if("hCR" === name) {
2241                return;
2242            }
2243
2244            if (options.debug) {
2245                console.log('[Facebook] ' + name, params,"eventID",eventId);
2246            }
2247
2248            if(eventId != null) {
2249                arg.eventID = eventId;
2250            }
2251
2252            fbq(actionType, name, params, arg);
2253
2254        }
2255
2256        /**
2257         * Public API
2258         */
2259        return {
2260            tag: function() {
2261                return "facebook";
2262            },
2263            isEnabled: function () {
2264                return options.hasOwnProperty('facebook');
2265            },
2266
2267            disable: function () {
2268                initialized = false;
2269            },
2270            advancedMatching: function () {
2271                if(options.facebook.advancedMatchingEnabled) {
2272                    let advancedMatchingForm = Utils.getAdvancedFormData();
2273                    let advancedMatching = {};
2274                    if(Object.keys(options.facebook.advancedMatching).length > 0) {
2275                        advancedMatching = options.facebook.advancedMatching;
2276                    }
2277
2278                    if(!advancedMatching.hasOwnProperty("em")
2279                        && advancedMatchingForm.hasOwnProperty("email") && advancedMatchingForm["email"].length > 0) {
2280                        advancedMatching["em"] = advancedMatchingForm["email"];
2281                    }
2282                    if(!advancedMatching.hasOwnProperty("ph")
2283                        && advancedMatchingForm.hasOwnProperty("phone") && advancedMatchingForm["phone"].length > 0) {
2284                        advancedMatching["ph"] = advancedMatchingForm["phone"];
2285                    }
2286                    if(!advancedMatching.hasOwnProperty("fn")
2287                        && advancedMatchingForm.hasOwnProperty("first_name") && advancedMatchingForm["first_name"].length > 0) {
2288                        advancedMatching["fn"] = advancedMatchingForm["first_name"];
2289                    }
2290                    if(!advancedMatching.hasOwnProperty("ln")
2291                        && advancedMatchingForm.hasOwnProperty("last_name") && advancedMatchingForm["last_name"].length > 0) {
2292                        advancedMatching["ln"] = advancedMatchingForm["last_name"];
2293                    }
2294                    if(!advancedMatching.hasOwnProperty("external_id")){
2295                        if (Cookies.get('pbid') || (options.hasOwnProperty('pbid') && options.pbid)) {
2296                            advancedMatching["external_id"] = Cookies.get('pbid') ? Cookies.get('pbid') : options.pbid;
2297                        }
2298                    }
2299                    else if(advancedMatching.external_id != Cookies.get('pbid'))
2300                    {
2301                        advancedMatching["external_id"] = Cookies.get('pbid') ? Cookies.get('pbid') : advancedMatching.external_id;
2302                    }
2303                    if(Object.keys(advancedMatching).length > 0) {
2304                        return advancedMatching;
2305                    }
2306                }
2307                return false
2308            },
2309            /**
2310             * Load pixel's JS
2311             */
2312            loadPixel: function () {
2313
2314                if (initialized || !this.isEnabled() || !Utils.consentGiven('facebook')) {
2315                    return;
2316                }
2317
2318                !function (f, b, e, v, n, t, s) {
2319                    if (f.fbq) return;
2320                    n = f.fbq = function () {
2321                        n.callMethod ?
2322                            n.callMethod.apply(n, arguments) : n.queue.push(arguments)
2323                    };
2324                    if (!f._fbq) f._fbq = n;
2325                    n.push = n;
2326                    n.loaded = !0;
2327                    n.version = '2.0';
2328                    n.agent = 'dvpixelyoursite';
2329                    n.queue = [];
2330                    t = b.createElement(e);
2331                    t.async = !0;
2332                    t.src = v;
2333                    s = b.getElementsByTagName(e)[0];
2334                    s.parentNode.insertBefore(t, s)
2335                }(window,
2336                    document, 'script', 'https://connect.facebook.net/en_US/fbevents.js');
2337
2338                let expires = parseInt(options.cookie_duration);
2339                if(!Cookies.get('_fbp')) {
2340                    Cookies.set('_fbp',genereateFbp(),  { expires: expires,path: '/',domain: domain });
2341                }
2342
2343                if(getUrlParameter('fbclid')) {
2344                    Cookies.set('_fbc',genereateFbc(),  { expires: expires,path: '/',domain: domain });
2345                }
2346
2347                // Now that Facebook consent is confirmed, mirror the session
2348                // entry referrer (if any) into the session cookie so PHP can
2349                // use it as a fallback for orphan server-only events.
2350                getOrInitSessionEntryReferrer(true);
2351
2352                // Refresh the per-page event referrer cookie so checkout
2353                // hooks can snapshot the buyer's prior page.
2354                writeEventReferrerCookie();
2355
2356                // initialize pixel
2357                options.facebook.pixelIds.forEach(function (pixelId) {
2358                    if (options.facebook.removeMetadata) {
2359                        fbq('set', 'autoConfig', false, pixelId);
2360                    }
2361                    let advancedMatching = Facebook.advancedMatching();
2362
2363                    if ( +options.facebook.meta_ldu === 1  ) {
2364                        fbq( 'dataProcessingOptions', [ 'LDU' ], 0, 0 );
2365                    }
2366
2367                    if (options.gdpr.consent_magic_integration_enabled && typeof CS_Data !== "undefined") {
2368                        if(!advancedMatching) {
2369                            fbq('init', pixelId);
2370                        } else {
2371                            var test_prefix = CS_Data.test_prefix;
2372                            var cs_advanced_matching = Cookies.get('cs_enabled_advanced_matching'+test_prefix);
2373                            if (jQuery('#cs_enabled_advanced_matching'+test_prefix).length > 0) {
2374                                if (cs_advanced_matching == 'yes') {
2375                                    fbq('init', pixelId, advancedMatching);
2376                                } else {
2377                                    fbq('init', pixelId);
2378                                }
2379                            } else {
2380                                fbq('init', pixelId, advancedMatching);
2381                            }
2382                        }
2383                    } else {
2384                        if(!advancedMatching) {
2385                            fbq('init', pixelId);
2386                        }  else {
2387                            fbq('init', pixelId, advancedMatching);
2388                        }
2389                    }
2390                });
2391
2392                initialized = true;
2393
2394                Utils.fireStaticEvents('facebook');
2395
2396            },
2397
2398            fireEvent: function (name, data) {
2399
2400                if (!initialized || !this.isEnabled()) {
2401                    return false;
2402                }
2403
2404                data.delay = data.delay || 0;
2405                data.params = data.params || {};
2406
2407                if (data.delay === 0) {
2408
2409                    fireEvent(name, data);
2410
2411                } else {
2412
2413                    setTimeout(function (name, params) {
2414                        fireEvent(name, params);
2415                    }, data.delay * 1000, name, data);
2416
2417                }
2418
2419                return true;
2420
2421            },
2422
2423            onCommentEvent: function (event) {
2424                this.fireEvent(event.name, event);
2425            },
2426
2427            onDownloadEvent: function (event) {
2428                this.fireEvent(event.name, event);
2429            },
2430
2431            onFormEvent: function (event) {
2432
2433                this.fireEvent(event.name, event);
2434
2435            },
2436
2437            onWooAddToCartOnButtonEvent: function (product_id) {
2438
2439                if(!options.dynamicEvents.woo_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
2440                    return;
2441                var event = options.dynamicEvents.woo_add_to_cart_on_button_click[this.tag()];
2442                window.pysWooProductData = window.pysWooProductData || [];
2443                if (window.pysWooProductData.hasOwnProperty(product_id)) {
2444                    if (window.pysWooProductData[product_id].hasOwnProperty('facebook')) {
2445                        event = Utils.copyProperties(event, {})
2446                        Utils.copyProperties(window.pysWooProductData[product_id]['facebook'].params, event.params)
2447                        this.fireEvent(event.name, event);
2448                    }
2449                }
2450            },
2451
2452            onWooAddToCartOnSingleEvent: function (product_id, qty, product_type, $form) {
2453
2454                window.pysWooProductData = window.pysWooProductData || [];
2455                if(!options.dynamicEvents.woo_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
2456                    return;
2457                var event = Utils.clone(options.dynamicEvents.woo_add_to_cart_on_button_click[this.tag()]);
2458
2459                if (product_type === Utils.PRODUCT_VARIABLE && !options.facebook.wooVariableAsSimple) {
2460                    product_id = parseInt($form.find('input[name="variation_id"]').val());
2461                }
2462
2463                if (window.pysWooProductData.hasOwnProperty(product_id)) {
2464                    if (window.pysWooProductData[product_id].hasOwnProperty('facebook')) {
2465
2466
2467                        Utils.copyProperties(window.pysWooProductData[product_id]['facebook'].params, event.params);
2468
2469                        var groupValue = 0;
2470                        if(product_type === Utils.PRODUCT_GROUPED ) {
2471                            $form.find(".woocommerce-grouped-product-list .qty").each(function(index){
2472                                var childId = $(this).attr('name').replaceAll("quantity[","").replaceAll("]","");
2473                                var quantity = parseInt($(this).val());
2474                                if(isNaN(quantity)) {
2475                                    quantity = 0;
2476                                }
2477                                var childItem = window.pysWooProductData[product_id]['facebook'].grouped[childId];
2478
2479                                if(quantity == 0) {
2480                                    event.params.content_ids.forEach(function(el,index,array) {
2481                                        if(el == childItem.content_id) {
2482                                            array.splice(index, 1);
2483                                        }
2484                                    });
2485                                }
2486
2487                                if(event.params.hasOwnProperty('contents')) {
2488                                    event.params.contents.forEach(function(el,index,array) {
2489                                        if(el.id == childItem.content_id) {
2490                                            if(quantity > 0){
2491                                                el.quantity = quantity;
2492                                            } else {
2493                                                array.splice(index, 1);
2494                                            }
2495                                        }
2496                                    });
2497                                }
2498
2499
2500                                groupValue += childItem.price * quantity;
2501                            });
2502                            if(groupValue == 0) return;
2502 // skip if no items selected
2503                        }
2504
2505                        // maybe customize value option
2506                        if (options.woo.addToCartOnButtonValueEnabled && options.woo.addToCartOnButtonValueOption !== 'global') {
2507
2508                            if(product_type === Utils.PRODUCT_GROUPED) {
2509                                event.params.value = groupValue;
2510                            } else if(product_type === Utils.PRODUCT_BUNDLE) {
2511                                var data = $(".bundle_form .bundle_data").data("bundle_form_data");
2512                                var items_sum = getBundlePriceOnSingleProduct(data);
2513                                event.params.value = (parseInt(data.base_price) + items_sum )* qty;
2514                            } else {
2515                                event.params.value = event.params.value * qty;
2516                            }
2517                        }
2518
2519                        // only when non Facebook for WooCommerce logic used
2520                        if (event.params.hasOwnProperty('contents') && product_type !== Utils.PRODUCT_GROUPED) {
2521                            event.params.contents[0].quantity = qty;
2522                        }
2523
2524                        this.fireEvent(event.name, event);
2525
2526                    }
2527                }
2528            },
2529
2530            onWooRemoveFromCartEvent: function (event) {
2531                this.fireEvent(event.name, event);
2532            },
2533
2534            onEddAddToCartOnButtonEvent: function (download_id, price_index, qty) {
2535
2536                if(!options.dynamicEvents.edd_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
2537                    return;
2538                var event = Utils.clone(options.dynamicEvents.edd_add_to_cart_on_button_click[this.tag()]);
2539
2540                if (window.pysEddProductData && window.pysEddProductData.hasOwnProperty(download_id)) {
2541
2542                    var index;
2543
2544                    if (price_index) {
2545                        index = download_id + '_' + price_index;
2546                    } else {
2547                        index = download_id;
2548                    }
2549
2550                    if (window.pysEddProductData[download_id].hasOwnProperty(index)) {
2551                        if (window.pysEddProductData[download_id][index].hasOwnProperty('facebook')) {
2552
2553
2554                            Utils.copyProperties(window.pysEddProductData[download_id][index]['facebook']["params"], event.params)
2555
2556                            // maybe customize value option
2557                            if (options.edd.addToCartOnButtonValueEnabled && options.edd.addToCartOnButtonValueOption !== 'global') {
2558                                event.params.value = event.params.value * qty;
2559                            }
2560
2561                            // update contents qty param
2562                            var contents = event.params.contents;
2563                            contents[0].quantity = qty;
2564                            event.params.contents = contents;
2565
2566                            this.fireEvent(event.name,event);
2567
2568                        }
2569                    }
2570
2571                }
2572
2573            },
2574
2575            onEddRemoveFromCartEvent: function (event) {
2576                this.fireEvent(event.name, event);
2577            },
2578            onPageScroll: function (event) {
2579                this.fireEvent(event.name, event);
2580            },
2581            onTime: function (event) {
2582                this.fireEvent(event.name, event);
2583            },
2584            initEventIdCookies: function (key) {
2585                var ids = {};
2586                ids[key] = pys_generate_token(36)
2587                Cookies.set('pys_fb_event_id', JSON.stringify(ids));
2588            },
2589            updateEventId:function(key) {
2590                var cooData = Cookies.get("pys_fb_event_id")
2591                if(cooData === undefined) {
2592                    this.initEventIdCookies(key);
2593                } else {
2594                    var data = JSON.parse(cooData);
2595                    data[key] = pys_generate_token(36);
2596                    Cookies.set('pys_fb_event_id', JSON.stringify(data) );
2597                }
2598            },
2599
2600            getEventId:function (key) {
2601                var data = Cookies.get("pys_fb_event_id");
2602                if(data === undefined) {
2603                    this.initEventIdCookies(key);
2604                    data = Cookies.get("pys_fb_event_id");
2605                }
2606                return JSON.parse(data)[key];
2607            },
2608
2609            sendFacebookRestAPIRequest: function(allData, name, params) {
2610                if (typeof window.pysFacebookRest === 'undefined' || !window.pysFacebookRest.restApiUrl) {
2611                    return;
2612                }
2613
2614                const restApiData = {
2615                    event: name,
2616                    data: JSON.stringify(params),
2617                    ids: allData.pixelIds || options.facebook.pixelIds,
2618                    eventID: allData.eventID,
2619                    woo_order: allData.woo_order || 0,
2620                    edd_order: allData.edd_order || 0,
2621                    order_key: allData.order_key || ''
2622                };
2623
2624                if (pageReferrer) {
2625                    restApiData.referrer_url = pageReferrer;
2626                }
2627
2628                // Variant A: send the actual page URL where the event happened
2629                // so the server sets event_source_url to this page, not the
2630                // REST endpoint. PHP applies enable_remove_source_url_params.
2631                restApiData.event_source_url = window.location.href;
2632
2633                // Try sendBeacon first
2634                if (navigator.sendBeacon) {
2635                    const formData = new FormData();
2636                    Object.keys(restApiData).forEach(key => {
2637                        if (Array.isArray(restApiData[key])) {
2638                            restApiData[key].forEach(value => {
2639                                formData.append(key + '[]', value);
2640                            });
2641                        } else {
2642                            formData.append(key, restApiData[key]);
2643                        }
2644                    });
2645
2646                    if (navigator.sendBeacon(window.pysFacebookRest.restApiUrl, formData)) {
2647                        return;
2648                    }
2649                }
2650
2651                // Fallback to fetch
2652                fetch(window.pysFacebookRest.restApiUrl, {
2653                    method: 'POST',
2654                    headers: {
2655                        'Content-Type': 'application/x-www-form-urlencoded',
2656                    },
2657                    body: new URLSearchParams(restApiData)
2658                }).catch(() => {
2659                    // Fallback to AJAX if fetch fails
2660                    this.sendAjaxFallback(allData, name, params);
2661                });
2662            },
2663
2664            sendAjaxFallback: function(allData, name, params) {
2665                var json = {
2666                    action: 'pys_api_event',
2667                    pixel: 'facebook',
2668                    event: name,
2669                    data: params,
2670                    ids: allData.pixelIds || options.facebook.pixelIds,
2671                    eventID: allData.eventID,
2672                    url: window.location.href,
2673                    ajax_event: options.ajax_event
2674                };
2675                if (allData.hasOwnProperty('woo_order')) {
2676                    json['woo_order'] = allData.woo_order;
2677                }
2678                if (allData.hasOwnProperty('edd_order')) {
2679                    json['edd_order'] = allData.edd_order;
2680                }
2681                if (allData.hasOwnProperty('order_key') && allData.order_key) {
2682                    json['order_key'] = allData.order_key;
2683                }
2684                if (pageReferrer) {
2685                    json.referrer_url = pageReferrer;
2686                }
2687
2688                Utils.sendServerAjaxRequest(options.ajaxUrl, json);
2689            },
2690        };
2691
2692    }(options);
2693
2694    var Analytics = function (options) {
2695
2696        var initialized = false;
2697
2698        /**
2699         * Fires event
2700         *
2701         * @link: https://developers.google.com/analytics/devguides/collection/gtagjs/sending-data
2702         * @link: https://developers.google.com/analytics/devguides/collection/gtagjs/events
2703         * @link: https://developers.google.com/gtagjs/reference/event
2704         * @link: https://developers.google.com/gtagjs/reference/parameter
2705         *
2706         * @link: https://developers.google.com/analytics/devguides/collection/gtagjs/custom-dim
2706s-mets
2707         *
2708         * @param name
2709         * @param data
2710         */
2711        function fireEvent(name, data) {
2712
2713            if(typeof window.pys_event_data_filter === "function" && window.pys_disable_event_filter(name,'ga')) {
2714                return;
2715            }
2716
2717            var eventParams = Utils.copyProperties(data, {});
2718            var _fireEvent = function (tracking_id,name,params) {
2719
2720                // Firing without a destination would park the hit inside gtag.js
2721                // instead of sending it, which Tag Assistant reports as a missing
2722                // config command. Skip, but keep the reason visible in debug mode.
2723                if (!Utils.hasSendToTarget(tracking_id)) {
2724                    if (options.debug) {
2725                        console.warn('[Google Analytics] ' + name + ' skipped: empty tracking ID', {
2726                            trackingIds: options.ga.trackingIds
2727                        });
2728                    }
2729                    return;
2730                }
2731
2732                params['send_to'] = tracking_id;
2733
2734                if (options.debug) {
2735                    console.log('[Google Analytics #' + tracking_id + '] ' + name, params);
2736                }
2737
2738                Utils.gtagPush('event', name, params);
2739
2740            };
2741            options.ga.trackingIds.forEach(function (tracking_id) {
2742                var copyParams = Utils.copyProperties(eventParams, {}); // copy params because mapParamsTov4 can modify it
2743                var params = mapParamsTov4(tracking_id,name,copyParams)
2744                _fireEvent(tracking_id,name,params);
2745            });
2746
2747        }
2748        function mapParamsTov4(tag,name,param) {
2749            //GA4 automatically collects a number of parameters for all events
2750            delete param.landing_page;
2751            // end
2752            if(isv4(tag)) {
2753                delete param.traffic_source;
2754                delete param.event_category;
2755                delete param.event_label;
2756                delete param.ecomm_prodid;
2757                delete param.ecomm_pagetype;
2758                delete param.ecomm_totalvalue;
2759                if(name === 'search') {
2760                    param['search'] = param.search_term;
2761                    delete param.search_term;
2762                    delete param.dynx_itemid;
2763                    delete param.dynx_pagetype;
2764                    delete param.dynx_totalvalue;
2765                }
2766            }
2767            return param;
2768        }
2769
2770        function isv4(tag) {
2771            return tag.indexOf('G') === 0;
2772        }
2773
2774        /**
2775         * Public API
2776         */
2777        return {
2778            tag: function() {
2779                return "ga";
2780            },
2781            isEnabled: function () {
2782                return options.hasOwnProperty('ga');
2783            },
2784
2785            disable: function () {
2786                initialized = false;
2787            },
2788
2789            loadPixel: function () {
2790
2791                if (initialized || !this.isEnabled() || !Utils.consentGiven('analytics')) {
2792                    return;
2793                }
2794
2795                Utils.loadGoogleTag(options.ga.trackingIds[0]);
2796                var config = {};
2797                // Cross-Domain tracking
2798                if (options.ga.crossDomainEnabled) {
2799                    config.linker = {
2800                        accept_incoming: options.ga.crossDomainAcceptIncoming,
2801                        domains: options.ga.crossDomainDomains
2802                    };
2803                }
2804                var ids = options.ga.trackingIds;
2805                // configure tracking ids
2806                ids.forEach(function (trackingId,index) {
2807                    var obj = options.ga.isDebugEnabled;
2808                    var searchValue = "index_"+index;
2809                    var config_for_tag = Object.assign({}, options.config);
2810                    config_for_tag.debug_mode = false;
2811                    config_for_tag.send_page_view = !options.ga.custom_page_view_event;
2812                    for (var key in obj) {
2813                        if (obj.hasOwnProperty(key) && obj[key] === searchValue) {
2814                            config_for_tag.debug_mode = true;
2815                            break;
2816                        }
2817                    }
2818                    if(!config_for_tag.debug_mode)
2819                    {
2820                        delete config_for_tag.debug_mode;
2821                    }
2822                    if(isv4(trackingId)) {
2823                        if(options.ga.disableAdvertisingFeatures) {
2824                            config_for_tag.allow_google_signals = false
2825                        }
2826                        if(options.ga.disableAdvertisingPersonalization) {
2827                            config_for_tag.allow_ad_personalization_signals = false
2828                        }
2829                    }
2830                    if(options.ga.hasOwnProperty('additionalConfig')){
2831                        if(options.ga.additionalConfig.hasOwnProperty(trackingId) && options.ga.additionalConfig[trackingId]){
2832                            config_for_tag.first_party_collection = options.ga.additionalConfig[trackingId].first_party_collection;
2833                        }
2834
2835                    }
2836                    if(options.ga.hasOwnProperty('serverContainerUrls')){
2837                        if(options.ga.serverContainerUrls.hasOwnProperty(trackingId) && options.ga.serverContainerUrls[trackingId].enable_server_container != false){
2838                            if(options.ga.serverContainerUrls[trackingId].server_container_url != ''){
2839                                config_for_tag.server_container_url = options.ga.serverContainerUrls[trackingId].server_container_url;
2840                            }
2841                            if(options.ga.serverContainerUrls[trackingId].transport_url != ''){
2842                                config_for_tag.transport_url = options.ga.serverContainerUrls[trackingId].transport_url;
2843                            }
2844                        }
2845                    }
2846                    if (options.gdpr.cookiebot_integration_enabled && typeof Cookiebot !== 'undefined') {
2847
2848                        var cookiebot_consent_category = options.gdpr['cookiebot_analytics_consent_category'];
2849                        if (options.gdpr['analytics_prior_consent_enabled']) {
2850                            if (Cookiebot.consented === true && Cookiebot.consent[cookiebot_consent_category]) {
2851                                Utils.gtagPush('config', trackingId, config_for_tag);
2852                            }
2853                        } else {
2854                            if (Cookiebot.consent[cookiebot_consent_category]) {
2855                                Utils.gtagPush('config', trackingId, config_for_tag);
2856                            }
2857                        }
2858                    }
2859                    else
2860                    {
2861                        Utils.gtagPush('config', trackingId, config_for_tag);
2862                    }
2863                });
2864
2865                initialized = true;
2866
2867                Utils.fireStaticEvents('ga');
2868
2869            },
2870
2871            fireEvent: function (name, data) {
2872
2873                if (!initialized || !this.isEnabled()) {
2874                    return false;
2875                }
2876
2877                data.delay = data.delay || 0;
2878                data.params = data.params || {};
2879                data.params.eventID = Utils.generateUniqueId(data);
2880                if (data.delay === 0) {
2881
2882                    fireEvent(name, data.params);
2883
2884                } else {
2885
2886                    setTimeout(function (name, params) {
2887                        fireEvent(name, params);
2888                    }, data.delay * 1000, name, data.params);
2889
2890                }
2891
2892                return true;
2893
2894            },
2895
2896            onCommentEvent: function (event) {
2897                this.fireEvent(event.name, event);
2898            },
2899
2900            onDownloadEvent: function (event) {
2901                this.fireEvent(event.name, event);
2902            },
2903
2904            onFormEvent: function (event) {
2905                this.fireEvent(event.name, event);
2906            },
2907
2908            onWooAddToCartOnButtonEvent: function (product_id) {
2909
2910                if(!options.dynamicEvents.woo_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
2911                    return;
2912                var event = Utils.clone(options.dynamicEvents.woo_add_to_cart_on_button_click[this.tag()]);
2913
2914                if (window.pysWooProductData.hasOwnProperty(product_id)) {
2915                    if (window.pysWooProductData[product_id].hasOwnProperty('ga')) {
2916                        Utils.copyProperties(window.pysWooProductData[product_id]['ga'].params, event.params)
2917                        this.fireEvent(event.name, event);
2918                    }
2919                }
2920
2921
2922            },
2923
2924            onWooAddToCartOnSingleEvent: function (product_id, qty, product_type, $form) {
2925
2926                window.pysWooProductData = window.pysWooProductData || [];
2927
2928                if(!options.dynamicEvents.woo_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
2929                    return;
2930                var event = Utils.clone(options.dynamicEvents.woo_add_to_cart_on_button_click[this.tag()]);
2931
2932                if (product_type === Utils.PRODUCT_VARIABLE && !options.ga.wooVariableAsSimple) {
2933                    product_id = parseInt($form.find('input[name="variation_id"]').val());
2934                }
2935
2936                if (window.pysWooProductData.hasOwnProperty(product_id)) {
2937                    if (window.pysWooProductData[product_id].hasOwnProperty('ga')) {
2938
2939                        Utils.copyProperties(window.pysWooProductData[product_id]['ga'].params, event.params);
2940                        if(product_type === Utils.PRODUCT_GROUPED ) {
2941                            var groupValue = 0;
2942                            $form.find(".woocommerce-grouped-product-list .qty").each(function(index){
2943                                var childId = $(this).attr('name').replaceAll("quantity[","").replaceAll("]","");
2944                                var quantity = parseInt($(this).val());
2945                                if(isNaN(quantity)) {
2946                                    quantity = 0;
2947                                }
2948                                var childItem = window.pysWooProductData[product_id]['ga'].grouped[childId];
2949                                // update quantity
2950                                event.params.items.forEach(function(el,index,array) {
2951                                    if(el.id == childItem.content_id) {
2952                                        if(quantity > 0){
2953                                            el.quantity = quantity;
2954                                        } else {
2955                                            array.splice(index, 1);
2956                                        }
2957                                    }
2958                                });
2959                                groupValue += childItem.price * quantity;
2960                            });
2961                            if(options.woo.addToCartOnButtonValueEnabled &&
2962                                options.woo.addToCartOnButtonValueOption !== 'global' &&
2963                                event.params.hasOwnProperty('ecomm_totalvalue')) {
2964                                event.params.ecomm_totalvalue = groupValue;
2965                            }
2966
2967                            if(groupValue == 0) return; // skip if no items selected
2968                        } else {
2969                            // update items qty param
2970                            event.params.items[0].quantity = qty;
2971                        }
2972
2973                        // maybe customize value option
2974                        if (options.woo.addToCartOnButtonValueEnabled &&
2975                            options.woo.addToCartOnButtonValueOption !== 'global' &&
2976                            product_type !== Utils.PRODUCT_GROUPED)
2977                        {
2978                            if(event.params.hasOwnProperty('ecomm_totalvalue')) {
2979                                event.params.ecomm_totalvalue = event.params.items[0].price * qty;
2980                            }
2981
2982                        }
2983
2984
2985                        this.fireEvent(event.name, event);
2986                    }
2987                }
2988
2989            },
2990
2991            onWooRemoveFromCartEvent: function (event) {
2992
2993                this.fireEvent(event.name, event);
2994
2995            },
2996
2997            onEddAddToCartOnButtonEvent: function (download_id, price_index, qty) {
2998
2999                if(!options.dynamicEvents.edd_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
3000                    return;
3001                var event = Utils.clone(options.dynamicEvents.edd_add_to_cart_on_button_click[this.tag()]);
3002
3003
3004                if (window.pysEddProductData && window.pysEddProductData.hasOwnProperty(download_id)) {
3005
3006                    var index;
3007
3008                    if (price_index) {
3009                        index = download_id + '_' + price_index;
3010                    } else {
3011                        index = download_id;
3012                    }
3013
3014                    if (window.pysEddProductData[download_id].hasOwnProperty(index)) {
3015                        if (window.pysEddProductData[download_id][index].hasOwnProperty('ga')) {
3016
3017                            Utils.copyProperties(window.pysEddProductData[download_id][index]['ga'].params, event.params);
3018
3019                            // update items qty param
3020                            event.params.items[0].quantity = qty;
3021
3022                            this.fireEvent(event.name,event);
3023
3024                        }
3025                    }
3026
3027                }
3028
3029            },
3030
3031            onEddRemoveFromCartEvent: function (event) {
3032                this.fireEvent(event.name, event);
3033            },
3034            onPageScroll: function (event) {
3035                this.fireEvent(event.name, event);
3036            },
3037            onTime: function (event) {
3038                this.fireEvent(event.name, event);
3039            },
3040
3041        };
3042
3043    }(options);
3044
3045    var GTM = function (options) {
3046        var initialized = false;
3047
3048        var datalayer_name = 'dataLayer';
3049        /*
3050         * @param name
3051         * @param data
3052         */
3053        function fireEvent(name, event) {
3054            if(typeof window.pys_event_data_filter === "function" && window.pys_disable_event_filter(name,'gtm')) {
3055                return;
3056            }
3057
3058
3059            var eventParams = event.params;
3060            var data = event.params;
3061            var valuesArray = Object.values(event.trackingIds);
3062            var ids = valuesArray;
3063            Utils.copyProperties(Utils.getRequestParams(), eventParams);
3064            var _fireEvent = function (tracking_id,name,params, event=null) {
3065                var eventData = {};
3066                var ContainerCodeHasTag = !options.gtm.gtm_just_data_layer && tracking_id.length > 0;
3067                if(ContainerCodeHasTag) {
3068                    params['send_to'] = tracking_id;
3069                }
3070                else if(!options.gtm.gtm_just_data_layer){
3071                    return
3072                }
3073
3074                if (params.hasOwnProperty('ecommerce')) {
3075                    eventData.ecommerce = params.ecommerce;
3076                    delete params.ecommerce;
3077                }
3078
3079                var automatedParams = { ...params };
3080                [params['manualName'], 'manualName', 'triggerType'].forEach(key => delete automatedParams[key]);
3081                if(event && (!event.hasOwnProperty('hasAutoParam') || (event.hasOwnProperty('hasAutoParam') && event.hasAutoParam))) {
3082                    eventData.automatedParameters = automatedParams;
3083                }
3084
3085// Move custom parameters to eventData
3086
3087                if (params.hasOwnProperty(params['manualName'])) {
3088                    eventData[params['manualName']] = params[params['manualName']];
3089                    delete params[params['manualName']];
3090                }
3091
3092                ['manualName','triggerType'].forEach(key => {
3093                    if (params.hasOwnProperty(key)) {
3094                        eventData[key] = params[key];
3095                        delete params[key];
3096                    }
3097                });
3098
3099                eventData.manualDataLayer = options.gtm.gtm_dataLayer_name ?? 'dataLayer';
3100
3101                eventData.event = name;
3102
3103                if (options.debug) {
3104                    if (ContainerCodeHasTag){
3105                        console.log('[Google GTM #' + tracking_id + '] ' + name, eventData);
3106                    }
3107                    else {
3108                        console.log('[Google GTM push to "'+datalayer_name+'"] ' + name, eventData);
3109                    }
3110                }
3111                window[datalayer_name].push(eventData);
3112
3113            };
3114
3115            var copyParams = Utils.copyProperties(eventParams, {}); // copy params because mapParamsToGTM can modify it
3116
3117            var params = mapParamsToGTM(ids,name,copyParams)
3118
3119            params.event_id = Utils.generateUniqueId(event);
3120
3121            _fireEvent(ids, name, params, event);
3122        }
3123
3124        function normalizeEventName(eventName) {
3125
3126            var matches = {
3127                ViewContent: 'view_item',
3128                AddToCart: 'add_to_cart',
3129                AddToWishList: 'add_to_wishlist',
3130                InitiateCheckout: 'begin_checkout',
3131                Purchase: 'purchase',
3132                Lead: 'generate_lead',
3133                CompleteRegistration: 'sign_up',
3134                AddPaymentInfo: 'set_checkout_option'
3135            };
3136
3137            return matches.hasOwnProperty(eventName) ? matches[eventName] : eventName;
3138
3139        }
3140
3141        function mapParamsToGTM(tag,name,param) {
3142            //GA4 automatically collects a number of parameters for all events
3143            var hasGTM = false;
3144
3145            // end
3146            if (Array.isArray(tag)) {
3147                hasGTM = tag.some(function (element) {
3148                    return isGTM(element);
3149                });
3150            } else if(isGTM(tag)) {
3151                // tag is a string and matches GTM
3152                hasGTM = true;
3153            }
3154            if(hasGTM) {
3155                delete param.event_category;
3156                delete param.event_label;
3157
3158                delete param.analytics_storage;
3159                delete param.ad_storage;
3160                delete param.ad_user_data;
3161                delete param.ad_personalization;
3162
3163                if(name === 'search') {
3164                    param['search'] = param.search_term;
3165                    delete param.search_term;
3166                    delete param.dynx_itemid;
3167                    delete param.dynx_pagetype;
3168                    delete param.dynx_totalvalue;
3169                }
3170            }
3171            return param;
3172        }
3173
3174        function isGTM(tag) {
3175            return tag.indexOf('GTM') === 0;
3176        }
3177        /**
3178         * Public API
3179         */
3180        return {
3181            tag: function() {
3182                return "gtm";
3183            },
3184            isEnabled: function () {
3185                return options.hasOwnProperty('gtm');
3186            },
3187            disable: function () {
3188                initialized = false;
3189            },
3190            updateEnhancedConversionData : function () {
3191                if (!initialized || !this.isEnabled()) {
3192                    return;
3193                }
3194                if(options.hasOwnProperty("tracking_analytics") && options.tracking_analytics.hasOwnProperty("userDataEnable") && options.tracking_analytics.userDataEnable) {
3195                    var advanced = Utils.getAdvancedFormData();
3196                    if (Object.keys(advanced).length > 0) {
3197                        window[datalayer_name].push({'user_data' : advanced});
3198                    }
3199                }
3200            },
3201            loadPixel: function () {
3202
3203                if (initialized || !this.isEnabled() || !Utils.consentGiven('analytics')) {
3204                    return;
3205                }
3206                datalayer_name = options.gtm.gtm_dataLayer_name ?? 'dataLayer';
3207
3208                for (var i = 0; i < options.gtm.trackingIds.length; i++) {
3209                    var trackingId = options.gtm.trackingIds[i];
3210                    if (!options.gtm.gtm_just_data_layer) {
3211                        console.log('[PYS] Google Tag Manager container code loaded');
3212                        Utils.loadGTMScript(trackingId);
3213                        break;
3214                    }
3215                }
3216                if(options.gtm.gtm_just_data_layer) {
3217                    console.warn && console.warn("[PYS] Google Tag Manager container code placement set to OFF !!!");
3218                    console.warn && console.warn("[PYS] Data layer codes are active but GTM container must be loaded using custom coding !!!");
3219                    // Push consent defaults even when GTM script loading is handled externally.
3220                    // Fixes the case where gtm_just_data_layer=true with tracking IDs configured,
3221                    // which previously skipped loadGTMScript() entirely and never pushed defaults.
3222                    Utils.pushGTMConsentDefaults( datalayer_name );
3223                    if(options.gtm.trackingIds.length == 0){
3224                        Utils.loadGTMScript();
3225                    }
3226                }
3227
3228                if(options.hasOwnProperty("tracking_analytics") && options.tracking_analytics.hasOwnProperty("userDataEnable") && options.tracking_analytics.userDataEnable){
3229                    var advanced = Utils.getAdvancedFormData();
3230                    if(Object.keys(advanced).length > 0){
3231                        window[datalayer_name].push({'user_data' : advanced});
3232                    }
3233                }
3234
3235
3236                var config = {};
3237
3238                if(options.user_id && options.user_id != 0) {
3239                    config.user_id = options.user_id;
3240                }
3241
3242                // Cross-Domain tracking
3243
3244                var ids = options.gtm.trackingIds;
3245
3246
3247                initialized = true;
3248
3249                Utils.fireStaticEvents('gtm');
3250            },
3251
3252            fireEvent: function (name, data) {
3253
3254                if (!initialized || !this.isEnabled()) {
3255                    return false;
3256                }
3257
3258                data.delay = data.delay || 0;
3259                data.params = data.params || {};
3260
3261                if (data.delay === 0) {
3262
3263                    fireEvent(name, data);
3264
3265                } else {
3266
3267                    setTimeout(function (name, params) {
3268                        fireEvent(name, params);
3269                    }, data.delay * 1000, name, data);
3270
3271                }
3272
3273                return true;
3274
3275            },
3276
3277            onCommentEvent: function (event) {
3278                this.fireEvent(event.name, event);
3279            },
3280
3281            onDownloadEvent: function (event) {
3282                this.fireEvent(event.name, event);
3283            },
3284
3285            onFormEvent: function (event) {
3286                this.fireEvent(event.name, event);
3287            },
3288
3289            onWooAddToCartOnButtonEvent: function (product_id, prod_info = null) {
3290                if(!options.dynamicEvents.woo_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
3291                    return;
3292                var event = Utils.clone(options.dynamicEvents.woo_add_to_cart_on_button_click[this.tag()]);
3293
3294                if (window.pysWooProductData.hasOwnProperty(product_id)) {
3295                    if (window.pysWooProductData[product_id].hasOwnProperty('gtm')) {
3296                        Utils.copyProperties(window.pysWooProductData[product_id]['gtm'].params, event.params)
3297                        this.fireEvent(event.name, event);
3298                    }
3299                }
3300
3301            },
3302
3303            onWooAddToCartOnSingleEvent: function (product_id, qty, product_type, $form) {
3304                window.pysWooProductData = window.pysWooProductData || [];
3305
3306                if(!options.dynamicEvents.woo_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
3307                    return;
3308                var event = Utils.clone(options.dynamicEvents.woo_add_to_cart_on_button_click[this.tag()]);
3309
3310                if (product_type === Utils.PRODUCT_VARIABLE && !options.gtm.wooVariableAsSimple) {
3311                    product_id = parseInt($form.find('input[name="variation_id"]').val());
3312                }
3313
3314                if (window.pysWooProductData.hasOwnProperty(product_id)) {
3315                    if (window.pysWooProductData[product_id].hasOwnProperty('gtm')) {
3316
3317                        Utils.copyProperties(window.pysWooProductData[product_id]['gtm'].params, event.params);
3318
3319                        params = event.params.hasOwnProperty('ecommerce') ? event.params.ecommerce : event.params;
3320
3321                        if(product_type === Utils.PRODUCT_GROUPED ) {
3322                            var groupValue = 0;
3323                            $form.find(".woocommerce-grouped-product-list .qty").each(function(index){
3324                                var childId = $(this).attr('name').replaceAll("quantity[","").replaceAll("]","");
3325                                var quantity = parseInt($(this).val());
3326                                if(isNaN(quantity)) {
3327                                    quantity = 0;
3328                                }
3329                                var childItem = window.pysWooProductData[product_id]['gtm'].grouped[childId];
3330                                params.items.forEach(function(el,index,array) {
3331                                    if(el.id == childItem.content_id) {
3332                                        if(quantity > 0){
3333                                            el.quantity = quantity;
3334                                            el.price = childItem.price;
3335                                        } else {
3336                                            array.splice(index, 1);
3337                                        }
3338                                    }
3339                                });
3340                                groupValue += childItem.price * quantity;
3341                            });
3342
3343                            if(options.woo.addToCartOnButtonValueEnabled &&
3344                                options.woo.addToCartOnButtonValueOption !== 'global' &&
3345                                params.hasOwnProperty('value')) {
3346                                params.value = groupValue;
3347                            }
3348
3349                            if(groupValue == 0) return; // skip if no items selected
3350                        } else {
3351                            // update items qty param
3352                            params.items[0].quantity = qty;
3353                        }
3354
3355                        // maybe customize value option
3356                        if (options.woo.addToCartOnButtonValueEnabled &&
3357                            options.woo.addToCartOnButtonValueOption !== 'global' &&
3358                            product_type !== Utils.PRODUCT_GROUPED)
3359                        {
3360                            if(params.hasOwnProperty('value')) {
3361                                params.value = params.items[0].price * qty;
3362                            }
3363
3364                        }
3365
3366
3367                        this.fireEvent(event.name, event);
3368                    }
3369                }
3370
3371            },
3372
3373            onWooRemoveFromCartEvent: function (event) {
3374
3375                this.fireEvent(event.name, event);
3376
3377            },
3378
3379            onEddAddToCartOnButtonEvent: function (download_id, price_index, qty) {
3380                if(!options.dynamicEvents.edd_add_to_cart_on_button_click.hasOwnProperty(this.tag()))
3381                    return;
3382                var event = Utils.clone(options.dynamicEvents.edd_add_to_cart_on_button_click[this.tag()]);
3383
3384
3385                if (window.pysEddProductData && window.pysEddProductData.hasOwnProperty(download_id)) {
3386
3387                    var index;
3388
3389                    if (price_index) {
3390                        index = download_id + '_' + price_index;
3391                    } else {
3392                        index = download_id;
3393                    }
3394
3395                    if (window.pysEddProductData[download_id].hasOwnProperty(index)) {
3396                        if (window.pysEddProductData[download_id][index].hasOwnProperty('gtm')) {
3397
3398                            Utils.copyProperties(window.pysEddProductData[download_id][index]['gtm'].params, event.params);
3399                            item = event.params.hasOwnProperty('ecommerce') ? event.params.ecommerce.items[0] : event.params.items[0];
3400                            // update items qty param
3401                            item.quantity = qty;
3402
3403                            this.fireEvent(event.name,event);
3404
3405                        }
3406                    }
3407
3408                }
3409
3410            },
3411
3412            onEddRemoveFromCartEvent: function (event) {
3413                this.fireEvent(event.name, event);
3414            },
3415            onPageScroll: function (event) {
3416                this.fireEvent(event.name, event);
3417            },
3418            onTime: function (event) {
3419                this.fireEvent(event.name, event);
3420            },
3421        };
3422
3423    }(options);
3424
3425    var getPixelBySlag = function getPixelBySlag(slug) {
3426        switch (slug) {
3427            case "facebook": return window.pys.Facebook;
3428            case "ga": return window.pys.Analytics;
3429            case "gtm": return window.pys.GTM;
3430            case "bing": return window.pys.Bing;
3431            case "pinterest": return window.pys.Pinterest;
3432	        case "reddit": return window.pys.Reddit;
3433        }
3434    }
3435
3436    window.pys = window.pys || {};
3437    window.pys.Facebook = Facebook;
3438    window.pys.Analytics = Analytics;
3439    window.pys.GTM = GTM;
3440    window.pys.Utils = Utils;
3441    window.pys.getPixelBySlag = getPixelBySlag;
3442
3443    // Allow external scripts (e.g. generatePixelCode fragments) to refresh
3444    // the uniqueId cache for a given event key, so that each new add_to_cart
3445    // action gets a fresh shared ID across all pixels.
3446    window.pys.setEventUniqueId = function(idKey, newId) {
3447        uniqueId[idKey] = newId;
3448    };
3449
3450    // Export getPixelBySlag globally for backward compatibility
3451    window.getPixelBySlag = getPixelBySlag;
3452
3453
3454    function pysNormalInit() {
3455
3456        if($("#pys_late_event").length > 0) {
3457            var dirAttr = $("#pys_late_event").attr("dir");
3458            if (dirAttr) {
3459                try {
3460                    var events = JSON.parse(dirAttr);
3461                } catch (e) {
3462                    console.warn("Invalid JSON in pys_late_event dir attribute:", e);
3463                }
3464            } else {
3465                console.warn("pys_late_event dir attribute is undefined or empty");
3466            }
3467            if (events) {
3468                for (var key in events) {
3469                    var event = {};
3470                    event[events[key].e_id] = [events[key]];
3471                    if (options.staticEvents.hasOwnProperty(key)) {
3472                        Object.assign(options.staticEvents[key], event);
3473                    } else {
3474                        options.staticEvents[key] = event;
3475                    }
3476                }
3477            }
3478        }
3479
3480        var Pinterest = Utils.setupPinterestObject();
3481        var Bing = Utils.setupBingObject();
3482	    var Reddit = Utils.setupRedditObject();
3483
3484        if(options.hasOwnProperty('cookie'))
3485        {
3486            if(options.cookie.externalID_disabled_by_api || options.cookie.disabled_all_cookie)
3487            {
3488                Cookies.remove('pbid')
3489            }
3490            if(options.cookie.disabled_advanced_form_data_cookie || options.cookie.disabled_all_cookie)
3491            {
3492                Cookies.remove('pys_advanced_form_data')
3493            }
3494            if(options.cookie.disabled_landing_page_cookie || options.cookie.disabled_all_cookie)
3495            {
3496                Cookies.remove('pys_landing_page')
3497                Cookies.remove('last_pys_landing_page')
3498            }
3499            if(options.cookie.disabled_trafficsource_cookie || options.cookie.disabled_all_cookie)
3500            {
3501                Cookies.remove('pysTrafficSource')
3502                Cookies.remove('last_pysTrafficSource')
3503            }
3504            if(options.cookie.disabled_first_visit_cookie || options.cookie.disabled_all_cookie)
3505            {
3506                Cookies.remove('pys_first_visit')
3507
3508            }
3509            if(options.cookie.disabled_utmTerms_cookie || options.cookie.disabled_all_cookie)
3510            {
3511                $.each(Utils.utmTerms, function (index, name) {
3512                    Cookies.remove('pys_' + name)
3513                });
3514                $.each(Utils.utmTerms, function (index, name) {
3515                    Cookies.remove('last_pys_' + name)
3516                });
3517            }
3518            if(options.cookie.disabled_utmId_cookie || options.cookie.disabled_all_cookie)
3519            {
3520                $.each(Utils.utmId,function(index,name) {
3521                    Cookies.remove('pys_' + name)
3522                })
3523                $.each(Utils.utmId,function(index,name) {
3524                    Cookies.remove('last_pys_' + name)
3525                });
3526            }
3527        }
3528
3529
3530        if (options.gdpr.cookie_law_info_integration_enabled) {
3531            var cli_cookie = Cookies.get('cookieyes-consent') ?? Cookies.get('viewed_cookie_policy');
3532
3533            if (typeof cli_cookie !== 'undefined') {
3534                if (cli_cookie === Cookies.get('cookieyes-consent') && getCookieYes('analytics') == 'yes') {
3535                    Utils.manageCookies();
3536                } else if (cli_cookie === Cookies.get('viewed_cookie_policy') && cli_cookie == 'yes') {
3537                    Utils.manageCookies();
3538                }
3539            }
3540        }
3541
3542        if ( options.gdpr.consent_magic_integration_enabled && typeof CS_Data !== "undefined" ) {
3543            if ( CS_Data.cs_script_cat.pys == CS_Data.cs_necessary_cat_id || CS_Data.cs_script_cat.pys == 0 ) {
3544                Utils.manageCookies();
3545            } else if ( Cookies.get( 'cs_enabled_cookie_term' + CS_Data.test_prefix + '_' + CS_Data.cs_script_cat.pys ) == 'yes' ) {
3546                Utils.manageCookies();
3547            }
3548        } else {
3549            Utils.manageCookies();
3550        }
3551
3552        Utils.setupGdprCallbacks();
3553        // page scroll event
3554        if ( options.dynamicEvents.hasOwnProperty("automatic_event_scroll")
3555        ) {
3556
3557            var singlePageScroll = function () {
3558
3559
3560                var docHeight = $(document).height() - $(window).height();
3561                var isFired = false;
3562
3563                if (options.dynamicEvents.hasOwnProperty("automatic_event_scroll")) {
3564                    var pixels = Object.keys(options.dynamicEvents.automatic_event_scroll);
3565                    for(var i = 0;i<pixels.length;i++) {
3566                        var event = Utils.clone(options.dynamicEvents.automatic_event_scroll[pixels[i]]);
3567                        var scroll = Math.round(docHeight * event.scroll_percent / 100)// convert % to absolute positions
3568
3569                        if(scroll < $(window).scrollTop()) {
3570	                        if ( pixels[i] !== 'reddit') {
3571		                        Utils.copyProperties( Utils.getRequestParams(), event.params );
3572	                        }
3573	                        getPixelBySlag( pixels[ i ] ).onPageScroll( event );
3574                            isFired = true
3575                        }
3576                    }
3577                }
3578                if(isFired) {
3579                    $(document).off("scroll",singlePageScroll);
3580                }
3581            }
3582            $(document).on("scroll",singlePageScroll);
3583        }
3584
3585
3586        if (options.dynamicEvents.hasOwnProperty("automatic_event_time_on_page")) {
3587            var pixels = Object.keys(options.dynamicEvents.automatic_event_time_on_page);
3588            var time = options.dynamicEvents.automatic_event_time_on_page[pixels[0]].time_on_page; // the same for all pixel
3589            setTimeout(function(){
3590                for(var i = 0;i<pixels.length;i++) {
3591                    var event = Utils.clone(options.dynamicEvents.automatic_event_time_on_page[pixels[i]]);
3592
3593	                if ( pixels[i] !== 'reddit') {
3594		                Utils.copyProperties(Utils.getRequestParams(), event.params);
3595	                }
3596                    getPixelBySlag(pixels[i]).onTime(event);
3597                }
3598            },time*1000);
3599        }
3600
3601        // setup Click Event
3602        if (options.dynamicEvents.hasOwnProperty("automatic_event_download")) {
3603
3604            $(document).onFirst('click', 'a, button, input[type="button"], input[type="submit"]', function (e) {
3605
3606                var $elem = $(this);
3607
3608                // Download
3609                if(options.dynamicEvents.hasOwnProperty("automatic_event_download")
3610                ) {
3611                    var isFired = false;
3612                    if ($elem.is('a')) {
3613                        var href = $elem.attr('href');
3614                        if (typeof href !== "string") {
3615                            return;
3616                        }
3617                        href = href.trim();
3618                        var extension = Utils.getLinkExtension(href);
3619                        var track_download = false;
3620
3621                        if (extension.length > 0) {
3622
3623                            if(options.dynamicEvents.hasOwnProperty("automatic_event_download") ) {
3624                                var pixels = Object.keys(options.dynamicEvents.automatic_event_download);
3625                                for (var i = 0; i < pixels.length; i++) {
3626                                    var event = Utils.clone(options.dynamicEvents.automatic_event_download[pixels[i]]);
3627                                    var extensions = event.extensions;
3628                                    if (extensions.includes(extension)) {
3629
3630                                        if( pixels[i] == "tiktok" || pixels[i] == "reddit" ) {
3631                                            getPixelBySlag(pixels[i]).fireEvent(tikEvent.name, event);
3632                                        } else {
3633                                            if (options.enable_remove_download_url_param) {
3634                                                href = stripUrlQuery(href);
3635                                            }
3636                                            event.params.download_url = href;
3637                                            event.params.download_type = extension;
3638                                            event.params.download_name = Utils.getLinkFilename(href);
3639                                            getPixelBySlag(pixels[i]).onDownloadEvent(event);
3640                                        }
3641
3642                                        isFired = true;
3643                                    }
3644                                }
3645                            }
3646                        }
3647                    }
3648                    if(isFired) { // prevent duplicate events on the same element
3649                        return;
3650                    }
3651                }
3652            });
3653        }
3654
3655
3656        // setup Dynamic events
3657        $.each(options.triggerEventTypes, function (triggerType, events) {
3658
3659            $.each(events, function (eventId, triggers) {
3660
3661                switch (triggerType) {
3662                    case 'url_click':
3663                        //@see: Utils.setupURLClickEvents()
3664                        break;
3665
3666                    case 'css_click':
3667                        Utils.setupCSSClickEvents(eventId, triggers);
3668                        break;
3669
3670                    case 'css_mouseover':
3671                        Utils.setupMouseOverClickEvents(eventId, triggers);
3672                        break;
3673
3674                    case 'scroll_pos':
3675                        Utils.setupScrollPosEvents(eventId, triggers);
3676                        break;
3677                    case 'comment':
3678                        Utils.setupCommentEvents(eventId, triggers);
3679                        break;
3680                }
3681
3682            });
3683
3684        });
3685        // setup WooCommerce events
3686        if (options.woo.enabled) {
3687
3688            // WooCommerce AddToCart
3689            if (options.dynamicEvents.hasOwnProperty("woo_add_to_cart_on_button_click")
3690                && options.woo.hasOwnProperty("addToCartCatchMethod")
3691                && options.woo.addToCartCatchMethod === "add_cart_js"
3692            ) {
3693
3694                // Loop, any kind of "simple" product, except external
3695                $('.add_to_cart_button:not(.product_type_variable,.product_type_bundle,.single_add_to_cart_button)').on("click",function (e) {
3696
3697                    var product_id = $(this).data('product_id');
3698                    if (typeof product_id !== 'undefined') {
3699                        if (options.dynamicEvents.hasOwnProperty("woo_add_to_cart_on_button_click")) {
3700                            var tmpEventID = pys_generate_token();
3701                            $.each(options.dynamicEvents.woo_add_to_cart_on_button_click, function (i, tag) {
3702                                tag.eventID = tmpEventID;
3703                                // Also refresh the uniqueId cache so server-API path
3704                                // (ajaxForServerStaticEvent) gets the same fresh ID.
3705                                var idKey = tag.hasOwnProperty('custom_event_post_id') ? tag.custom_event_post_id : tag.e_id;
3706                                uniqueId[idKey] = tmpEventID;
3707                            });
3708                        }
3709                        if (typeof product_id !== 'undefined') {
3710                            Facebook.onWooAddToCartOnButtonEvent(product_id);
3711                            Analytics.onWooAddToCartOnButtonEvent(product_id);
3712                            GTM.onWooAddToCartOnButtonEvent(product_id);
3713                            Pinterest.onWooAddToCartOnButtonEvent(product_id);
3714                            Bing.onWooAddToCartOnButtonEvent(product_id);
3715	                        Reddit.onWooAddToCartOnButtonEvent(product_id);
3716                        }
3717                    }
3718                });
3719
3720                // Single Product
3721                // tap try to https://stackoverflow.com/questions/30990967/on-tap-click-event-firing-twice-how-to-avoid-it
3722                //  $(document) not work
3723                $('body').onFirst('click','button.single_add_to_cart_button,.single_add_to_cart_button',function (e) {
3724
3725                    var $button = $(this);
3726
3727                    if ($button.hasClass('disabled')) {
3728                        return;
3729                    }
3730
3731                    var $form = $button.closest('form');
3732
3733                    var product_type = Utils.PRODUCT_SIMPLE;
3734
3735                    if ($form.length === 0) {
3736                        return ;
3737                    } else if ($form.hasClass('variations_form')) {
3738                        product_type = Utils.PRODUCT_VARIABLE;
3739                    } else if($form.hasClass('bundle_form')) {
3740                        product_type = Utils.PRODUCT_BUNDLE;
3741                    } else if($form.hasClass('grouped_form')) {
3742                        product_type = Utils.PRODUCT_GROUPED;
3743                    }
3744
3745
3746
3747
3748                    var product_id;
3749                    var qty;
3750
3751                    if (product_type === Utils.PRODUCT_GROUPED) {
3752                        qty = 1;
3753                        product_id = parseInt($form.find('*[name="add-to-cart"]').val());
3754                    } else if (product_type === Utils.PRODUCT_VARIABLE) {
3755                        product_id = parseInt($form.find('*[name="add-to-cart"]').val());
3756                        var qtyTag = $form.find('input[name="quantity"]');
3757                        if(qtyTag.length <= 0) {
3758                            qtyTag = $form.find('select[name="quantity"]');
3759                        }
3760                        qty = parseInt(qtyTag.val());
3761                    } else {
3762                        product_id = parseInt($form.find('*[name="add-to-cart"]').val());
3763                        var qtyTag = $form.find('input[name="quantity"]');
3764                        if(qtyTag.length <= 0) {
3765                            qtyTag = $form.find('select[name="quantity"]');
3766                        }
3767                        qty = parseInt(qtyTag.val());
3768                    }
3769
3770                    if(options.dynamicEvents.hasOwnProperty("woo_add_to_cart_on_button_click")){
3771                        var tmpEventID = pys_generate_token();
3772                        $.each(options.dynamicEvents.woo_add_to_cart_on_button_click, function (i, tag) {
3773                            tag.eventID = tmpEventID;
3774                            // Also refresh the uniqueId cache so server-API path
3775                            // (ajaxForServerStaticEvent) gets the same fresh ID.
3776                            var idKey = tag.hasOwnProperty('custom_event_post_id') ? tag.custom_event_post_id : tag.e_id;
3777                            uniqueId[idKey] = tmpEventID;
3778                        });
3779                    }
3780
3781                    Facebook.onWooAddToCartOnSingleEvent(product_id, qty, product_type, $form);
3782                    Analytics.onWooAddToCartOnSingleEvent(product_id, qty, product_type, $form);
3783
3784                    GTM.onWooAddToCartOnSingleEvent(product_id, qty, product_type, $form);
3785
3786                    Pinterest.onWooAddToCartOnSingleEvent(product_id, qty, product_type, false, $form);
3787                    Bing.onWooAddToCartOnSingleEvent(product_id, qty, product_type, false, $form);
3788	                Reddit.onWooAddToCartOnSingleEvent(product_id, qty, product_type, false, $form);
3789
3790                });
3791
3792            }
3793
3794            // WooCommerce RemoveFromCart
3795            if (options.dynamicEvents.hasOwnProperty("woo_remove_from_cart")) {
3796
3797                $('body').on('click', options.woo.removeFromCartSelector, function (e) {
3798
3799                    var $a = $(e.currentTarget),
3800                        href = $a.attr('href');
3801
3802                    // extract cart item hash from remove button URL
3803                    var regex = new RegExp("[\\?&]remove_item=([^&#]*)"),
3804                        results = regex.exec(href);
3805
3806                    if (results !== null) {
3807
3808                        var item_hash = results[1];
3809
3810                        if (options.dynamicEvents["woo_remove_from_cart"].hasOwnProperty(item_hash)) {
3811                            var events = options.dynamicEvents["woo_remove_from_cart"][item_hash];
3812                            Utils.fireEventForAllPixel("onWooRemoveFromCartEvent",events)
3813                        }
3814
3815                    }
3816
3817                });
3818            }
3819        }
3820
3821        // setup EDD events
3822        if (options.edd.enabled) {
3823
3824            // EDD AddToCart
3825            if (options.dynamicEvents.hasOwnProperty("edd_add_to_cart_on_button_click")) {
3826
3827                $('form.edd_download_purchase_form .edd-add-to-cart').on("click",function (e) {
3828
3829                    var $button = $(this);
3830                    var $form = $button.closest('form');
3831                    var variable_price = $button.data('variablePrice'); // yes/no
3832                    var price_mode = $button.data('priceMode'); // single/multi
3833                    var ids = [];
3834                    var quantities = [];
3835                    var qty;
3836                    var id;
3837
3838                    if (variable_price === 'yes' && price_mode === 'multi') {
3839
3840                        id = $form.find('input[name="download_id"]').val();
3841
3842                        // get selected variants
3843                        $.each($form.find('input[name="edd_options[price_id][]"]:checked'), function (i, el) {
3844                            ids.push(id + '_' + $(el).val());
3845                        });
3846
3847                        // get qty for selected variants
3848                        $.each(ids, function (i, variant_id) {
3849
3850                            var variant_index = variant_id.split('_', 2);
3851                            qty = $form.find('input[name="edd_download_quantity_' + variant_index[1] + '"]').val();
3852
3853                            if (typeof qty !== 'undefined') {
3854                                quantities.push(qty);
3855                            } else {
3856                                quantities.push(1);
3857                            }
3858
3859                        });
3860
3861                    } else if (variable_price === 'yes' && price_mode === 'single') {
3862
3863                        id = $form.find('input[name="download_id"]').val();
3864                        ids.push(id + '_' + $form.find('input[name="edd_options[price_id][]"]:checked').val());
3865
3866                        qty = $form.find('input[name="edd_download_quantity"]').val();
3867
3868                        if (typeof qty !== 'undefined') {
3869                            quantities.push(qty);
3870                        } else {
3871                            quantities.push(1);
3872                        }
3873
3874                    } else {
3875
3876                        ids.push($button.data('downloadId'));
3877
3878                        qty = $form.find('input[name="edd_download_quantity"]').val();
3879
3880                        if (typeof qty !== 'undefined') {
3881                            quantities.push(qty);
3882                        } else {
3883                            quantities.push(1);
3884                        }
3885                    }
3886
3887                    // fire event for each download/variant
3888                    $.each(ids, function (i, download_id) {
3889
3890                        var q = parseInt(quantities[i]);
3891                        var variant_index = download_id.toString().split('_', 2);
3892                        var price_index;
3893
3894                        if (variant_index.length === 2) {
3895                            download_id = variant_index[0];
3896                            price_index = variant_index[1];
3897                        }
3898
3899                        Facebook.onEddAddToCartOnButtonEvent(download_id, price_index, q);
3900                        Analytics.onEddAddToCartOnButtonEvent(download_id, price_index, q);
3901                        GTM.onEddAddToCartOnButtonEvent(download_id, price_index, q);
3902                        Pinterest.onEddAddToCartOnButtonEvent(download_id, price_index, q);
3903                        Bing.onEddAddToCartOnButtonEvent(download_id, price_index, q);
3904	                    Reddit.onEddAddToCartOnButtonEvent(download_id, price_index, q);
3905                    });
3906
3907                });
3908
3909            }
3910
3911
3912            // EDD RemoveFromCart
3913            if (options.dynamicEvents.hasOwnProperty("edd_remove_from_cart") ) {
3914
3915                $('form#edd_checkout_cart_form .edd_cart_remove_item_btn').on("click",function (e) {
3916
3917                    var href = $(this).attr('href');
3918                    if(href) {
3919                        var key = href.substring(href.indexOf('=') + 1).charAt(0);
3920                        if (options.dynamicEvents.edd_remove_from_cart.hasOwnProperty(key)) {
3921                            var events = options.dynamicEvents.edd_remove_from_cart[key];
3922                            Utils.fireEventForAllPixel("onEddRemoveFromCartEvent",events)
3923                        }
3924                    }
3925                });
3926
3927            }
3928
3929        }
3930
3931        // setup Comment Event
3932        if (options.dynamicEvents.hasOwnProperty("automatic_event_c
3932omment")
3933        ) {
3934
3935            $('form.comment-form').on("submit",function () {
3936                if (options.dynamicEvents.hasOwnProperty("automatic_event_comment")) {
3937                    var pixels = Object.keys(options.dynamicEvents.automatic_event_comment);
3938                    for (var i = 0; i < pixels.length; i++) {
3939	                    var event = Utils.clone(options.dynamicEvents.automatic_event_comment[pixels[i]]);
3940	                    if ( pixels[i] !== 'reddit') {
3941		                    Utils.copyProperties(Utils.getRequestParams(), event.params);
3942	                    }
3943                        getPixelBySlag(pixels[i]).onCommentEvent(event);
3944                    }
3945                }
3946            });
3947
3948        }
3949
3950
3951        // setup Form Event
3952        if ( options.dynamicEvents.hasOwnProperty("automatic_event_form")) {
3953
3954            $(document).onFirst('submit', 'form', function (e) {
3955
3956                var $form = $(this);
3957
3958                // exclude WP forms
3959                const formClass = $form.attr('class') || '';
3960                const formRole = ($form.attr('role') || '').toLowerCase();
3961                const formId = $form.attr('id');
3962
3963                if (
3964                    $form.hasClass('comment-form') ||
3965                    $form.hasClass('search-form') ||
3966                    $form.hasClass('woocommerce-ordering') ||
3967                    formClass.toLowerCase().includes('search') || formClass.toLowerCase().includes('add-to-cart') ||
3968                    formRole === 'search' ||
3969                    formId === 'adminbarsearch'
3970                ) {
3971                    return;
3972                }
3973
3974                // exclude Woo forms
3975                if ($form.hasClass('woocommerce-product-search') || $form.hasClass('cart') || $form.hasClass('woocommerce-cart-form') ||
3976                    $form.hasClass('woocommerce-shipping-calculator') || $form.hasClass('checkout') || $form.hasClass('checkout_coupon')) {
3977                    return;
3978                }
3979
3980                // exclude EDD forms
3981                if ($form.hasClass('edd_form') || $form.hasClass('edd_download_purchase_form')) {
3982                    return;
3983                }
3984// exclude CF7 forms
3985                if ($form.hasClass('wpcf7-form')) {
3986                    return;
3987                }
3988                // exclude Forminator forms
3989                if ($form.hasClass('forminator-custom-form') || $form.hasClass('forminator_ajax')) {
3990                    return;
3991                }
3992                // exclude WPforms forms
3993                if ($form.hasClass('wpforms-form') || $form.hasClass('wpforms-ajax-form')) {
3994                    return;
3995                }
3996                // exclude Formidable forms
3997                /*if ($form.hasClass('frm-show-form')) {
3998                    return;
3999                }*/
4000                // exclude Ninja Forms forms
4001                if ($form.parent().hasClass('nf-form-layout')) {
4002                    return;
4003                }
4004                // exclude Fluent forms
4005                if ($form.hasClass('frm-fluent-form')) {
4006                    return;
4007                }
4008                if(!options.enable_success_send_form) {
4009                    var params = {
4010                        form_id: $form.attr('id'),
4011                        form_class: $form.attr('class'),
4012                        text: $form.find('[type="submit"]').is('input') ?
4013                            $form.find('[type="submit"]').val() : $form.find('[type="submit"]').text()
4014                    };
4015
4016                    if (options.dynamicEvents.hasOwnProperty("automatic_event_form")) {
4017                        var pixels = Object.keys(options.dynamicEvents.automatic_event_form);
4018                        for (var i = 0; i < pixels.length; i++) {
4019                            var event = Utils.clone(options.dynamicEvents.automatic_event_form[pixels[i]]);
4020
4021	                        if (pixels[i] === "tiktok" || pixels[i] === "reddit") {
4022                                getPixelBySlag(pixels[i]).fireEvent(event.name, event);
4023                            } else {
4024                                Utils.copyProperties(params, event.params,)
4025                                Utils.copyProperties(Utils.getRequestParam
4025s(), event.params);
4026                                getPixelBySlag(pixels[i]).onFormEvent(event);
4027                            }
4028                        }
4029                    }
4030                }
4031            });
4032
4033            document.addEventListener( 'wpcf7mailsent', function( event ) {
4034                var form_id = event.detail.contactFormId;
4035                sendFormAction($(event.target), form_id);
4036            }, false );
4037            //Forminator
4038            $(document).on( 'forminator:form:submit:success', function( event ){
4039                var form_id = $(event.target).find('input[name="form_id"]').val();
4040                sendFormAction($(event.target), form_id);
4041            });
4042
4043            //WPForm
4044            $('form.wpforms-form').on('wpformsAjaxSubmitSuccess', (event) => {
4045                var form_id = $(event.target).attr('data-formid');
4046                sendFormAction($(event.target), form_id);
4047            })
4048            $(document).on( 'frmFormComplete', function( event, form, response ) {
4049                const form_id = $(form).find('input[name="form_id"]').val();
4050                sendFormAction($(event.target), form_id);
4051            });
4052            // Ninja Forms
4053            $(document).onFirst('nfFormSubmitResponse', function (event, data) {
4054                const form_id = data.response.data.form_id;
4055                sendFormAction($(event.target), form_id);
4056            });
4057
4058            var fluentForms = $('form.frm-fluent-form');
4059            fluentForms.each(function() {
4060                var $form = $(this);
4061                $form.on('fluentform_submission_success', function(event) {
4062                    var $formItem = $(this);
4063                    var form_id = $formItem.attr('data-form_id');
4064                    sendFormAction($(event.target), form_id);
4065                });
4066            });
4067
4068        }
4069
4070
4071        // load pixel APIs
4072        Utils.loadPixels();
4073        // setup Enrich content
4074        if(Utils.isCheckoutPage()) {
4075            Utils.addCheckoutFields();
4076        }
4077    }
4078
4079    $(document).ready(function () {
4080        if ( options.dynamicDataUrl ) {
4081            fetch( options.dynamicDataUrl, {
4082                method: 'GET',
4083                credentials: 'same-origin',
4084                cache: 'no-store',
4085                headers: { 'Accept': 'application/json' }
4086            } )
4087                .then( function(r) { return r.ok ? r.json() : Promise.reject(r.status); } )
4088                .then( function(dynamic) {
4089                    options.ajax_event         = dynamic.ajax_event;
4090                    options.cache_bypass       = dynamic.cache_bypass;
4091                    options.tracking_analytics = dynamic.tracking_analytics;
4092                    if ( dynamic.gdpr_dynamic && options.gdpr ) {
4093                        Object.assign( options.gdpr, dynamic.gdpr_dynamic );
4094                        if ( dynamic.gdpr_dynamic.analytics_storage_value !== null )
4095                            options.gdpr.analytics_storage.value = dynamic.gdpr_dynamic.analytics_storage_value;
4096                        if ( dynamic.gdpr_dynamic.ad_storage_value !== null )
4097                            options.gdpr.ad_storage.value = dynamic.gdpr_dynamic.ad_storage_value;
4098                        if ( dynamic.gdpr_dynamic.ad_user_data_value !== null )
4099                            options.gdpr.ad_user_data.value = dynamic.gdpr_dynamic.ad_user_data_value;
4100                        if ( dynamic.gdpr_dynamic.ad_personalization_value !== null )
4101                            options.gdpr.ad_personalization.value = dynamic.gdpr_dynamic.ad_personalization_value;
4102                    }
4103                    options.cookie = dynamic.cookie;
4104
4105                    // Per-pixel user data (advanced matching) that was emptied out of
4106                    // the cached HTML. An inline value that still holds something wins:
4107                    // on pages we never strip (order received / EDD success) the HTML
4108                    // carries the order's own data, which beats the account-level data
4109                    // this endpoint can see.
4110                    if ( dynamic.pixels ) {
4111                        for ( var slug in dynamic.pixels ) {
4112                            if ( ! dynamic.pixels.hasOwnProperty( slug ) || ! options[ slug ] ) continue;
4113                            for ( var key in dynamic.pixels[ slug ] ) {
4114                                if ( ! dynamic.pixels[ slug ].hasOwnProperty( key ) ) continue;
4115                                var inline = options[ slug ][ key ];
4116                                if ( ! inline || Object.keys( inline ).length === 0 ) {
4117                                    options[ slug ][ key ] = dynamic.pixels[ slug ][ key ];
4118                                }
4119                            }
4120                        }
4121                    }
4122
4123                    pysNormalInit();
4124                } )
4125                .catch( function(err) {
4126                    if ( options.debug ) console.warn( '[PYS] dynamic-options fetch failed:', err );
4127                    pysNormalInit();
4128                } );
4129        } else {
4130            pysNormalInit();
4131        }
4132    });
4133
4134    var sendFormAction = function (form_target, formId){
4135        var params = {
4136            form_id: formId,
4137            text: form_target.find('[type="submit"]').is('input') ? form_target.find('[type="submit"]').val() :
4138                form_target.find('.forminator-button-submit').text() != '' ? form_target.find('.forminator-button-submit').text() :
4139                    form_target.find('[type="submit"]').text()
4140        };
4141
4142        if (options.dynamicEvents.hasOwnProperty("automatic_event_form")) {
4143            var pixels = Object.keys(options.dynamicEvents.automatic_event_form);
4144            for (var i = 0; i < pixels.length; i++) {
4145                var event = options.dynamicEvents.automatic_event_form[pixels[i]];
4146                if (pixels[i] === "tiktok" || pixels[i] === "reddit") {
4147                    getPixelBySlag(pixels[i]).fireEvent(event.name, event);
4148                } else {
4149                    Utils.copyProperties(params, event.params)
4150                    Utils.copyProperties(Utils.getRequestParams(), event.params);
4151                    getPixelBySlag(pixels[i]).onFormEvent(event);
4152                }
4153            }
4154        }
4155    }
4156
4157}(jQuery, pysOptions);
4158
4159function pys_generate_token() {
4160    return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
4161        (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
4162    );
4163}
4164
4165function getBundlePriceOnSingleProduct(data) {
4166    var items_sum = 0;
4167    jQuery(".bundle_form .bundled_product").each(function(index){
4168        var id = jQuery(this).find(".cart").data("bundled_item_id");
4169        var item_price = data.prices[id];
4170        var item_quantity = jQuery(this).find(".bundled_qty").val();
4171        if(!jQuery(this).hasClass("bundled_item_optional") ||
4172            jQuery(this).find(".bundled_product_optional_checkbox input").prop('checked')) {
4173            items_sum += item_price*item_quantity;
4174        }
4175    });
4176    return items_sum;
4177}
4178function getUrlParameter(sParam) {
4179    var sPageURL = window.location.search.substring(1),
4180        sURLVariables = sPageURL.split('&'),
4181        sParameterName,
4182        i;
4183
4184    for (i = 0; i < sURLVariables.length; i++) {
4185        sParameterName = sURLVariables[i].split('=');
4186
4187        if (sParameterName[0] === sParam) {
4188            return sParameterName[1] === undefined ? true : decodeURIComponent(sParameterName[1]);
4189        }
4190    }
4191    return false;
4192};
4193function getCookieYes(key) {
4194    const cookiesObj = document.cookie
4195        .split(";")
4196        .reduce((ac, cv) => {
4197            const [k, v] = cv.split("=");
4198            if (k && v) ac[k.trim()] = v;
4199            return ac;
4200        }, {});
4201    const consentCookie = cookiesObj["cookieyes-consent"] || cookiesObj["wt_consent"];
4202    if (!consentCookie) return undefined;
4203    const { [key]: value } = consentCookie
4204        .split(",")
4205        .reduce((obj, pair) => {
4206            const [k, v] = pair.split(":");
4207            if (k && v) obj[k] = v;
4208            return obj;
4209        }, {});
4210    return value;
4211}
4212function getRootDomain(useSubdomain = false) {
4213    const hostname = window.location.hostname; // Get the current hostname
4214    // Check if tldjs is defined before using it
4215    if (typeof tldjs === "undefined") {
4216        console.warn("tldjs is not defined");
4217        return hostname; // Return hostname as a fallback
4218    }
4219
4220    const rootDomain = tldjs.getDomain(hostname); // Use tldjs to extract the root domain
4221
4222    // Return the root domain with or without a leading dot based on useSubdomain
4223    return rootDomain ? (useSubdomain ? '.' + rootDomain : rootDomain) : hostname;
4224}

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.