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