1var ab_highlight_timer; 2function bt_highlight(selector){ 3 if(window.ab_highlight_timer) { 4 clearTimeout(window.ab_highlight_timer); 5 jQuery('.ab-highlight').removeClass('ab-highlight'); 6 } 7 var elem; 8 if(jQuery("#elementor-preview-iframe").length) 9 { 10 elem = jQuery("#elementor-preview-iframe").contents().find(selector); 11 jQuery("#elementor-preview-iframe").contents().find('.ab-highlight').removeClass('ab-highlight'); 12 } 13 else 14 { 15 elem = jQuery(selector); 16 jQuery('.ab-highlight').removeClass('ab-highlight'); 17 } 18 19 elem.addClass("ab-highlight"); 20 window.ab_highlight_timer = setTimeout(function(){ 21 elem.removeClass('ab-highlight'); 22 },2000); 23} 24 25 26jQuery(function(){ 27 28// add ai button to pages sao far gb and BB toolbars, need to do the rest 29// jQuery('.edit-post-header-toolbar__left, .fl-builder-bar-actions').append('<button><STRONG>AI</strong></button>') 30 31 32 if ( self !== top ) // if inside an iframe, then its a preview and we dont want to do things. 33 return; 34 35 jQuery('body').on('change blur','[name="bt_click_conversion_selector"], .bt_click_conversion_selector input, [data-setting="bt_click_conversion_selector"]',function(){ 36 bt_highlight(jQuery(this).val()); 37 }); 38 39}); 40 41 42// AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI 43// AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI 44// AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI AI 45 46 47 48function abai(){ 49 jQuery.magnificPopup.open({ 50 items: { 51 src: '#ab-ai-form', // can be a HTML string, jQuery object, or CSS selector 52 type: 'inline' 53 } 54 }); 55} 56 57 58/* 59 60 61 62*/ 63jQuery(function(){ 64 65 jQuery('body',parent.window.document).on('click','#ab-ai, #wp-admin-bar-ab-ai, .ab-ai-launch',function(){ 66 abai(); 67 }); 68 69 jQuery('body').on('click','.ai-option',function(){ 70 var theText = jQuery(this).text(); 71 copyText(theText); 72 }); 73 74 jQuery('#ab-rewrite-form').submit(function(event) { 75 event.preventDefault(); 76 77 //display loading screen 78 jQuery("#result .ai-responses").html('<p>'+loadingMessage()+' may take up to 30 seconds...</p>'); 79 jQuery('#result').fadeIn(); 80 jQuery('.ai-loading').show(); 81 82 //send request 83 callOpenAI('suggestions','#result .ai-responses'); 84 85 }); 86 87 88 jQuery('input[type=radio][name=abaitype]').change(function() { 89 jQuery("#result .ai-responses").text('submit to see response.'); 90 jQuery("#ab-ai-submit").show(); 91 if (this.value == 'suggestions') { 92 jQuery('#suggestions-div').show(); 93 jQuery('#rewrite-div').hide(); 94 } 95 else if (this.value == 'rewrite') { 96 jQuery('#suggestions-div').hide(); 97 jQuery('#rewrite-div').show(); 98 } 99 }); 100 101 // add to builders 102 setTimeout(function(){ // improve this 103 104 // bb 105 jQuery( '.fl-builder-bar-actions', parent.window.document ).append( '<button class="ab-ai-launch fl-builder-button"> AI </button>' ); 106 107 // gb 108 jQuery('.edit-post-header-toolbar__left').append('<button type="button" data-toolbar-item="true" aria-disabled="false" class="components-button ab-ai-launch has-icon" aria-label="Launch AI"><svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="15" height="15" viewBox="0 0 24 24"><path d="M 17 2 A 2 2 0 0 0 15 4 A 2 2 0 0 0 16 5.7285156 L 16 7 L 13 7 L 13 5 L 13.001953 3.5546875 A 1.0001 1.0001 0 0 0 12.503906 2.6894531 C 11.787176 2.2732724 10.988534 2.0496274 10.183594 2.0175781 C 9.91528 2.006895 9.6455955 2.0167036 9.3789062 2.0488281 C 8.31215 2.1773261 7.2814338 2.6482536 6.4648438 3.4648438 C 6.1441089 3.7855785 5.8954006 4.1406575 5.6992188 4.515625 L 5.6699219 4.5 C 5.0630052 5.5507072 4.9071497 6.7326156 5.1015625 7.8476562 C 3.2754904 8.8728198 2 10.76268 2 13 C 2 14.819816 2.8864861 16.388036 4.1660156 17.484375 C 4.6408757 20.032141 6.8174874 22 9.5 22 C 10.627523 22 11.683838 21.655029 12.556641 21.070312 A 1.0001 1.0001 0 0 0 13 20.240234 L 13 19 L 13 17 L 16 17 L 16 18.269531 A 2 2 0 0 0 15 20 A 2 2 0 0 0 17 22 A 2 2 0 0 0 19 20 A 2 2 0 0 0 18 18.271484 L 18 16 A 1.0001 1.0001 0 0 0 17 15 L 13 15 L 13 13 L 19.271484 13 A 2 2 0 0 0 21 14 A 2 2 0 0 0 23 12 A 2 2 0 0 0 21 10 A 2 2 0 0 0 19.269531 11 L 13 11 L 13 9 L 17 9 A 1.0001 1.0001 0 0 0 18 8 L 18 5.7304688 A 2 2 0 0 0 19 4 A 2 2 0 0 0 17 2 z M 9.9765625 4.0253906 C 10.323274 4.0594887 10.663125 4.1899373 11 4.3144531 L 11 5 L 11 7.8320312 A 1.0001 1.0001 0 0 0 11 8.1582031 L 11 10.001953 L 10.994141 10.001953 C 10.995487 11.115594 10.11489 11.998654 9.0019531 12 L 9.0039062 14 C 9.7330113 13.999103 10.409972 13.784291 11 13.4375 L 11 15.832031 A 1.0001 1.0001 0 0 0 11 16.
108158203 L 11 19 L 11 19.544922 C 10.53433 19.775688 10.05763 20 9.5 20 C 7.6963955 20 6.2496408 18.652222 6.0449219 16.904297 A 1.0001 1.0001 0 0 0 5.6445312 16.214844 C 4.6481295 15.482432 4 14.327105 4 13 C 4 11.598815 4.7246346 10.392988 5.8105469 9.6816406 C 6.2276287 10.337914 6.7833892 10.916519 7.5 11.330078 L 8.5 9.5976562 C 7.8796927 9.2396745 7.4474748 8.6957359 7.2089844 8.0820312 A 1.0001 1.0001 0 0 0 7.1855469 8 C 7.0449376 7.6024542 6.9871315 7.1827317 7.015625 6.7695312 C 7.0230903 6.6612728 7.0432757 6.5542362 7.0625 6.4472656 C 7.076659 6.3735269 7.0914327 6.2997442 7.1113281 6.2265625 C 7.1310767 6.1505802 7.1498827 6.0745236 7.1757812 6 C 7.2330841 5.8402322 7.3023195 5.6825058 7.3886719 5.5292969 C 7.5187762 5.2975857 7.6804734 5.0773393 7.8789062 4.8789062 C 8.3733162 4.3844964 8.9892096 4.1023458 9.6269531 4.0273438 C 9.7439583 4.0135832 9.860992 4.0140246 9.9765625 4.0253906 z"></path></svg> AI</button>'); 109 // elementor 110 //todo 111 // oxy 112 //todo 113 114 // breakdance 115 //soon // jQuery('.undo-redo-top-bar-section',window.parent.document).before('<div class="topbar-section topbar-section-bl"><button type="button" class="v-btn v-btn--outlined theme--light elevation-0 v-size--default breakdance-toolbar-button ab-ai-launch" style="height: 37px; margin-left: 3px; margin-right: 3px;"><span class="v-btn__content"> AI </span></button></div>'); 116 117 // bricks 118 //todo 119 120 },2000); 121}); 122 123 124 125 126 127 128/** 129 * Copies the specified text to the clipboard. 130 * @param {String} text The text to copy. 131 */ 132 133 function copyText(text) { 134 if (!navigator.clipboard) { 135 console.info('Cant copy to navigator.clipboard, you are probably on localhost where window.clipboard isnt allowed.'); 136 return; 137} 138 139 navigator.clipboard.writeText(text).then(function() { 140 alert('Copied!'); 141 }, function(err) { 142 console.info('Cant copy, you are probably on localhost where window.clipboard isnt allowed. Full error: ', err); 143 }); 144} 145 146 147 148async function callOpenAI(abAiType,outputSelector) { 149 150 // send text and ai response required type 151 var abAiType = jQuery('input[type="radio"][name="abaitype"]:checked')[0]['value']; 152 if(abAiType == 'rewrite') 153 var query = jQuery('[name="inputText"]').val().trim(); 154 else 155 { 156 var cleanedHtml = getAbPageContent(); 157 158 query = cleanedHtml; 159 sendToOpenAI(query,abAiType,outputSelector); 160 } 161 162} 163 164function getAbPageContent(){ 165 166 167 var selectorList = [ 168 '.wp-block-post-content', 169 '[itemprop="mainContentOfPage"]', 170 '[role="main"]', 171 'main', 172 '#mainContent', 173 'article', 174 '.article', 175 '.content', 176 '#content', 177 '.entry-content', 178 'body', 179 ] 180 var abPageContent = false; 181 jQuery.each( selectorList, function( key, selector ) { // run through the selectors until we find one, cant fail with good ol body at the end 182 if(jQuery(selector).length) 183 { 184 console.info('found with selector: ' + selector); 185 abPageContent = jQuery(selector).clone(); // Clone the content to avoid modifying the actual page 186 return false; // break loop 187 } 188 }); 189 190 // Remove non understandable things, media, and links 191 abPageContent.find('source, header,footer, iframe, #wpadminbar,script,style,#ab-ai-form,meta,script,style,link, #wpadminbar').remove(); 192 193 // remove classes and styles from html 194 abPageContent.find('*').removeAttr('style').removeAttr('data-*').removeAttr('data-node'); 195 // remove spaces between html elements 196 abPageContent.find('*').contents().filter(function() { 197 return this.nodeType === 3 && !/\S/.test(this.nodeValue); 198 }).remove(); 199 200 // Convert the cleaned content to HTML 201 var cleanedHtml = abPageContent.html(); 202 203 return cleanedHtml; 204} 205 206 207 208/** 209 * Sends a query to OpenAI and returns the response. 210 * @param {string} query - The query to send to OpenAI. 211 * @param {string} abAiType - The type of AI to use. (rewrite, suggestions, magic) 212 * @param {string} outputSelector - The selector for the output element. 213 */ 214function sendToOpenAI(query,abAiType,outputSelector) { 215 jQuery("#ai-suggestions").slideDown().append('<p>Generating AI Suggestions â¨</p>'); 216 217 context = getAbPageContent();
218 //convert to markdown w turndown 219 var turndownService = new TurndownService(); 220 var markdown = turndownService.turndown(context); 221 222 jQuery.ajax({ 223 url: bt_ajaxurl, 224 type : 'post', 225 data : { 226 'action': 'send_to_openai', 227 'input_text': query, 228 'type': abAiType, 229 'title': abAiType, 230 'context':markdown, 231 }, 232 success: function( response ) { 233 if(response && typeof response.error !== 'undefined') 234 { 235 console.log(response); 236 jQuery(outputSelector).html('<small><strong>ERROR</strong></small><BR>' + response["error"]['message']); 237 } 238 else 239 { 240 console.log(response.choices[0]['message']['content']); 241 var outt = ''; 242 if(abAiType == 'suggestions') 243 { 244 //trim the content before the first square bracket and after the last square bracket 245 respo = response.choices[0]['message']['content']; 246 // remove ```json 247 respo = respo.replace(/```json/g, ''); 248 // remove ``` 249 respo = respo.replace(/```/g, ''); 250 251 var ideas = JSON.parse(respo); 252 253 console.log(ideas); 254 //remove ```json and ``` from response 255 outt += "<h3>CRO Page Score: " + ideas.overall_page_rating + "%</h3><h4> You should consider adding:</h4><p> " + ideas.missing_content +"</p>"; 256 jQuery.each(ideas.suggestions,function(index, content){ 257 258 outt += "<div class='ai-option'><h4>" + content.test_name + "</h4><p> " + content.reason_why +"</p><p>Original text:<BR><strong>" + content.original_string + "</strong></p><p>Suggestions:</p>"; 259 jQuery.each(content.suggestions,function(index, suggestion){ 260 261 outt += "<p class='ai-suggestion-item'>" + suggestion + "</p>"; 262 }); 263 outt += "</div>"; 264 }); 265 } 266 else if(abAiType == 'magic') 267 { 268 outt=''; 269 jQuery("#ai-suggestions-list").empty(); 270 suggestions = JSON.parse(response.choices[0]['message']['content']).suggestions; 271 jQuery.each(suggestions,function(index, choice){ 272 outt += "<li class='ai-suggestion-item'>" + choice + "</li>"; 273 }); 274 jQuery("#ai-suggestions p").remove(); 275 jQuery("#ai-suggestions-list").append(outt); 276 jQuery("#ai-suggestions-list").slideDown(1000); 277 jQuery("#ai-suggestions").prepend('<p>AI Suggestions ⨠<small>Click to add to test variations & edit.</small></p>').slideDown(1000); 278 } 279 else 280 { 281 suggestions = JSON.parse(response.choices[0]['message']['content']); 282 jQuery.each(suggestions.suggestions,function(index, choice){ 283 outt += "<div class='ai-option'>" + choice +"</div>"; 284 }); 285 } 286 jQuery(outputSelector).html(outt); 287 } 288 jQuery('.ai-loading').hide(); 289 } 290 }); 291} 292 293 294 295 296function loadingMessage(){ 297 298 var loadingMessages = [ 299 "Loading, please wait...", 300 "Fetching unicorns from the cloud...", 301 "Initiating data transfer...", 302 "Preparing to dazzle you...", 303 "Calculating the meaning of life...", 304 "Polishing up the pixels...", 305 "Preparing to amaze you...", 306 "Transmitting awesomeness...", 307 "Crafting your results...", 308 "Preparing for blast off...", 309 "Assembling the pieces...", 310 "Elevating your experience...", 311 "Generating quantum states...", 312 "Refining your results...", 313 "Transforming data into gold...", 314 "Accelerating electrons...", 315 "Integrating over functions...", 316 "Deconstructing the code...", 317 "Tuning the engine...", 318 "Synchronizing the clocks...", 319 "Translating into ones and zeros...",
320 "Archiving history...", 321 "Empowering your experience...", 322 "Conducting the orchestra...", 323 "Compiling the modules...", 324 "Scaling the heights...", 325 "Breaking the sound barrier...", 326 "Hyperspace travel engaged...", 327 "Charging the capacitors...", 328 "Aligning the planets...", 329 "Distributing the load...", 330 "Mapping the unknown...", 331 "Decompressing the data...", 332 "Filtering out the noise...", 333 "Quantum entangling particles...", 334 "Deciphering the glyphs...", 335 "Optimizing the algorithm...", 336 "Defragging the disk...", 337 "Unleashing the power...", 338 "Leveraging the network...", 339 "Expanding the universe...", 340 "Ruling out the impossible...", 341 "Crunching the numbers...", 342 "Synthesizing reality...", 343 "Solving the puzzle...", 344 "Unearthing the truth...", 345 "Breaking the deadlock...", 346 "Refining the solution...", 347 ]; 348 349 return loadingMessages[Math.floor(Math.random()*loadingMessages.length)]; 350} 351 352 353/* 354 355 356 357jQuery('.fl-builder-bar-actions').append('<button class="ab-ai-launch fl-builder-button"> AI </button>'); 358 359 360 361*/ 362 363 364 365 366 367 368 369 370 371 372 373 374/** 375 * ABST Magic Bar 376 * Displays a modal at the top of the website and pushes down content 377 */ 378 379 380function selectorDetection(){ 381 382 if(jQuery('body').hasClass('abst-selector-detection')) 383 return; // only once 384 385 jQuery('body').css('pointer-events', 'auto').addClass('abst-selector-detection'); 386 jQuery('img').css('pointer-events', 'auto'); 387 // Create the tooltip and box elements once, outside the event handlers 388 var tooltip = jQuery('<div id="selector-tooltip" style="position: absolute; background-color: #fff; border: 2px solid #ddd; padding: 5px; font-size: 12px; display: none; pointer-events: none;"></div>'); 389 var box = jQuery('<div id="selector-box" style="position: absolute; border: 4px solid orange; padding: 5px; font-size: 14px; display: none; pointer-events: none;"></div>'); 390 jQuery('body').append(tooltip); 391 jQuery('body').append(box); 392 393// Track the current element being hovered 394var currentElement = null; 395var hoverTimer = null; 396
397// Add a debounce mechanism at the start of your code 398var selectorHoverDebounce = null; 399var lastProcessedElement = null; 400 401 402 403jQuery('body').on('mouseover', function(e){ 404 // Clear any existing debounce timer 405 if (selectorHoverDebounce) { 406 clearTimeout(selectorHoverDebounce); 407 } 408 409 // Set a small delay before processing 410 selectorHoverDebounce = setTimeout(function() { 411 showBar = true; 412 if(!jQuery('#abst-magic-bar').is(':visible')) 413 showBar = false; 414 415 var element = e.target; 416 417 // Skip if hovering over the tooltip or box or their children 418 if (element.id === 'selector-tooltip' || element.id === 'selector-box' || 419 jQuery.contains(document.getElementById('selector-tooltip'), element) || 420 jQuery.contains(document.getElementById('selector-box'), element)) { 421 showBar = false; 422 } 423 424 if(jQuery(element).parents('.media-modal.wp-core-ui').length > 0)//not inside media modal 425 showBar = false; 426 427 // If we're already showing for this element, don't do anything 428 if (currentElement === element) { 429 showBar = false; 430 } 431 432 currentElement = element; 433 var selector = getUniqueSelector(element); 434 435 //filters 436 var elementType = false;
437 438 //dont show if on the abst-magic-bar or any parent is abst-magic-bar 439 if(element.id !== 'abst-magic-bar') { 440 // Check all parents for the ID 441 var parent = element.parentElement; 442 while(parent) { 443 if(parent.id == 'abst-magic-bar') 444 showBar = false; 445 parent = parent.parentElement; 446 } 447 } 448 449 if(!showBar) 450 { 451 tooltip.hide(); 452 box.hide(); 453 return; 454 } 455 456 //fin if elementr has inntertext or is an image 457 if(element.tagName == 'IMG' || element.tagName == 'SVG') 458 elementType = 'image'; 459 460 // Check if element has direct text (not just from child elements) 461 var hasDirectText = false; 462 for (var i = 0; i < element.childNodes.length; i++) { 463 var node = element.childNodes[i]; 464 if (node.nodeType === 3 && node.textContent.trim() !== '') { // Text node 465 hasDirectText = true; 466 break; 467 } 468 } 469 470 // Check for text elements that typically contain direct text content 471 var textTags = ['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'SPAN', 'A', 'BUTTON', 'LABEL', 'LI', 'TD', 'TH', 'STRONG', 'EM', 'B', 'I']; 472 if (hasDirectText || (textTags.includes(element.tagName) && element.textContent && element.textContent.trim() !== '')) { 473 elementType = 'text'; 474 } 475 476 //if element has bg img 477 if(element.style.backgroundImage && element.style.backgroundImage !== 'none') 478 elementType = 'bgimage'; 479 480 if(!elementType) 481 return; 482 483 // Clear any existing timer 484 if (hoverTimer) { 485 clearTimeout(hoverTimer); 486 hoverTimer = null; 487 } 488 //add elementtype to element 489 element.setAttribute('data-abmagictestelementtype', elementType); 490 // Display the tooltip with the selector 491 tooltip.text(elementType.toUpperCase() + " " + selector + ' (' + element.tagName + ') Click to test this element'); 492 493 // Calculate tooltip position to keep it within viewport 494 var tooltipWidth = 250; // Approximate width, adjust as needed 495 var viewportWidth = window.innerWidth; 496 var leftPosition = Math.min(e.clientX + 10, viewportWidth - tooltipWidth - 10); 497 498 tooltip.css({ 499 top: e.clientY + 10 + window.scrollY, 500 left: leftPosition + window.scrollX, 501 zIndex: '9999999999', 502 maxWidth: tooltipWidth + 'px' 503 }); 504 tooltip.show(); 505 506 // Get position of element to display a box around 507 var rect = element.getBoundingClientRect(); 508 box.css({ 509 zIndex: '158000', 510 top: rect.top + window.scrollY - 10, 511 left: rect.left + window.scrollX - 10, 512 width: rect.width + 20, 513 height: rect.height + 20 514 }); 515 box.show(); 516 // Improved element comparison to avoid flickering 517 if (lastProcessedElement === element) { 518 return; 519 } 520 521 lastProcessedElement = element; 522 523 524 }, 50); // Small delay to debounce 525 }); 526 527 // Add a mouseleave handler to hide the tooltip and box when leaving the element 528 jQuery('body').on('mouseout', function(e) { 529 if (jQuery(e.relatedTarget).closest(jQuery(this)).length === 0) { 530 // Only hide when truly leaving the element (not entering a child) 531 if (hoverTimer) { 532 clearTimeout(hoverTimer); 533 } 534 535 // Add a small delay before hiding 536 hoverTimer = setTimeout(function() { 537 tooltip.hide(); 538 box.hide(); 539 lastProcessedElement = null; 540 }, 150); 541 } 542 }); 543 jQuery('body').on('click', function(e){ 544 // if the magic bar is not visible, do nothing 545 if(!jQuery('#abst-magic-bar').is(':visible')) 546 return; 547 548 var element = e.target; 549 //if it has data-abmagictestelementtype 550 if(!element.getAttribute('data-abmagictestelementtype')) 551 return; 552 553 e.preventDefault(); 554 555 var selector = getUniqueSelector(element); 556 console.log('unique selector is ' + selector); 557 558 //modes image or text 559 //if its an image 560 if(e.target.tagName == 'IMG') 561 { 562 console.log('clicked on image'); 563 //get unique selector for this page 564 width = jQuery(element).width(); 565 height = jQuery(element).height(); 566 console.log('meniu popop now '); 567 setMagicBar(selector, jQuery(element).attr('src'),false, 'image'); 568 } 569 else if(e.target.tagName == 'SVG') 570 { 571 console.log('clicked on svg'); 572 width = jQuery(element).width(); 573 height = jQuery(element).height(); 574 var imgSrc = jQuery(element).attr('src'); 575 jQuery(element).attr('src', 'https://placekeanu.com/'+width+'/'+height+'/'); 576 //remove srcset so its not broken 577 jQuery(element).removeAttr('srcset'); 578 jQuery(element).attr('alt', 'SWAPPED TEXT STRING'); 579 setMagicBar(selector, imgSrc,false, 'image'); 580 setTimeout(function(){ 581 jQuery(element).attr('src', imgSrc); 582 }, 800); 583 584 } 585 else 586 { 587 console.log('clicked on txt'); 588 if(jQuery(selector).text() != '') 589 { 590 sText = jQuery(selector).text(); 591 //create newtext that is the same number of chars s current text 592 var newText = 'Split Test text here! Depending on how much text you have you will see more of this generic filler text. But once you add your variations, you will see your own text instead of this. Test text here! Depending on how much text you have you will see more of this generic filler text. But once you add your variations, you will see your own text instead of this. Test text here! Depending on how much text you have you will see more of this generic filler text. But once you add your variations, you will see your own text instead of this. Test text here! Depending on how much text you have you will see more of this generic filler text. But once you add your variations, you will see your own text instead of this. ';
593 newText = newText.substring(0, sText.length); 594 setMagicBar(selector, sText); 595 jQuery(selector).text(newText); 596 setTimeout(function(){ 597 jQuery(selector).text(sText); 598 }, 800); 599 } 600 else 601 { 602 console.log('no text'); 603 return false 604 } 605 //select and swap 606 } 607 608 }); 609} 610function setMagicBar(selector, selectorText,goal = false,type = 'text') { 611 //check we have an active element 612 if(jQuery(".abst-element-column.active").length < 1) 613 jQuery('.abst-element-column').first().addClass('active'); 614 615 jQuery('.active .abst-element-title-type').text(" " + type); 616 617 jQuery('.active .abst-magic-element-selector').val(selector); 618 jQuery('.active .abst-variant').val('').hide(); 619 jQuery('.active .abst-magic-a-value').val(selectorText.trim()); 620 jQuery('.active .abst-magic-a-value, .active .abst-magic-b-value').show(); 621 jQuery('#ai-suggestions').hide(); 622 jQuery('#ai-suggestions-list').empty(); 623 624 if(goal) 625 jQuery('#abst-goal').val(goal).trigger('change'); 626 if(type == 'text'){ 627 sendToOpenAI(selectorText,'magic','#ai-suggestions-list'); 628 } 629 if(type == 'image'){ 630 jQuery('#ai-suggestions').slideUp(); 631 //on .abst-variant input click, open media modal 632 //set up click listener for .abst-variant input 633 //if we dont already have a listener set oen up 634 if(!jQuery('.abst-variant').data('hasListener')){ 635 jQuery('.abst-variant').data('hasListener', true); 636 637 jQuery('.abst-variant').on('click', function(){ 638 if(jQuery(this).parents('.abst-element-column').find('.abst-element-title-type').text().toLowerCase().trim() != 'image') 639 return; 640 641 var actualElement = jQuery(jQuery(this).parents('.abst-element-column').find('.abst-magic-element-selector').val()) 642 if(jQuery(this).val() != ''){ 643 console.log('setting image already set'); 644 if(jQuery(actualElement).attr('abogimg') == '') 645 jQuery(actualElement).attr('abogimg', jQuery(this).val()); 646 //show background image in plkace for 2s then hide 647 jQuery(actualElement).attr('src', jQuery(this).val()).removeAttr('srcset'); 648 setTimeout(function(){ 649 jQuery(actualElement).attr('src', jQuery(actualElement).attr('abogimg')); 650 }, 2000); 651 return; 652 } 653 // Create the media frame. 654 var file_frame = wp.media.frames.file_frame = wp.media({ 655 title: 'Choose a variation image', 656 button: { 657 text: 'Add image to test' 658 }, 659 multiple: false // Set to true to allow multiple file selection 660 }); 661 662 // When an image is selected, show media frame 663 file_frame.on( 'select', function() { 664 // We set multiple to true so get all images from the uploader 665 var attachments = file_frame.state().get('selection').toJSON(); 666 //add to magic bar 667 console.log(attachments); 668 for(var i = 0; i < attachments.length; i++) 669 { 670 jQuery(".active .abst-variant").each(function(){ 671 if(jQuery(this).val() == '') 672 { 673 jQuery(this).val(attachments[i].url).css('transition', '1s').css('background-color', 'lightgreen').trigger('input').parents('.abst-magic-variants').show(); 674 if(!jQuery(actualElement).attr('abogimg')) 675 jQuery(actualElement).attr('abogimg', jQuery(actualElement).attr('src')); 676 jQuery(actualElement).attr('src', jQuery(this).val()).removeAttr('srcset'); 677 return false;
678 } 679 }); 680 } 681 setTimeout(function(){ 682 console.log('hiding image'); 683 jQuery(actualElement).css('background-color', 'transparent'); 684 jQuery(actualElement).attr('src', jQuery(actualElement).attr('abogimg')); 685 }, 2000); 686 }); 687 file_frame.open(); 688 }); 689 } 690 } 691} 692 693function getUniqueSelector(element) { 694 // If not an element, return null 695 if (!(element instanceof Element)) return null; 696 697 // If element has an ID, use that (simplest case) 698 if (element.id) { 699 return '#' + element.id; 700 } 701 702 // Try to find a simple selector with classes 703 const simpleSelector = getSimpleSelector(element); 704 if (simpleSelector && jQuery(simpleSelector).length === 1) { 705 return simpleSelector; 706 } 707 708 // If we need to use a path, generate the shortest possible path 709 return generateShortPath(element); 710} 711 712// Helper function to get a simple selector for an element 713function getSimpleSelector(element) { 714 const tag = element.tagName.toLowerCase(); 715 716 // Try tag with classes 717 if (element.classList.length > 0) { 718 // Get the most specific class (usually the last one is most specific) 719 const classes = Array.from(element.classList).sort((a, b) => { 720 // Prioritize classes that are less common in the document 721 const aCount = jQuery('.' + a).length; 722 const bCount = jQuery('.' + b).length; 723 return aCount - bCount; 724 }); 725 726 // Try with just one class first 727 for (const cls of classes) { 728 const selector = tag + '.' + cls; 729 if (jQuery(selector).length === 1) { 730 return selector; 731 } 732 } 733 734 // Try with two classes if needed 735 if (classes.length >= 2) { 736 for (let i = 0; i < classes.length - 1; i++) { 737 for (let j = i + 1; j < classes.length; j++) { 738 const selector = tag + '.' + classes[i] + '.' + classes[j]; 739 if (jQuery(selector).length === 1) { 740 return selector; 741 } 742 } 743 } 744 } 745 } 746 747 // Try with a single important attribute 748 const importantAttrs = ['href', 'src', 'alt', 'title', 'name', 'value', 'type', 'role']; 749 for (const attrName of importantAttrs) { 750 if (element.hasAttribute(attrName)) { 751 const attrValue = element.getAttribute(attrName); 752 if (attrValue && attrValue.length < 100) { // Skip very long attribute values 753 const selector = tag + '[' + attrName + '="' + attrValue.replace(/"/g, '\\"') + '"]'; 754 if (jQuery(selector).length === 1) { 755 return selector; 756 } 757 } 758 } 759 } 760 761 return null; 762} 763 764// Generate a short path to the element 765function generateShortPath(element) { 766 let path = []; 767 let current = element; 768 769 // Walk up the DOM tree until we find an ID or reach the body 770 while (current && current !== document.body) { 771 // If we find an element with ID, use that and stop 772 if (current.id) { 773 path.unshift('#' + current.id); 774 break; 775 } 776 777 // Try to create a unique selector for this level 778 const tag = current.tagName.toLowerCase(); 779 let selector = tag; 780 781 // Add a class if it helps make it more specific but not too specific 782 if (current.classList.length > 0) { 783 // Find the most specific useful class 784 for (const cls of current.classList) { 785 const testSelector = tag + '.' + cls; 786 const matches = jQuery(testSelector, current.parentNode).length; 787 if (matches === 1) { 788 selector = testSelector; 789 break; 790 } 791 } 792 } 793 794 // If we still don't have a unique selector at this level, add nth-child 795 if (jQuery(selector, current.parentNode).length > 1) { 796 const index = Array.from(current.parentNode.children).indexOf(current) + 1; 797 selector += ':nth-child(' + index + ')'; 798 } 799 800 path.unshift(selector); 801 current = current.parentNode; 802 803 // Check if our path is already unique 804 const testPath = path.join(' > '); 805 if (jQuery(testPath).length === 1) { 806 return testPath; 807 } 808 } 809 810 return path.join(' > '); 811} 812 813 814 815 816 817 818function abst_magic_bar(options = {}) { 819 820 // include turndown 821 var script = document.createElement('script'); 822 script.src = btab_vars.plugins_uri + "js/turndown.js"; 823 document.head.appendChild(script); 824 script.onload = function() { 825 console.log('Turndown script loaded to create context'); 826 }; 827 828 selectorDetection(); 829 // Default options 830 const defaults = { 831 height: '250px', 832 backgroundColor: '#f5f5f5', 833 borderColor: '#ddd', 834 content: '', 835 closeButton: true, 836 animation: true, 837 onOpen: null, 838 onClose: null, 839 // Default values for the form elements 840 selector: '.selector', 841 goal: 'Woo Order Recieved', 842 versionA: 'Test anything WordPress', 843 versionB: 'Helping WordPres business sell more', 844 versionC: 'Your private testing cloud' 845 }; 846 847 // Merge defaults with user options 848 const settings = Object.assign({}, defaults, options); 849 850 // Check if the magic bar already exists 851 if (document.getElementById('abst-magic-bar')) { 852 return; 853 } 854 855 // Create the magic bar element 856 const magicBar = document.createElement('div'); 857 magicBar.id = 'abst-magic-bar'; 858 magicBar.className = 'abst-magic-bar'; 859 860 // Add no-animation class if animation is disabled 861 if (!settings.animation) { 862 magicBar.classList.add('no-animation'); 863 } 864 865 var rolesHtml = ''; 866 // foreach abst_magic_data.roles 867 jQuery.each(abst_magic_data.roles, function(key, value) { 868 //if key in array abst_magic_data.defaults 869 if (abst_magic_data.defaults.includes(key)) 870 rolesHtml += '<div class="abst-user-role"><input type="checkbox" name="roles[]" value="' + key + '" checked> ' + value + '</div>'; 871 else 872 rolesHtml += '<div class="abst-user-role"><input type="checkbox" name="roles[]" value="' + key + '"> ' + value + '</div>'; 873 }); 874 // Add content 875 magicBar.innerHTML = ` 876 <div class="abst-magic-bar-content"> 877 <div class="abst-magic-bar-header"> 878 <input id="abst-magic-bar-title" class="abst-magic-bar-title" value="NEW MAGIC TEST"> 879 <div id="abst-magic-bar-actions" class="abst-magic-bar-actions"> 880 <button id="abst-magic-bar-start" class="abst-magic-bar-start">Start Test</button> 881 <!-- one day <button id="abst-magic-bar-save" class="abst-magic-bar-save">Save Draft</button>--> 882 </div> 883 </div> 884 885 <div class="abst-magic-bar-container"> 886 <div class="abst-elements-scroll-container"> 887 <!-- First Element Column --> 888 <div class="abst-element-column"> 889 <div class="abst-element-title">ELEMENT 1 <span class="abst-element-title-type"></span></div> 890 <div class="abst-section"> 891 <input type="text" class="abst-magic-element-selector" value="" placeholder="Click an element to add selector"> 892 </div> 893 <div class="abst-section"> 894 <div class="abst-section-header">VARIATION A (Original)</div> 895 <input type="text" readonly class="abst-variant abst-magic-a-value" value="" placeholder="This is the text that will be tested"> 896 </div> 897 <div class="abst-section"> 898 <div class="abst-section-header">VARIATION B</div> 899 <input type="text" class="abst-variant abst-magic-b-value" value=""> 900 </div> 901 <div class="abst-section abst-hidden"> 902 <div class="abst-section-header">VARIATION C</div> 903 <input type="text" class="abst-variant abst-magic-c-value" value=""> 904 </div> 905 <div class="abst-section abst-hidden"> 906 <div class="abst-section-header">VARIATION D</div> 907 <input type="text" class="abst-variant abst-magic-d-value" value=""> 908 </div> 909 <div class="abst-section abst-hidden"> 910 <div class="abst-section-header">VARIATION E</div> 911 <input type="text" class="abst-variant abst-magic-e-value" value=""> 912 </div> 913 <div class="abst-section abst-hidden"> 914 <div class="abst-section-header">VARIATION F</div> 915 <input type="text" class="abst-variant abst-magic-f-value" value=""> 916 </div> 917 </div> 918 <div class="abst-element-column-hidden"> 919 <div class="abst-element-title">ELEMENT 2 <span class="abst-element-title-type"></span></div> 920 <button class="abst-magic-element-remove">X</button> 921 <div class="abst-section"> 922 <input type="text" class="abst-magic-element-selector" value="" placeholder="Click an element to add selector"> 923 </div> 924 <div class="abst-section"> 925 <div class="abst-blue-header">VARIATION A (Original)</div> 926 <input type="text" readonly class="abst-variant abst-magic-a-value" value=""> 927 </div> 928 <div class="abst-section"> 929 <div class="abst-blue-header">VARIATION B</div> 930 <input type="text" class="abst-variant abst-magic-b-value" value=""> 931 </div> 932 <div class="abst-section abst-hidden"> 933 <div class="abst-blue-header">VARIATION C</div> 934 <input type="text" class="abst-variant abst-magic-c-value" value=""> 935 </div> 936 <div class="abst-section abst-hidden"> 937 <div class="abst-blue-header">VARIATION D</div> 938 <input type="text" class="abst-variant abst-magic-d-value" value=""> 939 </div> 940 <div class="abst-section abst-hidden"> 941 <div class="abst-blue-header">VARIATION E</div> 942 <input type="text" class="abst-variant abst-magic-e-value" value=""> 943 </div> 944 <div class="abst-section abst-hidden"> 945 <div class="abst-blue-header">VARIATION F</div> 946 <input type="text" class="abst-variant abst-magic-f-value" value=""> 947 </div> 948 </div> 949 950 <div class="abst-element-column-hidden"> 951 <div class="abst-element-title">ELEMENT 3 <span class="abst-element-title-type"></span></div> 952 <div class="abst-section"> 953 <input type="text" class="abst-magic-element-selector" value="" placeholder="Click an element to add selector"> 954 </div> 955 <button class="abst-magic-element-remove">X</button> 956 <div class="abst-section"> 957 <div class="abst-blue-header">VARIATION A (Original)</div> 958 <input type="text" readonly class="abst-variant abst-magic-a-value" value=""> 959 </div> 960 <div class="abst-section"> 961 <div class="abst-blue-header">VARIATION B</div> 962 <input type="text" class="abst-variant abst-magic-b-value" value=""> 963 </div> 964 <div class="abst-section abst-hidden"> 965 <div class="abst-blue-header">VARIATION C</div> 966 <input type="text" class="abst-variant abst-magic-c-value" value=""> 967 </div> 968 <div class="abst-section abst-hidden"> 969 <div class="abst-blue-header">VARIATION D</div> 970 <input type="text" class="abst-variant abst-magic-d-value" value=""> 971 </div> 972 <div class="abst-section abst-hidden"> 973 <div class="abst-blue-header">VARIATION E</div> 974 <input type="text" class="abst-variant abst-magic-e-value" value=""> 975 </div> 976 <div class="abst-section abst-hidden"> 977 <div class="abst-blue-header">VARIATION F</div> 978 <input type="text" class="abst-variant abst-magic-f-value" value=""> 979 </div> 980 </div> 981 <div class="abst-element-column-hidden"> 982 <div class="abst-element-title">ELEMENT 4 <span class="abst-element-title-type"></span></div> 983 <div class="abst-section"> 984 <input type="text" class="abst-magic-element-selector" value="" placeholder="Click an element to add selector"> 985 </div> 986 <div class="abst-section"> 987 <div class="abst-blue-header">VARIATION A (Original)</div> 988 <input type="text" readonly class="abst-variant abst-magic-a-value" value=""> 989 </div> 990 <div class="abst-section"> 991 <div class="abst-blue-header">VARIATION B</div> 992 <input type="text" class="abst-variant abst-magic-b-value" value=""> 993 </div> 994 <div class="abst-section abst-hidden"> 995 <div class="abst-blue-header">VARIATION C</div> 996 <input type="text" class="abst-variant abst-magic-c-value" value=""> 997 </div> 998 <div class="abst-section abst-hidden"> 999 <div class="abst-blue-header">VARIATION D</div> 1000 <input type="text" class="abst-variant abst-magic-d-value" value=""> 1001 </div> 1002 <div class="abst-section abst-hidden"> 1003 <div class="abst-blue-header">VARIATION E</div> 1004 <input type="text" class="abst-variant abst-magic-e-value" value=""> 1005 </div> 1006 <div class="abst-section abst-hidden"> 1007 <div class="abst-blue-header">VARIATION F</div> 1008 <input type="text" class="abst-variant abst-magic-f-value" value=""> 1009 </div> 1010 <button class="abst-magic-element-remove">X</button> 1011 </div> 1012 1013 <!-- Fifth Element Column --> 1014 <div class="abst-element-column-hidden"> 1015 <div class="abst-element-title">ELEMENT 5 <span class="abst-element-title-type"></span></div> 1016 <button class="abst-magic-element-remove">X</button> 1017 <div class="abst-section"> 1018 <input type="text" class="abst-magic-element-selector" value="" placeholder="Click an element to add selector"> 1019 </div> 1020 <div class="abst-section"> 1021 <div class="abst-blue-header">VARI
1021ATION A (Original)</div> 1022 <input type="text" readonly class="abst-variant abst-magic-a-value" value=""> 1023 </div> 1024 <div class="abst-section"> 1025 <div class="abst-blue-header">VARIATION B</div> 1026 <input type="text" class="abst-variant abst-magic-b-value" value=""> 1027 </div> 1028 <div class="abst-section abst-hidden"> 1029 <div class="abst-blue-header">VARIATION C</div> 1030 <input type="text" class="abst-variant abst-magic-c-value" value=""> 1031 </div> 1032 <div class="abst-section abst-hidden"> 1033 <div class="abst-blue-header">VARIATION D</div> 1034 <input type="text" class="abst-variant abst-magic-d-value" value=""> 1035 </div> 1036 <div class="abst-section abst-hidden"> 1037 <div class="abst-blue-header">VARIATION E</div> 1038 <input type="text" class="abst-variant abst-magic-e-value" value=""> 1039 </div> 1040 <div class="abst-section abst-hidden"> 1041 <div class="abst-blue-header">VARIATION F</div> 1042 <input type="text" class="abst-variant abst-magic-f-value" value=""> 1043 </div> 1044 </div> 1045 <div class="abst-element-column abst-add-element-column"> 1046 <button class="abst-add-element-button">+ ADD ELEMENT</button> 1047 </div> 1048 </div> 1049 1050 <!-- settings Column --> 1051 <div class="abst-settings-column"> 1052 1053 <div class="abst-settings-title">Targeting</div> 1054 <div class="abst-settings-header closed">User Roles</div> 1055 <div id="abst-user-roles-container" class="abst-hidden"> 1056 ${rolesHtml} 1057 </div> 1058 1059 <div class="abst-settings-header closed">Device Size</div> 1060 <div class="abst-targeting-option abst-hidden"> 1061 <select id="abst-device-size" class="abst-select"> 1062 <option value="all" selected>All Sizes</option> 1063 <option value="desktop">Desktop</option> 1064 <option value="tablet">Tablet</option> 1065 <option value="mobile">Mobile</option> 1066 </select> 1067 </div> 1068 1069 <div class="abst-settings-header closed">URL Filtering</div> 1070 <div class="abst-targeting-option abst-hidden"> 1071 <input type="text" id="abst-url-query" class="abst-url-input" placeholder="utm_source=Google"> 1072 <div class="abst-url-help">Test on traffic with matching URL query strings, or use a * to search the entire URL for a specific string</div> 1073 </div> 1074 1075 <div class="abst-settings-header closed">Traffic Allocation Percentage</div> 1076 <div class="abst-targeting-option abst-hidden"> 1077 <input type="number" id="abst-traffic-percentage" class="abst-number-input" value="100" min="1" max="100"> 1078 </div> 1079 1080 </div> 1081 <!-- Goals Column --> 1082 <div class="abst-goals-column"> 1083 1084 <div class="abst-goals-title">Goals</div> 1085 <div class="abst-goals-container"> 1086 1087 </div> 1088 1089 <div class="abst-button-container"> 1090 <button class="abst-add-goal-button">+ Add Goal</button> 1091 </div> 1092 </div> 1093 </div> 1094 <div id="ai-suggestions"> 1095 <ul id="ai-suggestions-list"></ul> 1096 </div> 1097 </div> 1098 `; 1099 1100 get_abst_pageselector(); 1101 1102 // Add close button if enabled 1103 if (settings.closeButton) { 1104 const closeButton = document.createElement('div'); 1105 closeButton.className = 'abst-magic-bar-close'; 1106 closeButton.innerHTML = 'Ã'; 1107 closeButton.addEventListener('click', function() { 1108 close_abst_magic_bar(); 1109 }); 1110 magicBar.appendChild(closeButton); 1111 } 1112 1113 // Add the magic bar to the body 1114 document.body.appendChild(magicBar); 1115 1116 jQuery(".abst-goals-container").append(abst_magic_data.goals).append('<input type="text" class="abst-goal-input-value" placeholder="">'); 1117 jQuery('body').on('click', '.abst-settings-header', function() { 1118 if(jQuery(this).hasClass('closed')){ 1119 jQuery(this).removeClass('closed'); 1120 jQuery(this).next().removeClass('abst-hidden'); 1121 }else{ 1122 jQuery(this).addClass('closed'); 1123 jQuery(this).next().addClass('abst-hidden'); 1124 } 1125 }); 1126 1127 1128 // Create a spacer element to push down the content 1129 const spacer = document.createElement('div'); 1130 spacer.id = 'abst-magic-bar-spacer'; 1131 spacer.style.height = jQuery('#abst-magic-bar').height() + 'px'; //height of abst-magic-bar; 1132 spacer.style.display = 'none'; 1133 document.body.insertBefore(spacer, document.body.firstChild); 1134 1135 // Show the magic bar 1136 setTimeout(() => { 1137 magicBar.style.transform = 'translateY(0)'; 1138 spacer.style.display = 'block'; 1139 1140 window.abspacerInterval = setInterval(() => { 1141 jQuery('#abst-magic-bar-spacer').css('transition', 'height 2s ease in out'); 1142 jQuery('#abst-magic-bar-spacer').height(jQuery('#abst-magic-bar').height() + 'px'); 1143 }, 300); 1144 1145 // Call onOpen callback if provided 1146 if (typeof settings.onOpen === 'function') { 1147 settings.onOpen(); 1148 } 1149 1150 // Add event listeners for the selector field 1151 const selectorField = document.getElementById('abst-selector'); 1152 if (selectorField) { 1153 selectorField.addEventListener('blur', function() { 1154 // You could add validation or other functionality here 1155 console.log('Selector updated:', this.value); 1156 }); 1157 } 1158 }, 10); 1159 1160 // Function to close the magic bar 1161 window.close_abst_magic_bar = function() { 1162 const magicBar = document.getElementById('abst-magic-bar'); 1163 const spacer = document.getElementById('abst-magic-bar-spacer'); 1164 1165 clearInterval(window.abspacerInterval); 1166 1167 if (magicBar) { 1168 magicBar.style.transform = 'translateY(-100%)'; 1169 1170 setTimeout(() => { 1171 if (magicBar.parentNode) { 1172 magicBar.parentNode.removeChild(magicBar); 1173 } 1174 1175 if (spacer && spacer.parentNode) { 1176 spacer.parentNode.removeChild(spacer); 1177 } 1178 1179 // Call onClose callback if provided 1180 if (typeof settings.onClose === 'function') { 1181 settings.onClose(); 1182 } 1183 }, settings.animation ? 300 : 0); 1184 1185 //stop the selector 1186 const selectorField = document.getElementById('abst-selector'); 1187 if (selectorField) { 1188 selectorField.removeEventListener('blur', function() { 1189 // You could add validation or other functionality here 1190 console.log('Selector updated:', this.value); 1191 }); 1192 } 1193 } 1194 }; 1195} 1196 1197// jQuery wrapper for the function 1198(function($) { 1199 $(function() { 1200 1201 1202 jQuery("body").on('click','.abst-add-element-button',function(){ 1203 //find and then show fiorst abst-hidden .abst-element-column 1204 newShowElement = jQuery('.abst-element-column-hidden').first(); 1205 newShowElement.removeClass('abst-element-column-hidden').addClass('abst-element-column'); 1206 jQuery('#ai-suggestions').hide(); 1207 setTimeout(function() { 1208 newShowElement.addClass('active'); 1209 }, 100); 1210 jQuery('.abst-elements-scroll-container').animate({ 1211 scrollLeft: '+=280px' 1212 }, 1000); 1213 }); 1214 // remove element 1215 jQuery("body").on('click','.abst-magic-element-remove',function(){ 1216 $(this).parent('.abst-element-column').addClass('abst-element-column-hidden').removeClass('abst-element-column').find('input').val(''); 1217 }); 1218 1219 jQuery("body").on('click','.abst-add-goal-button',function(){ 1220 //if theres less than 11 goals 1221 if(jQuery('.abst-goals-container').length < 10){ 1222 goalCount = jQuery('.abst-goals-container').length + 1; 1223 jQuery('<div class="abst-goals-container"><h5>
1223Goal #'+goalCount+'</h5><div class="remove-goal"> X</div> ' +abst_magic_data.goals+'<input type="text" class="abst-goal-input-value" placeholder="Enter goal"></div>').insertBefore('.abst-add-goal-button'); 1224 } 1225 else{ 1226 alert('You can only have 10 goals'); 1227 } 1228 }); 1229 1230 jQuery('body').on('click','.abst-element-column, .abst-goals-column',function(){ 1231 //add class active to this element 1232 if(!$(this).hasClass('abst-add-element-column')) 1233 $(this).addClass('active'); 1234 //remove class active from all other elements 1235 $(this).siblings().removeClass('active'); 1236 }); 1237 // Make the function available in the jQuery namespace 1238 $.abst_magic_bar = function(options) { 1239 abst_magic_bar(options); 1240 }; 1241 1242 // Add event listener for the admin bar menu item 1243 $(document).on('click', '#wp-admin-bar-ab-new-magic-test a', function(e) { 1244 e.preventDefault(); 1245 abst_magic_bar(); 1246 }); 1247 1248 jQuery('body').on('click','.ai-suggestion-item',function(){ 1249 //remove the item clicked 1250 var theText = jQuery(this).text(); 1251 jQuery(this).remove(); 1252 // foreach each 1253 jQuery('.active .abst-variant').each(function() { 1254 //if the input is empty 1255 if(!jQuery(this).val() || jQuery(this).val() == ''){ 1256 jQuery(this).val(theText).trigger('change').parents('.abst-section').removeClass('abst-hidden').find('input').show(); 1257 return false; 1258 } 1259 }); 1260 }); 1261 1262 // Add event handler for variant delete buttons 1263 jQuery('body').on('click', '.abst-variant-delete', function() { 1264 // Get the parent .abst-magic-variants element 1265 var variantContainer = jQuery(this).closest('.abst-magic-variants'); 1266 // Clear the input 1267 variantContainer.find('input').val(''); 1268 // Hide the container 1269 variantContainer.hide(); 1270 }); 1271 1272 jQuery('body').on('click','.remove-goal',function(){ 1273 //remove the container 1274 jQuery(this).parent('.abst-goals-container').remove(); 1275 }); 1276 1277 jQuery('body').on('click','[magic-eid]',function(){ 1278 showMagicTest(jQuery(this).attr('magic-eid'), jQuery(this).attr('magic-index')); 1279 }); 1280 1281 1282 function isInViewport(element) { 1283 const rect = element.getBoundingClientRect(); 1284 return ( 1285 rect.top >= 0 && 1286 rect.left >= 0 && 1287 rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && 1288 rect.right <= (window.innerWidth || document.documentElement.clientWidth) 1289 ); 1290 } 1291 jQuery('body').on('input blur change keyup paste click', '.abst-element-column.active input', function() { 1292 var inputValue = jQuery(this).val(); 1293 var selector = jQuery(this).parents('.abst-element-column').find('.abst-magic-element-selector').val(); 1294 if (selector && inputValue) { 1295 clearTimeout(window.abt_highlight); 1296 // Create or get the flash message element 1297 //IF DATA ATTR originalText is not set then setg it to text 1298 if(!jQuery(selector).data('originalText')){ 1299 jQuery(selector).data('originalText', jQuery(selector).text()); 1300 } 1301 var originalText = jQuery(selector).data('originalText'); 1302 // if selector not in view, scroll to it 1303 if(!isInViewport(jQuery(selector)[0]) && !jQuery(selector).hasClass('scrollingto')) { 1304 jQuery(selector).addClass('scrollingto'); 1305 jQuery('html, body').animate({ 1306 scrollTop: jQuery(selector).offset().top - 100 - jQuery('#abst-magic-bar').height() 1307 }, 600, function() { 1308 jQuery(selector).removeClass('scrollingto'); 1309 }); 1310 } 1311 jQuery(selector).text(inputValue); 1312 //set background color to orange 1313 jQuery(selector).attr('data-original-background-color', jQuery(selector).css('background-color')); 1314 //set background transition to 1s ease in out 1315 jQuery(selector).css('transition', 'background-color 1s ease-in-out'); 1316 //fade in background color 1 s then back to original 1317 jQuery(selector).css('background-color', 'rgba(240, 173, 78, 0.3)'); 1318 1319 //loop through all abst-magic-variants if has a value then show if empty show the first and hide the rest 1320 1321 // Show the updated value 1322 // Hide after 2 seconds 1323 window.abt_highlight = setTimeout(function() { 1324 jQuery(selector).text(originalText).css('background-color', jQuery(selector).data('original-background-color')); 1325 }, 2000); 1326 } 1327 1328 var shownEmpty = false;
1329 setTimeout(function() { 1330 jQuery('.abst-element-column.active .abst-variant').each(function() { 1331 if(jQuery(this).val() == ''){ 1332 if(!shownEmpty){ 1333 console.log('empty'); 1334 jQuery(this).show().parents('.abst-section').removeClass('abst-hidden'); 1335 shownEmpty = true; 1336 } 1337 else{ 1338 jQuery(this).parents('.abst-section').addClass('abst-hidden'); 1339 } 1340 } 1341 else 1342 { 1343 jQuery(this).show().parents('.abst-section').removeClass('abst-hidden'); 1344 } 1345 }); 1346 }, 100); 1347 }); 1348 //on select.goal-type change if it equals 'page' then append a pageselector dropdown that updates the input value next to it 1349 jQuery('body').on('change', '.abst-goals-container select.goal-type', function() { // update hidden input value 1350 jQuery(this).parents('.abst-goals-container').find('.abst-goal-input-value').val(''); 1351 var type = jQuery(this).val(); 1352 var goalContainer = jQuery(this).parents('.abst-goals-container'); 1353 //array of special pages to tranfer value into value 1354 var specialPages = ['woo-order-pay', 'woo-order-recieved', 'woo']; 1355 if(type == 'page'){ 1356 pagesWaiter = setInterval(function() { 1357 if(abst_magic_data.pages && abst_magic_data.pages.length > 0){ 1358 clearInterval(pagesWaiter); 1359 var pageSelect = '<select class="abst-goal-page-input">'; 1360 abst_magic_data.pages.forEach(function(page) { 1361 pageSelect += '<option class="abst-goal-page-input" value="' + page[0] + '">' + page[1] + '</option>'; 1362 }); 1363 pageSelect += '</select>'; 1364 goalContainer.find('.abst-goal-input-value').hide(); 1365 goalContainer.append(pageSelect); 1366 } 1367 }, 100); 1368 } 1369 else if (specialPages.includes(type) || abIsInt(type)){ 1370 goalContainer.find('.abst-goal-input-value').show().val(type); 1371 console.log('special type', type); 1372 } 1373 else { 1374 goalContainer.find('.abst-goal-input-value').show(); 1375 jQuery(this).parents('.abst-goals-container').find('.abst-goal-page-input').remove(); 1376 } 1377 }).trigger('change'); 1378 1379 jQuery('body').on('change', '.abst-goal-page-input', function() { // update hidden input value 1380 var page_id = jQuery(this).val(); 1381 console.log('page_id', page_id); 1382 jQuery(this).parents('.abst-goals-container').find('.abst-goal-input-value').val(page_id); 1383 }); 1384 1385 jQuery('body').on('click','#abst-magic-bar-start',function(){ 1386 //get all magic data 1387 var magic_data = []; 1388 jQuery('.abst-magic-variants').each(function() { 1389 var selector = jQuery(this).find('.abst-magic-element-selector').val(); 1390 var variations = []; 1391 jQuery(this).find('.abst-variant').each(function() { 1392 variations.push(jQuery(this).val()); 1393 }); 1394 magic_data.push({ 1395 selector: selector, 1396 variations: variations 1397 }); 1398 }); 1399 1400 //if theres no magicdata then alert 1401 if(!jQuery('.abst-element-column.active').length || !jQuery('.abst-magic-b-value').val()) { 1402 alert('Please add at least one element to the test'); 1403 return; 1404 } 1405 1406 if(!jQuery('.abst-goals-container').first().find('.abst-goal-input-value').val()) { 1407 alert('Please add at least one goal to the test'); 1408 return; 1409 } 1410 1411 var newTestData = { 1412 action: 'create_new_on_page_test', 1413 post_title: jQuery('#abst-magic-bar-title').val(), 1414 post_id: 'new', 1415 magic_definition: JSON.stringify(get_magic_definition_from_dom()), 1416 test_type: 'magic', 1417 conversion_style: 'bayesian', 1418 bt_experiments_url_query: jQuery("#abst-url-query").val(), 1419 bt_experiments_conversion_page: jQuery(".abst-goals-container").first().find('select').val(), 1420 bt_experiments_conversion_page_selector: '', 1421 bt_experiments_conversion_url: '', 1422 bt_experiments_conversion_selector: '', 1423 bt_experiments_full_page_default_page: '', 1424 css_test_variations: '', 1425 bt_experiments_conversion_time: '', 1426 bt_experiments_conversion_text: '', 1427 bt_experiments_target_option_device_size: jQuery('#abst-device-size').val(), 1428 bt_experiments_target_percentage: jQuery('#abst-traffic-percentage').val(), 1429 bt_allowed_roles: jQuery('#abst-user-roles-container input[type="checkbox"]:checked').map(function() { 1430 return jQuery(this).val(); 1431 }).get(), 1432 bt_experiments_conversion_order_value: 0, 1433 goal: get_goals_from_dom(), 1434 1435 }; 1436 1437 if(jQuery(".abst-goals-container").first().find('select').val() == 'page') 1438 newTestData.bt_experiments_conversion_page = jQuery(".abst-goals-container").first().find('.abst-goal-input-value').val(); 1439 1440 if(jQuery(".abst-goals-container").first().find('select').val() == 'url') 1441 newTestData.bt_experiments_conversion_url = jQuery(".abst-goals-container").first().find('.abst-goal-input-value').val(); 1442 1443 if(jQuery(".abst-goals-container").first().find('select').val() == 'time') 1444 newTestData.bt_experiments_conversion_time = jQuery(".abst-goals-container").first().find('.abst-goal-input-value').val(); 1445 1446 if(jQuery(".abst-goals-container").first().find('select').val() == 'text') 1447 newTestData.bt_experiments_conversion_text = jQuery(".abst-goals-container").first().find('.abst-goal-input-value').val(); 1448 1449 jQuery.ajax({
1450 url: bt_ajaxurl, 1451 type: 'POST', 1452 data: newTestData, 1453 success: function(response) { 1454 //redirect to experiment page 1455 response = JSON.parse(response); 1456 if(response.post_title && response.post_title !== ''){ 1457 alert(response.post_title + " created"); 1458 close_abst_magic_bar(); 1459 } 1460 } 1461 }); 1462 1463 }); 1464 }); 1465})(jQuery); 1466 1467function get_goals_from_dom(){ 1468 1469 var goals = {}; 1470 jQuery('.abst-goals-container').each(function(index){ 1471 if(index === 0) 1472 return; 1473 1474 var sel = jQuery(this).find('select').val(); 1475 var val = jQuery(this).find('input').val(); 1476 1477 // Create an object with the dynamically named property 1478 var goalObj = {}; 1479 goalObj[sel] = val; 1480 1481 // Add to goals object with index as key (starting from 1) 1482 goals[index] = goalObj; 1483 }); 1484 console.log(goals); 1485 1486 return goals; 1487} 1488 1489 1490function get_magic_definition_from_dom(){ 1491 var magic_definition = []; 1492 jQuery('.abst-element-column').each(function(){ 1493 var selector = jQuery(this).find('.abst-magic-element-selector').val(); 1494 var type = jQuery(this).find('.abst-element-title-type').text().trim().toLowerCase(); 1495 var variations = []; 1496 jQuery(this).find('.abst-variant').each(function(){ 1497 if(jQuery(this).val() !== '') 1498 variations.push(jQuery(this).val()); 1499 }); 1500 if(type && type !== '' && selector && selector !== '' && variations.length > 1){ 1501 magic_definition.push({ 1502 type: type, 1503 selector: selector, 1504 variations: variations 1505 }); 1506 } 1507 }); 1508 return magic_definition; 1509} 1510 1511function get_abst_pageselector() { 1512 jQuery.ajax({ 1513 url: bt_ajaxurl, 1514 type: 'GET', 1515 data: { 1516 action: 'ab_page_selector' 1517 }, 1518 success: function(response) { 1519 /* returns 1520 [[759,"noconversionstestpage : [PAGE] noconversionstestpage"],[2376,"wpbakery : [PAGE] wpbakery"],[33,"About Us : [PAGE] about-us"],[2310," : [PAGE] 2310-2"],[2342,"powerpack test test : [PAGE] powerpack-test-test"],[1437,"About Us : [PAGE] about-me"],[2334,"blocks fix : [PAGE] 2334-2"],[2250,"Elementor #2250 : [PAGE] elementor-2250"],[2239,"pl : [PAGE] pl"],[519,"Home : [PAGE] home-2"],[2202,"anwp : [PAGE] anwp"],[2153,"New home : [PAGE] elementor-2153"],[382,"AI : [PAGE] ai"],[491,"Contact 2 : [PAGE] contact-2"],[85,"one : [PAGE] one"],[34,"About Me : [PAGE] about-me"],[58,"test ai : [PAGE] 58-2"],[837,"tuyjeryejt : [POST] tuyjeryejt"],[836,"sdfhghsfdfgh : [POST] sdfhghsfdfgh"],[834,"wtrehrwhwqr : [POST] wtrehrwhwqr"],[839,"ekjmtsy : [POST] ekjmtsy"],[838,"sfgj : [POST] sfgj"],[425,"cghngdnghncn : [PAGE] 425-2"],[3,"Privacy Policy : [PAGE] privacy-policy"],[173,"asdfasdf : [POST] 173"],[930,"123 : [POST] 123-2"],[929,"ret : [POST] ret"],[928,"ehg : [POST] ehg"],[914,"dhtgfm n : [POST] dhtgfm-n"],[915,"itu,y : [POST] ituy-2"],[913,"trhw : [POST] trhw"],[916,"t,i : [POST] ti"],[917,"ui : [POST] ui"],[918,"it,uy : [POST] ituy"],[919,"m : [POST] m"],[920,"rhmgt, : [POST] rhmgt"],[921,"mhg : [POST] mhg"],[912,"...54wy : [POST] 54wy"],[931,"123 : [POST] 123"],[923,"gfhjhgjnm : [POST] gfhjhgjnm"],[922,"ghfm : [POST] ghfm"],[779,"order complete : [PAGE] order-complete"],[772,"Dashboard : [PAGE] customer-dashboard"],[774,"Shop : [PAGE] shop-2"],[771,"Checkout : [PAGE] checkout-2"],[766,"test page 2 : [PAGE] test-page-2"],[764,"test page one : [PAGE] test-page-one"],[741,"Generateblocks 2023 : [PAGE] generateblocks-2023"],[257,"text on page conversion : [PAGE] text-on-page-conversion"],[735,"top2test : [PAGE] top2test"]] 1521 */ 1522 //loop thropugh array and add select option to for each 1523 // add a false, choose page at the start of the array 1524 response = JSON.parse(response); 1525 response.unshift(['', "Choose a Page"]); 1526 window.abst_magic_data.pages = response; 1527 } 1528 }); 1529} 1530function abIsInt(value) { 1531 return !isNaN(value) && parseInt(Number(value)) == value && !isNaN(parseInt(value, 10)); 1532}
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.