1// is user active global vars 2window.abst = window.abst || {}; 3// activity timer 4window.abst.eventQueue = []; 5window.abst.abconvertpartner = {}; 6// Check localStorage for existing approval status, default to waiting if not found 7window.abst.waitForApproval = false;//localStorage.getItem('abstApprovalStatus') !== 'approved'; 8 9// Helper function to set approval status 10function setAbstApprovalStatus(approved) { 11 window.abst.waitForApproval = !approved; 12 if (approved) { 13 localStorage.setItem('abstApprovalStatus', 'approved'); 14 } else { 15 localStorage.removeItem('abstApprovalStatus'); 16 } 17} 18 19// activity timer - initialize once as an object 20window.abst.timer = localStorage.getItem('absttimer') === null ? {} : JSON.parse(localStorage.getItem('absttimer') || '{}'); 21 22window.abst.currscroll = window.scrollY; 23window.abst.currentMousePos = -1; 24window.abst.oldMousePos = -2; 25window.abst.abactive = true; 26window.abst.timeoutTime = 3000; // how much inactivity before we stop loggin in milliseconds 27window.abst.intervals = {}; 28// Initialize service partners 29const services = ['abawp', 'clarity', 'gai', 'abmix', 'abumav', 'umami', 'cabin', 'plausible', 'fathom', 'ga4', 'posthog']; 30window.abst.abconvertpartner = Object.fromEntries(services.map(service => [service, false])); 31 32//what size, mobile, tablet or desktop 33if(window.innerWidth < 768) { 34 window.abst.size = 'mobile'; 35} 36else if(window.innerWidth < 1024) { 37 window.abst.size = 'tablet'; 38} 39else { 40 window.abst.size = 'desktop'; 41} 42 43 if(window.btab_vars && window.btab_vars.advanced_tracking == '1') { 44 setAbCrypto(); 45 } 46 47 // add server events to cookie to be evented on next page load 48 function addServerEvents(data) { 49 const serverEvents = abstGetCookie('abst_server_events'); 50 51 if (serverEvents) { 52 const events = JSON.parse(decodeURIComponent(serverEvents)); 53 if (!events.some(event => event.eid === data.eid && event.variation === data.variation && event.type === data.type)) { 54 events.push(data); 55 abstSetCookie('abst_server_events', JSON.stringify(events), 2); 56 console.log('server event added to existing', data); 57 } 58 } else { 59 abstSetCookie('abst_server_events', JSON.stringify([data]), 2); 60 console.log('server event added none existing creating', data); 61 } 62} 63 64 65 jQuery(document).ready(function(){ 66 67 const serverEvents = abstGetCookie('abst_server_events'); 68 if (serverEvents) { 69 const events = JSON.parse(decodeURIComponent(serverEvents)); 70 events.forEach(event => { 71 console.log('server event', event); 72 btab_track_event(event); 73 }); 74 abstSetCookie('abst_server_events', false, 2); 75 } 76 77 78 79 80 if( window.btab_vars && !window.btab_vars.is_preview && jQuery('.ab-test-page-redirect').length > 0 ) { 81 jQuery('.ab-test-page-redirect').remove(); 82 } 83 84 if( jQuery('[bt_hidden="true"]').length > 0 ) { 85 jQuery('[bt_hidden="true"]').remove(); 86 } 87 88 89 // update scroll status 90 jQuery(document).mousemove(function(event) { 91 window.abst.currentMousePos = event.pageX; 92 }); 93 // catch mouse / keyboard action 94 jQuery('body').on('mousedown keydown touchstart', function(event) { 95 userActiveNow(); 96 }); 97 98 if(!('abConversionValue' in window.abst)) 99 window.abst.abConversionValue = 1; 100 101 window.abst.timerInterval = setInterval(function(){ 102 //check if scroll's changed 103 104 if((window.abst.currscroll != window.scrollY) || (window.abst.currentMousePos != window.abst.oldMousePos) ) 105 { 106 window.abst.currscroll = window.scrollY; 107 window.abst.oldMousePos = window.abst.currentMousePos; 108 userActiveNow(); 109 } 110 111 // check for active class and decrement all active timers 112 if(window.abst.abactive) //for each active timer, increment 113 abstOneSecond(); 114 115 },1000); // every second 116 117 118 //conversion things 119 if (typeof conversion_details !== 'undefined' && conversion_details) 120 { 121 var eid = null; 122 var variation = null; 123 124 // current p info 125 page_url = normalizeUrl(window.location.href); 126 //loop through each conversion d 127 jQuery.each( conversion_details, function( key, detail ) { 128 129 //regex url matching for url's with * *about* 130 if (detail.conversion_page_url && page_url) { 131 // Escape special characters in the user input 132 var escapedUrl = detail.conversion_page_url.replace(/([.+?^${}()|[\]\\])/g, '\\$1');
133 // Replace * with a regex pattern (.*) 134 var regexPattern = escapedUrl.replace(/\*/g, '.*'); 135 // Create a regular expression object 136 var regex = new RegExp(`^${regexPattern}$`); 137 } 138 139 if(typeof regex !== 'undefined'){ 140 if (regex.test(page_url)) { 141 eid = key; 142 } 143 } 144 else if((detail.conversion_page_url === page_url) && (detail.conversion_page_url !== undefined) && (detail.conversion_page_url != '')) 145 { 146 eid = key; 147 } 148 else if(current_page.includes(detail.conversion_page_id) || current_page.includes(parseInt(detail.conversion_page_id))) 149 { 150 eid = key; 151 } 152 else 153 { 154 return true; // skip to the next one 155 } 156 157 variation = abstGetCookie('btab_'+eid); 158 159 if(!variation) 160 return true; // skip to the next one 161 162 variation = JSON.parse(variation); 163 164 //skip if its already converted 165 if( variation.conversion == 1 ) 166 return true; // skip to the next one 167 conversionValue = 1; 168 if(window.abst.abConversionValue && detail.use_order_value == true)// woo etc 169 conversionValue = window.abst.abConversionValue; 170 171 bt_experiment_w(eid,variation.variation,'conversion',false,conversionValue); 172 }); 173 174 } 175 176 //foreach experiment 177 if (typeof bt_experiments !== 'undefined') 178 { 179 // check for css classes, then add attributes 180 jQuery("[class^='ab-'],[class*=' ab-']").each(function(e){ 181 if(jQuery(this).attr('class').includes('ab-var-') || jQuery(this).attr('class').includes('ab-convert')) 182 { 183 allClasses = jQuery(this).attr('class'); 184 allClasses = allClasses.split(" "); // into an array 185 thisTestVar = false; 186 thisTestGoal = false;
187 thisTestId = false; 188 thisTestConversion = false; 189 allClasses.forEach(function(element) { 190 191 if(element.startsWith('ab-var-')) 192 thisTestVar = element; 193 else if (element.startsWith('ab-goal-')) 194 thisTestGoal = element; 195 else if(element.startsWith('ab-') && !element.includes('ab-convert') ) 196 thisTestId = element; 197 if(element == 'ab-convert') 198 thisTestConversion = true; 199 200 }); 201 202 if(thisTestVar !== false && thisTestId !== false) 203 { 204 //we've got variations, do ya thing! 205 jQuery(this).attr('bt-eid',thisTestId.replace("ab-","")); 206 jQuery(this).attr('bt-variation',thisTestVar.replace("ab-var-","")); 207 } 208 209 if(thisTestConversion == true && thisTestId !== false) 210 { 211 // it's a conversion, convert! 212 abstConvert(thisTestId.replace("ab-","")); 213 } 214 215 if(thisTestGoal == true && thisTestId !== false) 216 { 217 //its a goal, record it! 218 abstGoal(thisTestId.replace("ab-",""),thisTestGoal.replace("ab-goal-","")); 219 } 220 } 221 222 if(jQuery(this).attr('class').includes('ab-click-convert-')) 223 { 224 jQuery(this).attr('class').split(" ").forEach(function(element) { // loop through all classes 225 if(element.trim().startsWith('ab-click-convert-')) 226 abClickListener(element.replace("ab-click-convert-",""),"."+element); 227 }); 228 } 229 230 }); 231 232 // legacy probably can be removed, check bricks 233 jQuery("[data-bt-variation]").each(function(){ 234 jQuery(this).attr('bt-variation',jQuery(this).attr('data-bt-variation')); 235 jQuery(this).attr('bt-eid',jQuery(this).attr('data-bt-eid')); 236 }); 237 238 //fix bricks child attributes 239 //fix bricks, move attr's up one level 240 document.querySelectorAll(".bricks-element [bt-eid]").forEach(el => { 241 let parent = el.closest('.bricks-element'); 242 parent.setAttribute('bt-eid', el.getAttribute('bt-eid')); 243 parent.setAttribute('bt-variation', el.getAttribute('bt-variation')); 244 el.removeAttribute('bt-eid'); 245 el.removeAttribute('bt-variation'); 246 }); 247 248 let searchParams = new URLSearchParams(window.location.search) 249 const abtv = searchParams.get("abtv"); 250 const abtid = searchParams.get("abtid"); 251 252 //sort experiments by bt_experiments.test_type = full_page, then the rest 253 bt_experiments = Object.entries(bt_experiments).sort((a, b) => { 254 if (a[1].test_type == "full_page") { 255 return -1; 256 } 257 if (b[1].test_type == "full_page") { 258 return 1; 259 } 260 return 0; 261 }).reduce((r, a) => Object.assign(r, { [a[0]]: a[1] }), {}); 262 263 if (abtv && abtid && bt_experiments[abtid]) { 264 console.log('AB Split Test: URL variables detected. Skipping user.'); 265 showSkippedVisitorDefault(abtid,true,abtv); 266 return true; 267 } 268 jQuery.each(bt_experiments, function(experimentId, experiment) { 269 //check for URL variables and then ignore the user 270 271 // if there is a winner, AND the current page is the default page and AUTOCOMPLETE then just go straight there do not pass go 272 if(experiment.test_winner) 273 { 274 if(experiment.test_type == 'full_page' && (experiment.full_page_default_page == btab_vars.post_id)) // if full p and the p is this page 275 { 276 if(experiment.test_winner !== btab_vars.post_id) // if its not the current page 277 { 278 if(experiment.page_variations[experiment.test_winner] == undefined) // if its not defined 279 { 280 console.log('split test winner is not found, not redirecting'); 281 return false; // skip 282 } 283 284 console.log('Split Test winner is page redirect. Redirecting to '+ experiment.page_variations[experiment.test_winner]); 285 window.location.replace( abRedirectUrl(experiment.page_variations[experiment.test_winner])); 286 287 return false;
288 } 289 else // it is the current page so show it 290 { 291 console.log('Test winner is current page. Showing '+ experiment.test_winner); 292 document.body.classList.add('abst-show-page'); 293 return false; // skip 2 next experiment 294 } 295 296 } 297 else if(experiment.test_type == 'magic') 298 { 299 console.log('Magic winner is '+ experiment.test_winner + ' WHAT DO I DO NOW TODO'); 300 } 301 else if(experiment.test_type == 'css_test') 302 { 303 console.log('Split Test CSS winner. Showing '+ experiment.test_winner); 304 jQuery('body').addClass(experiment.test_winner); 305 } // text test winner todo 306 else // on page test 307 { 308 309 //show variation 310 console.log('Split Test winner is on this page. Showing '+ experiment.test_winner); 311 jQuery('*[bt-eid=\"'+experimentId+'\"][bt-variation=\"'+experiment.test_winner+'\"]').addClass('bt-show-variation'); 312 } 313 return true; 314 //skip this experiment 315 } 316 317 if(experiment.test_type == "css_test") 318 { 319 for (var i = 0; i < experiment.css_test_variations; i++) { 320 // Code to be executed for each element 321 jQuery('body').append('<script class="bt-css-scripts" bt-variation="test-css-'+experimentId+'-'+ (i+1)+ '" bt-eid="'+experimentId+'"/>'); 322 } 323 } 324 325 //full page test handler 326 if(experiment.test_type == 'full_page' && experiment.full_page_default_page == btab_vars.post_id) 327 { 328 //add original do nothing variation 329 jQuery('body').append('<div class="bt-redirect-handle" style="display: none !important" bt-variation="'+experiment.full_page_default_page+'" bt-eid="'+experimentId+'"></div>'); 330 //foreach variation 331 jQuery.each(experiment.page_variations,function(varId,variation){ 332 jQuery('body').append('<div class="bt-redirect-handle" style="display: none !important" bt-variation="'+varId+'" bt-eid="'+experimentId+'" bt-url="'+variation+'"></div>'); 333 }); 334 } 335 336 337 //add conversion click handlers 338 if(experiment.conversion_page == 'selector') 339 { 340 if(experiment.conversion_selector != '') 341 { 342 var conversionSelector = experiment.conversion_selector; 343 abClickListener(experimentId,conversionSelector, 0); 344 } 345 } 346 347 348 // if experiment goals exists 349 //loop through 350 if(experiment['goals']) 351 for (var i = 1; i < Object.keys(experiment['goals']).length; i++) { 352 // if experiment.goals[i] key is click 353 //get key name for goal 354 const firstKey = Object.keys(experiment['goals'][i])[0]; 355 356 357 // click selector 358 if(firstKey === 'selector') 359 { 360 abClickListener(experimentId,experiment['goals'][i]['selector'], i); 361 } 362 363 // text subgoall 364 startInverval = false; 365 if(firstKey === 'text') 366 { 367 startInverval = true; 368 369 convstatus = abstGetCookie('btab_'+experimentId); 370 if(startInverval && convstatus) // if test exists and not false 371 { 372 convstatus = JSON.parse(convstatus); 373 if (convstatus && convstatus['goals'] && convstatus['goals'][i] == 1) 374 startInverval = false; 375 } 376 377 if(experiment['goals'][i]['text'] == '') 378 startInverval = false; 379 380 //if text exists and not complete 381 if(startInverval) 382 { 383 startTextWatcher(experimentId,experiment['goals'][i]['text'],i); 384 } 385 } 386 387 if(firstKey === 'page') 388 { 389 var goalPage = experiment['goals'][i]['page']; 390 391 if(goalPage == btab_vars.post_id){ 392 abstGoal(experimentId,i); 393 } 394 } 395 396 if(firstKey === 'url') 397 { 398 page_url = normalizeUrl(window.location.href); 399 400 goal_url = normalizeUrl(experiment['goals'][i]['url']); 401 402 if(page_url == goal_url){ 403 abstGoal(experimentId,i); 404 } 405 406 } 407 408 } 409 410 411 412 //text conversion 413 if(experiment.conversion_page == 'text') 414 { 415 startInverval = true; 416 if(!bt_experiments[experimentId]) 417 startInverval = false; // not if not defined 418
419 convstatus = abstGetCookie('btab_'+experimentId); 420 if(startInverval && convstatus) // if test exists and not false 421 { 422 convstatus = JSON.parse(convstatus); 423 if(convstatus.conversion == 1) // if its converted 424 startInverval = false; 425 } 426 427 if(experiment.conversion_text == '') 428 startInverval = false; 429 430 //if text exists and not complete 431 if(startInverval) 432 { 433 startTextWatcher(experimentId,experiment.conversion_text); 434 } 435 } 436 437 //text subgoals 438 439 440 441 442 if(experiment.conversion_page == 'surecart-order-paid' && window.scData ) // if surecart is slected and surecart js is detected 443 { 444 document.addEventListener('scOrderPaid', function(e) { // add listener 445 console.log('surecart OrderPaid'); 446 const checkout = e.detail; 447 if( checkout && checkout.amount_due ) { 448 if( experiment.use_order_value == true) 449 window.abst.abConversionValue = (checkout.amount_due/100).toFixed(2); // set value 450 abstConvert(experimentId, window.abst.abConversionValue); 451 } 452 }); 453 } 454 455 456 if( experiment.conversion_page == 'fingerprint' && !localStorage.getItem("ab-uuid") ) { 457 console.log("ab-uuid: set fingerprint"); 458 setAbFingerprint(); 459 } 460 461 }); 462 463 464 var experiments_el = jQuery('[bt-eid]:not([bt-eid=""])[bt-variation]:not([bt-variation=""])'); 465 var current_exp = {}; 466 var exp_redirect = {}; 467 468 experiments_el.each(function(e) { 469 var me = jQuery(this), 470 experimentId = me.attr('bt-eid'), 471 variation = me.attr('bt-variation'), 472 redirect_url = me.attr('bt-url'); 473 474 if( current_exp[experimentId] === undefined ) { 475 current_exp[experimentId] = []; 476 exp_redirect[experimentId] = []; 477 } 478 if (!current_exp[experimentId].includes(variation)) { 479 current_exp[experimentId].push(variation); 480 exp_redirect[experimentId][variation] = redirect_url; 481 } 482 483 }); 484 485 // add css tests to current exp 486 jQuery.each(bt_experiments, function(experimentId, value) { 487 // Code to be executed for each element 488 if(bt_experiments[experimentId]['test_type'] == 'css_test') 489 { 490 current_exp[experimentId] = []; // create 491 exp_redirect[experimentId] = []; 492 493 for (var i = 1; i <= parseInt(bt_experiments[experimentId]['css_test_variations']); i++) { 494 current_exp[experimentId].push('test-css-'+experimentId+'-'+i); 495 exp_redirect[experimentId]['test-css-'+experimentId+'-'+i] = ''; 496 } 497 } 498 else if(bt_experiments[experimentId]['test_type'] == 'magic') 499 { 500 if(bt_experiments[experimentId]['magic_definition'] && bt_experiments[experimentId]['magic_definition'].length > 0){ 501 magic_definition = JSON.parse(bt_experiments[experimentId]['magic_definition']); 502 current_exp[experimentId] = []; // create 503 exp_redirect[experimentId] = []; 504 var randVar = getRandomInt(0, magic_definition[0].variations.length - 1); 505 current_exp[experimentId].push('test-magic-'+experimentId+'-'+randVar); 506 exp_redirect[experimentId]['test-magic-'+experimentId+'-'+randVar] = ''; 507 } 508 } 509 }); 510 511 jQuery.each(current_exp, function(experimentId, variations) { 512 //check it exists 513 if( bt_experiments[experimentId] === undefined ) { 514 console.log("ABST: " +'Test ID '+ experimentId +' does not exist.'); 515 showSkippedVisitorDefault(experimentId); 516 return true; // continue to next exp 517 } 518 519 520 521 // if there is a winner for the test, then do no more - its already done above 522 if( bt_experiments[experimentId].test_winner) 523 return true; // continue to next exp 524 525 /* 526 //if cookie consent isnt done, then show default 527 if ('BorlabsCookie' in window) 528 { 529 // borlabs v2 530 if (typeof window.BorlabsCookie.checkCookieConsent === 'function' && 531 !window.BorlabsCookie.checkCookieConsent('split-test') && 532 !window.BorlabsCookie.checkCookieConsent('ab-split-test')) 533 { 534 console.log('Borlabs cookie consent prevented a split test from running'); 535 showSkippedVisitorDefault(experimentId); 536 return true; // continue to next exp 537 } 538 //borlabs v3 539 if(typeof window.BorlabsCookie.Consents.hasConsent === 'function') 540 { 541 if(!window.BorlabsCookie.Consents.hasConsent('split-test') && !window.BorlabsCookie.Consents.hasConsent('ab-split-test') ) 542 { 543 console.log('Borlabs v3 cookie consent prevented a split test from running'); 544 showSkippedVisitorDefault(experimentId); 545 return true; // continue to next exp 546 } 547 } 548 } 549 */ 550 551 552 if( bt_experiments[experimentId]['is_current_user_track'] == false ) { 553 showSkippedVisitorDefault(experimentId); 554 return true; // continue to next exp 555 } 556 557 // if the test is not published 558 if( bt_experiments[experimentId]['test_status'] !== 'publish' ) { 559 showSkippedVisitorDefault(experimentId); 560 return true; // continue to next exp 561 } 562 563 var targetVisitor = true; 564 565 var btab = abstGetCookie('btab_'+ experimentId); 566 567 var experimentVariation = ''; 568 569 if( !btab ) // no existing data, create 570 { 571 if(bt_experiments[experimentId]['test_type'] == 'css_test'){ 572 var randVar = getRandomInt(1, parseInt(bt_experiments[experimentId]['css_test_variations'])) - 1; 573 } 574 else if(bt_experiments[experimentId]['test_type'] == 'magic' && bt_experiments[experimentId]['magic_definition'] && bt_experiments[experimentId]['magic_definition'].length > 0){ 575 magic_definition = JSON.parse(bt_experiments[experimentId]['magic_definition']); 576 //console.log(magic_definition[0].variations); 577 var randVar = getRandomInt(0, magic_definition[0].variations.length - 1); 578 variations = magic_definition[0].variations 579 } 580 else 581 var randVar = getRandomInt(0, variations.length - 1); 582 583 584 if(btab_vars.is_free == '1' && variations.length > 2){ 585 var randVar = getRandomInt(0, 1); //limit to 2 586 console.log('Free version of AB Split Test is limited to 1 variation. Your others will not be shown. Upgrade: https://absplittest.com/pricing?ref=ug'); 587 } 588 589 experimentVariation = variations[randVar]; 590 591 592 } 593 else //parse existing data 594 { 595 596 try { 597 var btab_cookie = JSON.parse(btab); 598 experimentVariation = btab_cookie.variation; 599 } catch (err) { 600 console.log('Error parsing cookie data:', err); 601 } 602 } 603 604 var variation_element = false;
605 if(bt_experiments[experimentId]['test_type'] == 'css_test') 606 { 607 jQuery('body').addClass(experimentVariation); 608 } 609 else if(bt_experiments[experimentId]['test_type'] == 'magic') 610 { 611 } 612 else // on page tests 613 { 614 variation_element = jQuery('*[bt-eid=\"'+experimentId+'\"][bt-variation=\"'+experimentVariation+'\"]'); 615 } 616 617 if( btab ){ // if we have a cookie 618 btab = JSON.parse( btab ); 619 var redirect_url = exp_redirect[experimentId]; 620 redirect_url = redirect_url[experimentVariation]; 621 if( redirect_url && !btab_vars.is_preview ) { 622 window.location.replace( abRedirectUrl( redirect_url) ); 623 return true; // finished 624 } 625 else 626 { 627 abstShowPage(); // full page 628 if(variation_element) 629 variation_element.addClass('bt-show-variation'); // on page 630 631 if(bt_experiments[experimentId]['test_type'] == 'magic') 632 { 633 magic_definition = JSON.parse(bt_experiments[experimentId]['magic_definition']); 634 635 magic_definition.forEach((element, index) => { 636 magicSelector = jQuery(element.selector); 637 var vartn = btab.variation.replace('magic-', ''); 638 if(element.type == 'text') 639 { 640 magicSelector.text(element.variations[vartn]); 641 console.log('swapped text '); 642 } 643 else if(element.type == 'image') 644 { 645 magicSelector.attr('src', element.variations[vartn]).attr('srcset', element.variations[vartn]); 646 console.log('swapped img'); 647 } 648 }); 649 } 650 } 651 return true; // continue to next exp 652 } 653 654 if( !btab ) { // new user, check targeting 655 var targetPercentage = bt_experiments[experimentId].target_percentage; 656 657 if(targetPercentage == '') 658 targetPercentage = 100; 659 660 var url_query = bt_experiments[experimentId].url_query; 661 urlQueryResult = null; 662 663 //we've got a url query 664 if(url_query !== '') 665 { 666 if(url_query.includes('*')) 667 { 668 console.log('wildcard search the entire URL ' + url_query); 669 // wildcard search the entire URL 670 //remove the * 671 url_query = url_query.replace(/\*/g,''); 672 console.log('url_query',url_query); 673 console.log('window.location.href',window.location.href); 674 targetVisitor = window.location.href.includes(url_query); 675 // if href includes string 676 console.log('targetVisitor',targetVisitor); 677 } 678 else 679 { 680 var exploded_query = url_query.trim().split("="); 681 if(exploded_query.length == 1) // just the query key 682 { 683 targetVisitor = bt_getQueryVariable(exploded_query[0]); 684 } 685 else if(exploded_query.length == 2) //query key and value 686 { 687 urlQueryResult = bt_getQueryVariable(exploded_query[0]); 688 targetVisitor = exploded_query[1] == urlQueryResult; 689 } // else if the string contains an * then we are going to wildcard search the entire URL 690 } 691 } 692 693 var target_option_device_size = bt_experiments[experimentId].target_option_device_size; 694 695 if(targetVisitor && target_option_device_size != 'all') 696 { 697 var device_size = getDeviceSize(jQuery(window).width()); 698 699 targetVisitor = target_option_device_size.includes(device_size); 700 701 } 702 703 704 if(!targetVisitor) 705 { 706 showSkippedVisitorDefault(experimentId); 707 return true; // continue to next exp 708 } 709 // randomly target users according to percentage 710 var percentage = getRandomInt(1,100); 711 if(targetPercentage < percentage) 712 { 713 showSkippedVisitorDefault(experimentId,true); 714 console.log('ABST ' + experimentId+' skipped not in percentage target'); 715 return true; // continue to next exp 716 } 717 718 // no experiment cookie set, calculate and create 719 bt_experiments[experimentId].variations = bt_get_variations(experimentId); 720 } 721 722 if(variation_element && !variation_element.length) 723 { 724 showSkippedVisitorDefault(experimentId); 725 console.log('ABST variation doesnt exist, or doesnt match'); 726 return true; // continue to next exp 727 } 728 729 if (Object.keys(exp_redirect).length > 0) 730 { 731 redirect_url = exp_redirect[experimentId]; 732 redirect_url = redirect_url[experimentVariation]; 733 } 734 else 735 redirect_url = ''; 736 737 // if its css, add it to body 738 739 if(bt_experiments[experimentId]['test_type'] == 'magic') 740 { 741 magic_definition.forEach((element, index) => { 742 magicSelector = jQuery(element.selector); 743 if(element.type == 'text') 744 { 745 magicSelector.text(magic_definition[index].variations[parseInt(randVar)]); 746 } 747 else if(element.type == 'image') 748 { 749 magicSelector.attr('src', magic_definition[index].variations[parseInt(randVar)]).attr('srcset', magic_definition[index].variations[parseInt(randVar)]); 750 } 751 experimentVariation = 'magic-' + randVar; 752 }); 753 } 754 755 if(variation_element) 756 variation_element.addClass('bt-show-variation'); 757 758 759 /// if its magic or on page, then set up visible listeners 760 if(bt_experiments[experimentId]['test_type'] == 'ab_test') 761 { 762 watch_for_tag_event(experimentId, 'experiment', experimentVariation); // watch 4 visible instead of insta visit 763 } 764 else if (bt_experiments[experimentId]['test_type'] == 'magic') 765 { 766 //loop through the magic elements and watch for them 767 magic_definition.forEach((element, index) => { 768 watch_for_tag_event(experimentId,element.selector, experimentVariation); // watch 4 visible instead of insta visit 769 }); 770 } 771 else 772 { 773 bt_experiment_w(experimentId,experimentVariation,'visit', redirect_url); 774 } 775 776 777 }); 778 } 779 780 // warn users on localhost 781 if(btIsLocalhost()) 782 console.info("AB Split Test: It looks like you're on a localhost, using local storage instead of cookies. External Conversion Pixels will not work on Local web servers."); 783 784 abst_find_analytics(); 785 window.dispatchEvent(new Event('resize')); // trigger a window resize event. Useful for sliders etc. that dynamically resize 786 jQuery('body').trigger('ab-test-setup-complete'); // the end of the ab testing. do other things if you like with jQuery('body').bind('ab-test-setup-complete', function() { //do what you want! }); 787 //add class ab-test-setup-complete to body 788 document.body.classList.add('ab-test-setup-complete'); 789}); 790 791function startTextWatcher(experimentId,word,goalId = null){ 792 793 if(!goalId) 794 goalId = 0; 795 796 if (typeof abst.intervals[experimentId] === 'undefined') { 797 abst.intervals[experimentId] = {}; 798 } 799 800 window.abst.intervals[experimentId][goalId] = setInterval(function () { 801 let found = false;
802 var escapedWord = word.replace(/'/g, "\\'"); // Escape single quotes 803 804 805 // Search within the main page more efficiently 806 jQuery("body").find(":contains('" + escapedWord + "')").each(function () { 807 if (jQuery(this).text().indexOf(word) >= 0 && jQuery(this).is(":visible")) { 808 found = true; 809 return false; // Break the loop 810 } 811 }); 812 813 if (!found) { 814 jQuery("iframe").each(function () { 815 try { 816 var iframeBody = jQuery(this).contents().find("body"); 817 found = iframeBody.find(":contains('" + escapedWord + "')").filter(function () { 818 return jQuery(this).text().indexOf(word) >= 0 && jQuery(this).is(":visible"); 819 }).length > 0; 820 821 if (found){ 822 console.log('ABST: ' + experimentId + ' ' + word); 823 return false; // Break the loop 824 } 825 } catch (error) { 826 //console.error("Error accessing iframe contents:", error); 827 } 828 }); 829 } 830 831 if(found){ 832 if(goalId == 0) 833 { 834 abstConvert(experimentId); 835 } 836 else 837 { 838 abstGoal(experimentId,goalId); 839 } 840 clearInterval(window.abst.intervals[experimentId][goalId]); // Stop the interval after finding the word 841 } 842 }, 1000); 843 } 844 845 846 function abstConvert(testId = '',orderValue = 1){ 847 console.log('abstConvert',testId,orderValue); 848 if(testId !== '') 849 { 850 var btab = abstGetCookie('btab_'+ testId); 851 if(btab) 852 { 853 btab = JSON.parse(btab); 854 if(btab.conversion == 0) 855 { 856 bt_experiment_w(testId,btab.variation,'conversion',false,orderValue); 857 btab.conversion = 1; 858 experiment_vars = JSON.stringify(btab); 859 abstSetCookie('btab_'+ testId, experiment_vars, 1000); 860 if(abst.intervals[testId]){ 861 clearInterval(abst.intervals[testId]); 862 } 863 } 864 else 865 { 866 console.log("ABST: " +bt_experiments[testId].name + ': Visitor has already converted'); 867 } 868 } 869 else 870 { 871 if(!bt_experiments[testId]) 872 console.log("ABST: " +'Test ID not found or test not active'); 873 } 874 } 875 } 876 877 function abstGoal(testId = '',goal = ''){ 878 // console.log('start abstGoal',testId,goal); 879 if(testId !== '' && goal !== '') 880 { 881 var btab = abstGetCookie('btab_'+ testId); 882 if(btab) 883 { 884 btab = JSON.parse(btab); 885 //console.log('btab',btab); 886 if(btab.conversion !== 1) // no goals after conversion 887 { 888 if(Array.isArray(btab.goals)) 889 { 890 //if its not in the goal 891 if(btab.goals[goal] !== 0) 892 { 893 btab.goals[goal] = 1; 894 bt_experiment_w(testId,btab.variation,goal,false,1); 895 } 896 897 experiment_vars = JSON.stringify(btab); 898 abstSetCookie('btab_'+ testId, experiment_vars, 1000); 899 if(abst.intervals[testId+""+goal]) 900 { 901 clearInterval(abst.intervals[testId+""+goal]); 902 } 903 } 904 else 905 { 906 console.log("ABST: " +bt_experiments[testId].name + ': Visitor has already goaled'); 907 } 908 } 909 //else 910 //console.log('no goals after conversion'); 911 } 912 else 913 { 914 if(!bt_experiments[testId]) 915 console.log("ABST: " +'Test ID not found or test not active'); 916 } 917 } 918 } 919 920function showSkippedVisitorDefault(eid,createCookie = false,variation = false){ 921 if(!window.bt_experiments[eid]){ // if no experiment, show first variations 922 923 btv = jQuery('*[bt-eid=\"'+eid+'\"]').first().attr('bt-variation'); 924 jQuery('*[bt-eid=\"'+eid+'\"][bt-variation=\"'+btv+'\"]').addClass('bt-show-variation'); 925 return true; 926 } 927 928 if(variation && eid) // if we have a variation passed, just do it 929 { 930 if(bt_experiments[eid].test_type == "css_test") // css version 1 931 { 932 jQuery("body").addClass(variation); // add 1 class 933 } 934 else if (bt_experiments[eid].test_type == "full_page") // full page 935 { 936 url = bt_experiments[eid].page_variations[variation]; 937 if(url !== undefined) 938 { 939 window.location.replace( abRedirectUrl(url) ); // follow the link w search params 940 return true; 941 } 942 else 943 { 944 console.log('No matching page found, must be default page no redirect.'); 945 //add show class to page 946 document.body.classList.add('abst-show-page'); 947 return false;
948 } 949 } 950 else if (bt_experiments[eid].test_type == "magic") 951 { 952 console.log('magic test, showing ver ' + variation); 953 // Clean up the variation string and get the letter 954 const letter = variation.replace('Variation ','').replace('(Original)','').trim().toLowerCase(); 955 956 // Safety check - make sure we have a single letter 957 if (letter.length !== 1 || !/[a-z]/.test(letter)) { 958 console.error('Invalid variation format:', variation); 959 return false; 960 } 961 962 // Convert letter to index (a=0, b=1, etc) 963 const magVar = letter.charCodeAt(0) - 'a'.charCodeAt(0); 964 965 // Verify the index is in valid range 966 if (magVar < 0 || magVar > 25) { 967 console.error('Invalid variation letter:', letter); 968 return false; 969 } 970 971 showMagicTest(eid, magVar); 972 } 973 else // on page 974 { 975 jQuery('*[bt-eid=\"'+eid+'\"][bt-variation=\"'+variation+'\"]').addClass('bt-show-variation'); 976 } 977 abstShowPage(); 978 if(createCookie) 979 { 980 skippedCookie(eid,variation); 981 } 982 return true; 983 } 984 985 if(bt_experiments[eid].test_winner !== ''){ // if we have a winner 986 987 if(bt_experiments[eid].test_type =="full_page") // full page winner 988 { 989 url = bt_experiments[eid].page_variations[bt_experiments[eid].test_winner]; 990 if(url !== undefined) 991 { 992 window.location.replace( abRedirectUrl(url) ); // follow the link w search params 993 994 return true; 995 } 996 else 997 { 998 console.log("ABST: " +'Full page test complete without matching page winner. Showing current page.'); 999 } 1000 abstShowPage(); 1001 } 1002 1003 if(bt_experiments[eid].test_type == "css_test") // css winner 1004 { 1005 console.log('css test winner, showing ver ' + bt_experiments[eid]['test_winner']); 1006 jQuery("body").addClass('test-css-'+eid+'-'+bt_experiments[eid]['test_winner']); // add winning class 1007 return true; // next 1008 } 1009 1010 if(bt_experiments[eid].test_type == "magic") // magic winner 1011 { 1012 console.log('magic test winner, showing ver ' + bt_experiments[eid]['test_winner']); 1013 showMagicTest(eid, bt_experiments[eid]['test_winner']); 1014 return true; // next 1015 } 1016 } 1017 1018 if(bt_experiments[eid].test_type == "full_page") 1019 { 1020 abstShowPage(); 1021 if(createCookie) 1022 skippedCookie(eid,bt_experiments[eid].full_page_default_page); 1023 1024 return true; // next 1025 } 1026 1027 if(bt_experiments[eid].test_type == "css_test") // css version 1 1028 { 1029 jQuery("body").addClass('test-css-'+eid+'-1'); // add 1 class 1030 if(createCookie) 1031 skippedCookie(eid,'test-css-'+eid+'-1'); 1032 return true; // next 1033 } 1034 1035 //on page tests only from here 1036 1037 if(!eid) 1038 return; 1039 var foundSpecial = false;
1040 jQuery('*[bt-eid=\"'+eid+'\"]').each(function(index, element){ 1041 var variationName = jQuery(element).attr('bt-variation').toLowerCase(); 1042 var defaultNames = ["original","one","1","default","standard","a","control"]; 1043 if(defaultNames.includes(variationName)) 1044 { 1045 btv = variationName; 1046 jQuery(element).addClass('bt-show-variation'); // show the matched element 1047 foundSpecial = true; 1048 } 1049 }); 1050 if(!foundSpecial){ 1051 btv = jQuery('*[bt-eid=\"'+eid+'\"]').first().attr('bt-variation'); 1052 jQuery('*[bt-eid=\"'+eid+'\"][bt-variation=\"'+btv+'\"]').addClass('bt-show-variation'); 1053 } 1054 1055 if(createCookie) 1056 skippedCookie(eid,btv); 1057 1058} 1059function skippedCookie(eid,btv){ 1060 var experiment_vars = { 1061 eid: eid, 1062 variation: btv, 1063 conversion: 1, 1064 skipped:1 1065 }; 1066 experiment_vars = JSON.stringify(experiment_vars); 1067 abstSetCookie('btab_'+ eid, experiment_vars, 1000); 1068 return true; 1069} 1070 1071//takes input slug or url and ends url suitable for window/replace 1072function abRedirectUrl(url){ 1073 1074 url = url + window.location.search + window.location.hash; 1075 // if it starts with http/s dop nothing 1076 if(url.startsWith('http') || url.startsWith('/')) 1077 return url; 1078 else 1079 return '/' + url; 1080 1081} 1082 1083function abstOneSecond(){ 1084 if(Object.keys(window.abst.timer).length > 0) 1085 { 1086 Object.entries(window.abst.timer).forEach(([ index,item]) => { 1087 if(window.abst.timer[index] > -1) // dont decrease below -1 1088 window.abst.timer[index] = window.abst.timer[index] - 1; 1089 1090 if(window.abst.timer[index] == 0){ // convert if its counted down, only fired once 1091 1092 //console.log('time active converting ' + index); 1093 //index could be goal-eid-goalid 1094 if(index.includes('goal-')) 1095 { 1096 var parts = index.split('-'); 1097 abstGoal(parts[1],parts[2]); 1098 } 1099 else 1100 abstConvert(index); 1101 1102 } 1103 }); 1104 1105 // update localstorage 1106 localStorage.setItem('absttimer', JSON.stringify(window.abst.timer)); // localstorage need strings 1107 } 1108} 1109 1110 1111 1112function userActiveNow(){ 1113 window.abst.currscroll = window.scrollY; // update last known scroll n mouse 1114 window.abst.abactive = true; // we active 1115 clearTimeout(window.abst.timeoutTimer); // delete the old timout 1116 window.abst.timeoutTimer = setTimeout(abstActiveTimeout, window.abst.timeoutTime); // create new timeout 1117} 1118 1119function abstActiveTimeout(){ 1120 window.abst.abactive = false; 1121} 1122 1123 1124function getRandomInt(min, max) { 1125 min = Math.ceil(min); 1126 max = Math.floor(max); 1127 return Math.floor(Math.random() * (max - min + 1)) + min; 1128} 1129 1130function abstSetCookie(c_name, value, exdays) { 1131 if(btIsLocalhost()) 1132 return btSetLocal(c_name, value); 1133 1134 var hostname = window.location.hostname; 1135 var domain = hostname.replace(/^www\./, '').split('.').slice(-2).join('.'); 1136 domain = '.' + domain; 1137 1138 var exdate = new Date(); 1139 exdate.setDate(exdate.getDate() + exdays); 1140 var c_value = escape(value) + ((exdays == null) ? '' : ';path=/; expires=' + exdate.toUTCString()) + "; SameSite=None; Secure; domain=" + domain; 1141 1142 document.cookie = c_name + '=' + c_value; 1143} 1144function deleteCookie(c_name) { 1145 if(btIsLocalhost()) 1146 return btDeleteLocal(c_name); 1147 1148 document.cookie = c_name+"= ; expires = Thu, 01 Jan 1970 00:00:00 GMT"; 1149} 1150 1151function abstGetCookie(c_name) { 1152 if(btIsLocalhost()) 1153 return btGetLocal(c_name); 1154 1155 var i, x, y, ARRcookies = document.cookie.split(';'); 1156 for (i = 0; i < ARRcookies.length; i++) { 1157 x = ARRcookies[i].substr(0, ARRcookies[i].indexOf('=')); 1158 y = ARRcookies[i].substr(ARRcookies[i].indexOf('=') + 1); 1159 x = x.replace(/^\s+|\s+$/g, ''); 1160 if (x == c_name) { 1161 return unescape(y); 1162 } 1163 } 1164} 1165 1166function abstShowPage(){ 1167 jQuery("body").addClass('abst-show-page'); 1168 jQuery("body",parent.window.document).addClass('abst-show-page'); // bb iframe UI 1169} 1170 1171function btSetLocal(c_name,value){ 1172 localStorage.setItem(c_name, value); 1173} 1174 1175function btGetLocal(c_name){ 1176 return localStorage.getItem(c_name); 1177} 1178 1179function btDeleteLocal(c_name){ 1180 localStorage.removeItem(c_name); 1181} 1182 1183function btIsLocalhost(){ 1184 return (location.hostname === "localhost" || location.hostname === "127.0.0.1"|| location.hostname.endsWith(".local")|| location.hostname.endsWith(".test")); 1185} 1186 1187function bt_get_variations(eid){ 1188 1189 variation = [];
1190 jQuery('[bt-eid=' + eid + ']').each(function (index, val) { 1191 var newVariation = jQuery(this).attr('bt-variation'); 1192 1193 // Check if the variation already exists in the array 1194 if (variation.indexOf(newVariation) === -1) { 1195 variation.push(newVariation); 1196 } 1197 }); 1198 1199 if(btab_vars.is_free == '1') 1200 { 1201 //only return first 2 variations 1202 variation = variation.slice(0,2); 1203 } 1204 1205 return variation; 1206} 1207 1208function bt_experiment_w(eid,variation,type, url,orderValue=1){ 1209 1210 // dont log it if its a skipper or malformed 1211 if(variation == '_bt_skip_' || btab_vars.is_preview || !eid || !variation) 1212 { 1213 return true; 1214 } 1215 1216 // if its a fingerprinter and we dont have a uuid, then wait for it 1217 if(bt_experiments[eid].conversion_page == 'fingerprint' && !localStorage.getItem('ab-uuid')) 1218 { 1219 console.log("bt_exp_w: waiting for fingerprint"); 1220 setTimeout(bt_experiment_w, 500, eid, variation, type, url,orderValue); 1221 return true; //back in 500ms 1222 } 1223 1224 var data = { 1225 'action': 'bt_experiment_w', 1226 'eid' : eid, 1227 'variation' : variation, 1228 'type' : type, 1229 'size' : abst.size, 1230 'location' : btab_vars.post_id, 1231 'orderValue': orderValue 1232 }; 1233 1234 var experiment_vars = { 1235 eid: eid, 1236 variation: variation, 1237 conversion: 0, 1238 goals:[], 1239 }; 1240 1241//todo mote to on seen watcher 1242 //visit time watcher 1243 if( type == 'visit' && bt_experiments[eid]['conversion_page'] == 'time') 1244 { 1245 window.abst.timer[eid] = bt_experiments[eid]['conversion_time']; 1246 } 1247 //goal time watcher 1248 if( type == 'visit') 1249 { 1250 // console.log(bt_experiments[eid]['goals']); 1251 //foreach goals 1252 jQuery.each(bt_experiments[eid]['goals'], function(key, value) { 1253 // if valuee.time is set 1254 if(value['time'] > 0) 1255 { 1256 window.abst.timer["goal-"+eid + "-"+key] = value['time']; 1257 } 1258 }); 1259 } 1260 1261 // set up conversion 1262 if( type == 'conversion' ) 1263 experiment_vars.conversion = 1; 1264 else if ( type == 'visit' ) 1265 { 1266 // not conversion or goal 1267 } 1268 else // goal 1269 { 1270 experiment_vars.goals[type] = 1; // add to goals list 1271 } 1272 1273 1274 //add uuid if necessary 1275 if( bt_experiments[eid]['conversion_page'] == 'fingerprint') 1276 data.uuid = localStorage.getItem('ab-uuid'); 1277 1278
1279 //add advanced id if necessary 1280 if( btab_vars.advanced_tracking == '1' ) 1281 data.ab_advanced_id = localStorage.getItem('ab-advanced-id'); 1282 1283 experiment_vars = JSON.stringify(experiment_vars); 1284 // For non-redirect events, check if we're waiting for approval 1285 if (window.abst.waitForApproval) { 1286 queueEventData(data, url); 1287 } else { 1288 // Process normally if not waiting for approval 1289 // Send beacon 1290 console.log('sent beacon', data); 1291 navigator.sendBeacon(bt_ajaxurl+"?action=bt_experiment_w&method=beacon", JSON.stringify(data)); 1292 } 1293 abstSetCookie('btab_'+ eid, experiment_vars, 1000); 1294 //show the page if its not a redirect or external 1295 if(url && url !== "ex") 1296 { 1297 addServerEvents({ 1298 eid: eid, 1299 variation: variation, 1300 type: 'visit', 1301 }); 1302 window.location.replace(abRedirectUrl(url)); 1303 } 1304 else 1305 { 1306 btab_track_event(data); // analytics tagger 1307 abstShowPage(); // show the page 1308 } 1309 1310 return true; 1311} 1312 1313/** 1314 * Helper function to queue event data in localStorage 1315 */ 1316function queueEventData(data, url) { 1317 // Add to queue in localStorage 1318 const queueData = { 1319 data: data, 1320 timestamp: new Date().getTime(), 1321 url: url 1322 }; 1323 1324 // Get existing queue or initialize empty array 1325 let queue = []; 1326 try { 1327 const queueString = localStorage.getItem('abstTestDataQueue'); 1328 if (queueString) { 1329 queue = JSON.parse(queueString); 1330 } 1331 } catch (e) { 1332 console.error('Error parsing abstTestDataQueue', e); 1333 } 1334 1335 // Add new item to queue 1336 queue.push(queueData); 1337 1338 // Save updated queue 1339 localStorage.setItem('abstTestDataQueue', JSON.stringify(queue)); 1340 1341 console.log('Event queued for approval', queueData); 1342} 1343 1344/** 1345 * Process events when approval is given 1346 * This function should be called when cookie consent or other approval is given 1347 */ 1348function abst_process_approved_events() { 1349 // Set approval flag to true and save to localStorage 1350 setAbstApprovalStatus(true); 1351 1352 // Get queued events 1353 let queue = []; 1354 try { 1355 const queueString = localStorage.getItem('abstTestDataQueue'); 1356 if (queueString) { 1357 queue = JSON.parse(queueString); 1358 } 1359 } catch (e) { 1360 console.error('Error parsing abstTestDataQueue', e); 1361 return false; 1362 } 1363 1364 if (!queue.length) { 1365 console.log('No queued events to process'); 1366 return true; 1367 } 1368 1369 console.log('Processing ' + queue.length + ' queued events'); 1370 1371 // Process each queued event 1372 queue.forEach(queueItem => { 1373 // Track the event 1374 btab_track_event(queueItem.data); 1375 1376 // Send beacon for the event 1377 console.log('Sending queued beacon', queueItem.data); 1378 navigator.sendBeacon(bt_ajaxurl+"?action=bt_experiment_w&method=beacon", JSON.stringify(queueItem.data)); 1379 }); 1380 1381 // Clear the queue 1382 localStorage.removeItem('abstTestDataQueue'); 1383 1384 return true; 1385} 1386 1387/** 1388 * Track an event 1389 * This function is used to track events in the analytics partners 1390 * @param {Object} data - The event data 1391 * @param {string} data.eid - The experiment ID 1392 * @param {string} data.variation - The variation 1393 * @param {string} data.type - The event type 1394 */ 1395async function btab_track_event(data){ 1396 1397 //create javascript event with data 1398 jQuery('body').trigger('abst_event', [data]); 1399 1400 if(btab_vars.is_free == '1') 1401 return false; 1402 1403 if(btab_vars.tagging == '0'){ 1404 //console.log('event tagging turned off'); 1405 return false; 1406 } 1407 1408 window.abst.eventQueue.push(data); 1409 1410 trackName = bt_experiments[data.eid].name || data.eid; 1411 //gtag always 1412 gtm_data = { 1413 'event': 'ab_split_test', 1414 'test_name': trackName, 1415 'test_variation': data.variation, 1416 'test_event': data.type, 1417 'test_id': data.eid, 1418 }; 1419 if( bt_experiments[data.eid]['conversion_page'] == 'fingerprint') 1420 gtm_data.abuuid = localStorage.getItem('ab-uuid'); 1421 else if(localStorage.getItem('ab-advanced-id')) 1422 gtm_data.abuuid = localStorage.getItem('ab-advanced-id'); 1423 1424 window.dataLayer = window.dataLayer || []; 1425 window.dataLayer.push(gtm_data); // add to gtm data layer 1426 1427 if(window.abst.abconvertpartner.ga4) //ga4 add 1428 gtag('event', 'ab_split_test', { 1429 'test_name': data.eid, 1430 'test_variation': data.variation, 1431 'test_event': data.type, 1432 }); 1433 1434 1435 if(window.abst.abconvertpartner.abawp){ //analyticswp 1436 AnalyticsWP.event('Test: ' + bt_experiments[data.eid].name, { 1437 test_id: data.eid, 1438 test_name: bt_experiments[data.eid].name, 1439 test_variation: data.variation, 1440 test_visit_type: data.type 1441 }); 1442 } 1443 1444 if(window.abst.abconvertpartner.clarity){ //clarity 1445 clarity("set", bt_experiments[data.eid].name + "-" + data.type, data.variation); 1446 } 1447 1448 if(window.abst.abconvertpartner.gai){ //google analytics 1449 tracker = ga.getAll()[0]; 1450 if (tracker) 1451 { 1452 tracker.send("event", bt_experiments[data.eid].name, data.type, data.variation,{nonInteraction: true}); // send non interactive event to GA 1453 window.abst.abconvertpartner.gai = true; 1454 } 1455 } 1456 1457 if(window.abst.abconvertpartner.abmix){ //abmix 1458 mixpanel.track(bt_experiments[data.eid].name, {'type': data.type,'variation':data.variation}, {send_immediately: true}); 1459 } 1460 1461 if(window.abst.abconvertpartner.abumav){ //umami 1462 usermaven("track", bt_experiments[data.eid].name, { 1463 type: data.type, 1464 variation: data.variation 1465 }); 1466 1467 } 1468 1469 if(window.abst.abconvertpartner.umami){ //umami 1470 umami.track(bt_experiments[data.eid].name, { 1471 type: data.type, 1472 variation: data.variation 1473 }); 1474 } 1475 1476 if(window.abst.abconvertpartner.cabin){ //cabin 1477 cabin.event(bt_experiments[data.eid].name + ' | ' + data.type + ' | ' + data.variation); 1478 } 1479 1480 if(window.abst.abconvertpartner.plausible){ //plausible 1481 plausible(bt_experiments[data.eid].name, { 1482 props: { 1483 type: data.type, 1484 variation: data.variation 1485 } 1486 }); 1487 } 1488 1489 if(window.abst.abconvertpartner.fathom){ //fathom 1490 fathom.trackGoal(bt_experiments[data.eid].name, { 1491 type: data.type, 1492 variation: data.variation 1493 }); 1494 } 1495 if(window.abst.abconvertpartner.posthog){ 1496 posthog.capture(bt_experiments[data.eid].name, { 1497 type: data.type, 1498 variation: data.variation 1499 }); 1500 } 1501} 1502 1503function abst_find_analytics(){ 1504 if(btab_vars.is_free == '1') 1505 return false;
1506 1507 window.abeventstarted = new Date().getTime(); 1508 1509 window.dataLayer || (window.dataLayer = []); //gtag 1510 1511 // Define all analytics providers we want to check for 1512 const analyticsProviders = [ 1513 { 1514 name: 'ga4', 1515 check: () => typeof gtag === "function", 1516 process: () => { 1517 window.abst.eventQueue.forEach(element => { 1518 gtag('event', 'ab_split_test', { 1519 'test_name': bt_experiments[element.eid].name, 1520 'test_variation': element.variation, 1521 'test_event': element.type, 1522 'ab_uuid': element.uuid, 1523 }); 1524 }); 1525 } 1526 }, 1527 { 1528 name: 'clarity', 1529 check: () => typeof clarity === "function", 1530 process: () => { 1531 window.abst.eventQueue.forEach(element => { 1532 clarity("set", element.eid + "-" + element.type, element.variation); 1533 }); 1534 } 1535 }, 1536 { 1537 name: 'gai', 1538 check: () => { 1539 if (typeof ga === "function") { 1540 const tracker = ga.getAll()[0]; 1541 return !!tracker; 1542 } 1543 return false; 1544 }, 1545 process: () => { 1546 const tracker = ga.getAll()[0]; 1547 window.abst.eventQueue.forEach(element => { 1548 tracker.send("event", element.eid, element.type, element.variation, {nonInteraction: true}); 1549 }); 1550 } 1551 }, 1552 { 1553 name: 'fathom', 1554 check: () => !!window.fathom, 1555 process: () => { 1556 window.abst.eventQueue.forEach(element => { 1557 window.fathom.trackEvent(element.eid + ", " + element.type + ": " + element.variation); 1558 }); 1559 } 1560 }, 1561 { 1562 name: 'posthog', 1563 check: () => !!window.posthog, 1564 process: () => { 1565 window.abst.eventQueue.forEach(element => { 1566 posthog.capture(bt_experiments[element.eid].name, { 1567 type: element.type, 1568 variation: element.variation 1569 }); 1570 }); 1571 } 1572 }, 1573 { 1574 name: 'abmix', 1575 check: () => typeof mixpanel === "object", 1576 process: () => { 1577 window.abst.eventQueue.forEach(element => { 1578 mixpanel.track(bt_experiments[element.eid].name, {'type': element.type,'variation':element.variation}, {send_immediately: true}); 1579 }); 1580 } 1581 }, 1582 { 1583 name: 'abumav', 1584 check: () => typeof usermaven === "function", 1585 process: () => { 1586 window.abst.eventQueue.forEach(element => { 1587 usermaven("track", bt_experiments[element.eid].name, { 1588 type: element.type, 1589 variation: element.variation 1590 }); 1591 }); 1592 } 1593 }, 1594 { 1595 name: 'umami', 1596 check: () => !!window.umami, 1597 process: () => { 1598 window.abst.eventQueue.forEach(element => { 1599 umami.track(bt_experiments[element.eid].name, { 1600 type: element.type, 1601 variation: element.variation 1602 }); 1603 }); 1604 } 1605 }, 1606 { 1607 name: 'cabin', 1608 check: () => !!window.cabin, 1609 process: () => { 1610 window.abst.eventQueue.forEach(element => { 1611 cabin.event(bt_experiments[element.eid].name + ' | ' + element.type + ' | ' + element.variation); 1612 }); 1613 } 1614 }, 1615 { 1616 name: 'plausible', 1617 check: () => !!window.plausible, 1618 process: () => { 1619 window.abst.eventQueue.forEach(element => { 1620 plausible(bt_experiments[element.eid].name, { 1621 props: { 1622 type: element.type, 1623 variation: element.variation 1624 } 1625 }); 1626 }); 1627 } 1628 }, 1629 { 1630 name: 'abawp', 1631 check: () => typeof AnalyticsWP === "object", 1632 process: () => { 1633 window.abst.eventQueue.forEach(element => { 1634 AnalyticsWP.event('Test: ' + bt_experiments[element.eid].name, { 1635 test_id: element.eid, 1636 test_name: bt_experiments[element.eid].name, 1637 test_variation: element.variation, 1638 test_visit_type: element.type 1639 }); 1640 }); 1641 } 1642 } 1643 ]; 1644 1645 // Initialize partner object if not exists 1646 window.abst.abconvertpartner = window.abst.abconvertpartner || {}; 1647 1648 // Maximum time to wait for analytics providers (in milliseconds) 1649 const MAX_WAIT_TIME = 10000; // 10 seconds 1650 const CHECK_INTERVAL = 500;
1650 // Check every 500ms 1651 1652 // Function to check a single analytics provider 1653 const checkProvider = (provider) => { 1654 // Skip if already processed 1655 if (window.abst.abconvertpartner[provider.name]) { 1656 return true; 1657 } 1658 1659 // Check if provider is available 1660 if (provider.check()) { 1661 window.abst.abconvertpartner[provider.name] = true; 1662 provider.process(); 1663 return true; 1664 } 1665 1666 return false; 1667 }; 1668 1669 // Function to check all providers 1670 const checkAllProviders = () => { 1671 return analyticsProviders.map(provider => checkProvider(provider)); 1672 }; 1673 1674 // Start time to calculate timeout 1675 const startTime = new Date().getTime(); 1676 1677 // Create a promise that resolves when all providers are found or timeout 1678 const findAnalyticsPromise = new Promise((resolve) => { 1679 const checkAnalytics = () => { 1680 // Check all providers 1681 const results = checkAllProviders(); 1682 1683 // If all providers are found or we've exceeded the max wait time, resolve 1684 const allFound = results.every(result => result === true); 1685 const timeElapsed = new Date().getTime() - startTime; 1686 1687 if (allFound || timeElapsed > MAX_WAIT_TIME) { 1688 resolve(); 1689 } else { 1690 // Check again after interval 1691 setTimeout(checkAnalytics, CHECK_INTERVAL); 1692 } 1693 }; 1694 1695 // Start checking 1696 checkAnalytics(); 1697 }); 1698 1699 // Return the promise for potential chaining 1700 return findAnalyticsPromise; 1701} 1702 1703// check for a full page test visit cookie 1704function bt_getQueryVariable(variable) { 1705 var query = window.location.search.substring(1); 1706 var vars = query.split("&"); 1707 for (var i=0;i<vars.length;i++) { 1708 var pair = vars[i].split("="); 1709 if(pair[0] == variable){ 1710 if(pair[1] == null) 1711 return true; 1712 return pair[1]; 1713 } 1714 } 1715 return(false); 1716} 1717 1718if (!String.prototype.endsWith) { 1719 String.prototype.endsWith = function(search, this_len) { 1720 if (this_len === undefined || this_len > this.length) { 1721 this_len = this.length; 1722 } 1723 return this.substring(this_len - search.length, this_len) === search; 1724 }; 1725} 1726 1727 1728 1729//function to find and replace strings uin document, including strings with formatting like strong em, etc 1730function bt_replace_all(find, replace, location = 'body') { 1731 // Get the element by the provided location 1732 const element = document.querySelector(location); 1733 1734 // Function to recursively replace text 1735 function replaceText(node) { 1736 if (node.nodeType === Node.TEXT_NODE) { // Check if it's a text node 1737 // Replace text, preserving HTML entities by decoding and encoding 1738 let text = node.textContent; 1739 let div = document.createElement('div'); 1740 div.innerHTML = text.replace(new RegExp(find, 'gi'), replace); 1741 node.textContent = div.textContent || div.innerText || ""; 1742 } else { 1743 // Otherwise, handle all its children nodes 1744 node.childNodes.forEach(replaceText); 1745 } 1746 } 1747 1748 // Start the text replacement process from the chosen element 1749 replaceText(element); 1750} 1751 1752// Usage example: 1753//bt_replace_all('find text', 'replace text'); 1754 1755function bt_replace_all_html(find, replace, location = 'body') { 1756 // Get the element by the provided location 1757 const element = document.querySelector(location); 1758 1759 // Function to recursively replace HTML 1760 function replaceHTML(node) { 1761 if (node.nodeType === Node.ELEMENT_NODE) { // Check if it's an element node 1762 // Replace HTML, preserving the node structure 1763 node.innerHTML = node.innerHTML.split(find).join(replace); 1764 } else { 1765 // Otherwise, handle all its children nodes 1766 node.childNodes.forEach(replaceHTML); 1767 } 1768 } 1769 1770 // Start the HTML replacement process from the chosen element 1771 replaceHTML(element); 1772} 1773 1774function abClickListener(experimentId,conversionSelector, goalId = 0) { 1775 var testCookie = abstGetCookie('btab_'+experimentId); 1776 if(testCookie) 1777 { 1778 testCookie = JSON.parse(testCookie); 1779 if(goalId == 0) 1780 { 1781 if(testCookie.conversion == 1) 1782 { 1783 //console.log('abClickListener: already converted'); 1784 return; 1785 } 1786 } 1787 else // issa goal 1788 { 1789 if(testCookie.goals[goalId] == 1) 1790 { 1791 //console.log('abClickListener: goal already converted'); 1792 return; 1793 } 1794 } 1795 } 1796 var eventType = 'click'; // Default event type 1797 // If a pipe symbol exists, split it into the conversion selector and the event type 1798 if(conversionSelector.indexOf('|') !== -1) { 1799 var conversionParts = conversionSelector.split('|'); 1800 // Check if there are at least two conversionParts 1801 if(conversionParts.length >= 2) { 1802 conversionSelector = conversionParts[0]; // First part is the conversion selector 1803 eventType = conversionParts[1]; // Second part is the event type 1804 } 1805 } 1806 1807 try { 1808 // Listen for clicks on elements matching the class selector 1809 document.addEventListener(eventType, function(event) { 1810 var target = event.target; 1811 while (target && target !== document) { 1812 if (target.matches(conversionSelector)) { 1813 //console.log(eventType + ' conversion on ' + conversionSelector + ' type ' + goalId); 1814 if(goalId > 0) 1815 abstGoal(experimentId,goalId); 1816 else
1817 abstConvert(experimentId); 1818 break; 1819 } 1820 target = target.parentNode; 1821 } 1822 }, true); 1823 1824 // Listen for clicks on any links with ab-click-convert- query parameter 1825 document.addEventListener('click', function(event) { 1826 var target = event.target; 1827 while (target && target !== document) { 1828 if (target.tagName === 'A' && target.href) { 1829 try { 1830 var url = new URL(target.href); 1831 var params = new URLSearchParams(url.search); 1832 1833 // Check for the ab-click-convert-ID parameter 1834 for (const [key, value] of params.entries()) { 1835 if (key.startsWith('ab-click-convert-')) { 1836 var linkExperimentId = key.replace('ab-click-convert-', ''); 1837 if (linkExperimentId === experimentId) { 1838 event.preventDefault(); // Prevent default link behavior 1839 console.log('Query string conversion for experiment ID: ' + experimentId); 1840 if(goalId > 0) 1841 abstGoal(experimentId, goalId); 1842 else 1843 abstConvert(experimentId); 1844 1845 // Continue to the link after a short delay to allow conversion to be processed 1846 setTimeout(function() { 1847 window.location.href = target.href; 1848 }, 300); 1849 break; 1850 } 1851 } 1852 } 1853 } catch (e) { 1854 // Invalid URL, just continue 1855 } 1856 } 1857 target = target.parentNode; 1858 } 1859 }, true); 1860 } catch(error) { 1861 console.info('ABST: Invalid conversion selector:' + conversionSelector + ' ' + error + ' ' + goalId); 1862 } 1863 var iframes = document.querySelectorAll('iframe'); 1864 iframes.forEach(function(iframe) { 1865 try { 1866 var iframeDoc = iframe.contentWindow.document; 1867 iframeDoc.addEventListener(eventType, function(event) { 1868 if (event.target.matches(conversionSelector)) { 1869 console.log(eventType + ' IFRAME conversion on ' + conversionSelector); 1870 abstConvert(experimentId); 1871 } 1872 }, true); 1873 } catch (error) { 1874 //console.error("Error accessing cross-origin iframe:", error); // CORS issue, the iframe is not in the same origin and does not allow cross origin access. 1875 } 1876 }); 1877} 1878async function setAbFingerprint() { 1879 if(!localStorage.getItem("ab-uuid")){ 1880 try { 1881 const module = await import(window.bt_pluginurl + "/js/ab-fingerprint.js"); 1882 const fp = await ThumbmarkJS.getFingerprint(); 1883 localStorage.setItem("ab-uuid", fp); 1884 console.log("set Fingerprint: " + fp); 1885 } catch (error) { 1886 console.error("Error setting fingerprint:", error); 1887 } 1888 } 1889 else{ 1890 console.log("ab-uuid: already set: " + localStorage.getItem("ab-uuid")); 1891 } 1892} 1893async function setAbCrypto() { 1894 if(!localStorage.getItem("ab-advanced-id")){ 1895 let fp; 1896 try { 1897 fp = crypto.randomUUID(); 1898 console.log("Set advanced id: " + fp); 1899 } catch (e) { // localhost, http 1900 fp = "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c => (+c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16)); 1901 console.log("Set advanced ID: " + fp); 1902 } 1903 localStorage.setItem("ab-advanced-id", fp); 1904 //set cookie same 1905 document.cookie = "ab-advanced-id=" + fp + "; SameSite=None; Secure;"; 1906 } 1907 else{ 1908 console.log("ab-advanced-id: already set: " + localStorage.getItem("ab-advanced-id")); 1909 } 1910} 1911 1912/** 1913 * Revoke approval for tracking (for testing or when user revokes consent) 1914 */ 1915function abst_revoke_approval() { 1916 setAbstApprovalStatus(false); 1917 console.log('Approval revoked, events will be queued until approval is given again'); 1918 return true; 1919} 1920 1921function showMagicTest(eid, index) { // called from magic bar so we arent logging anything 1922 var magic_definition = JSON.parse(bt_experiments[eid].magic_definition); 1923 //foreach 1924 jQuery.each(magic_definition, function(i, swapElement) { 1925 if(swapElement.type == 'text') 1926 jQuery(swapElement.selector).text(swapElement.variations[index]); 1927 if(swapElement.type == 'image') 1928 jQuery(swapElement.selector).attr('src', swapElement.variations[index]).attr('srcset', swapElement.variations[index]); 1929 }); 1930 //scroll into view first element 1931 jQuery('html, body').animate({ 1932 //scroll element to 1/3 down view 1933 scrollTop: jQuery(magic_definition[0].selector).offset().top - window.innerHeight / 3 1934 }, 1000); 1935 // flash a box around the swAPPED ELEMENT FOR 2 SECONDS 1936 jQuery(magic_definition[0].selector).addClass('ab-highlight'); 1937 setTimeout(function(){ 1938 jQuery(magic_definition[0].selector).removeClass('ab-highlight'); 1939 }, 3000); 1940} 1941 1942 1943 1944 1945// Initialize global visibility tracking system 1946if (!window.btab_vars.visibility_tracker) { 1947 window.btab_vars.visibility_tracker = { 1948 elements: {}, 1949 active: false, 1950 interval: null 1951 }; 1952 1953 // Master visibility checking function 1954 window.btab_vars.checkVisibility = function() { 1955 const tracker = window.btab_vars.visibility_tracker; 1956 let hasActiveElements = false;
1957 1958 Object.entries(tracker.elements).forEach(([key, data]) => { 1959 const elements = jQuery(data.selector); 1960 1961 if (elements.length && elements.is(":visible")) { 1962 // Check if any element and all its parents are truly visible 1963 let trulyVisible = false; 1964 1965 elements.each(function() { 1966 let el = jQuery(this); 1967 let allParentsVisible = true; 1968 1969 // Check this element and all parents 1970 while (el.length) { 1971 try { 1972 // Check if hidden or transparent 1973 if (el.is(":hidden")) { 1974 allParentsVisible = false; 1975 break; 1976 } 1977 1978 // Safely get CSS properties with null checks 1979 try { 1980 const visibility = el.css("visibility"); 1981 const opacity = el.css("opacity"); 1982 1983 if (visibility === "hidden" || (opacity !== undefined && parseFloat(opacity) === 0)) { 1984 allParentsVisible = false; 1985 break; 1986 } 1987 } catch (cssError) { 1988 // If we can't get CSS properties, assume element is visible 1989 } 1990 1991 // Move up to parent 1992 el = el.parent(); 1993 } catch (error) { 1994 //console.log('Error checking visibility:', error); 1995 break; 1996 } 1997 } 1998 1999 if (allParentsVisible) { 2000 trulyVisible = true; 2001 return false; // Break the each loop 2002 } 2003 }); 2004 2005 if (trulyVisible) { 2006 //console.log('absplit: visible viewed event fired:', data.eid, data.variation); 2007 bt_experiment_w(data.eid, data.variation, 'visit', false); 2008 delete tracker.elements[key]; // Remove from tracking 2009 } else { 2010 hasActiveElements = true; 2011 } 2012 } else { 2013 hasActiveElements = true; 2014 } 2015 }); 2016 2017 // Stop interval if nothing left to track 2018 if (!hasActiveElements) { 2019 clearInterval(tracker.interval); 2020 tracker.active = false; 2021 } 2022 }; 2023} 2024// Add this at the top of the file or before any usage 2025function ensureVisibilityTrackerInitialized() { 2026 if (!window.btab_vars.visibility_tracker) { 2027 window.btab_vars.visibility_tracker = { 2028 elements: {}, 2029 active: false, 2030 interval: null 2031 }; 2032 } 2033} 2034 2035// Use this function in watch_for_tag_event and anywhere else you initialize the tracker 2036function watch_for_tag_event(eid, selector = 'experiment', variation = null) { 2037 if (variation == null) return; // no variation, no event 2038 2039 // If it's an experiment, construct the selector 2040 if (selector == 'experiment') // get the testID, on page element tests 2041 selector = `[bt-eid="${eid}"]` + (variation ? `[bt-variation="${variation}"]` : ''); 2042 2043 // Ensure tracker is initialized only once 2044 ensureVisibilityTrackerInitialized(); 2045 2046 const tracker = window.btab_vars.visibility_tracker; 2047 const key = `${eid}_${selector}`; 2048 2049 // Add to tracking list 2050 tracker.elements[key] = { eid, selector, variation }; 2051 2052 // Start interval if not already running 2053 if (!tracker.active) { 2054 tracker.interval = setInterval(window.btab_vars.checkVisibility, 500); 2055 tracker.active = true; 2056 } 2057 2058 // For backward compatibility 2059 if (!window.btab_vars.waiting_for_view) { 2060 window.btab_vars.waiting_for_view = {}; 2061 } 2062 window.btab_vars.waiting_for_view[selector] = tracker.interval; 2063} 2064function normalizeUrl(url) { 2065 page_url = url.replace(window.location.origin,''); 2066 if (page_url.charAt(0) == "/") page_url = page_url.substr(1); 2067 if (page_url.charAt(page_url.length - 1) == "/") page_url = page_url.substr(0, page_url.length - 1); 2068 2069 return page_url; 2070}
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.