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