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 += '>m_auth=' + gtm_auth + '>m_preview=' + gtm_preview + '>m_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.