PageSourceSearch

https://gobravo.pt/wp-content/plugins/bt-bb-ab/js/highlighter.js?ver=1.9.1

js gobravo.pt collected 2026-10-06 07:18:51 UTC 68,630 bytes, 1,532 lines download raw bytes

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.