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