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