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