File: /var/www/intranet.kauko.lt/wp-content/plugins/js_composer/modules/ai/assets/js/ai-form-view.js
( function ( $ ) {
'use strict';
window.vc.AiFormView = Backbone.View.extend({
events: {
'click .vc_ai-generate-button': 'generateContent',
'change [name="contentType"]': 'changeContentType',
'input [name="prompt"]': 'changePrompt',
'click [data-vc-ui-element="button-save"]': 'insertContent',
'click .wpb-copy-output': 'copyContent'
},
seconds: 0,
minutes: 0,
timerInterval: null,
isGenerating: false,
maxWaitingCacheInterval: 900000,
maxPromptLength: 2000,
initialize: function ( options ) {
this.toggleModalPromoClass( options.data.type );
this.$el.find( '.vc_ui-helper-modal-ai-preloader' ).after( options.data.content );
this.setFormElements();
$( '.edit-form-info' ).initializeTooltips();
},
render: function ( options ) {
if ( this.timerInterval ) {
this.clearTimer();
}
this.toggleModalPromoClass( options.type );
this.$form.after( options.content );
this.$form.remove();
this.setFormElements();
$( '.edit-form-info' ).initializeTooltips();
return this;
},
setFormElements: function () {
this.$form = this.$el.find( '.vc_ui-panel-content-container' );
this.$generate_button = this.$el.find( '.vc_ai-generate-button' );
this.$close_button = this.$el.find( '[data-vc-ui-element="button-close"]' );
this.$insert_button = this.$el.find( '[data-vc-ui-element="button-save"]' );
this.$generated_content = this.$el.find( '.wpb_ai-generated-content' );
this.$prompt_field = this.$el.find( '[name="prompt"]' );
this.$generate_placeholder = this.$el.find( '.vc_ui-helper-modal-ai-placeholder' );
this.$generate_placeholder_timer = this.$generate_placeholder.find( '.vc_ai-timer' );
this.initialButtonText = this.$generate_button.text().trim();
this.contentType = this.$el.find( '[name="contentType"]' ).val();
if ( 'new_content' === this.contentType && !this.$prompt_field.val().trim() ) {
this.disableButton();
}
},
generateContent: function ( e ) {
e.preventDefault();
var _this = this;
// trim the prompt field value if user has set it programmatically
var promptWords = this.$prompt_field.val().split( ' ' );
if ( this.maxPromptLength < promptWords.length ) {
this.$prompt_field.val( promptWords.slice( 0, this.maxPromptLength ).join( ' ' ) );
}
var init_data = this.$form.find( ':visible:not([style*="display: none"]), [name="prompt"], input[type="hidden"]' ).serializeArray();
var cache_id = this.getUniqueCacheId();
init_data.push({ name: 'cacheId', value: cache_id });
this.$generated_content.val( '' );
var data = {
action: 'wpb_ai_api_get_response',
data: init_data,
_vcnonce: window.vcAdminNonce
};
this.isGenerating = true;
this.$generate_placeholder.removeClass( 'vc_ui-hidden' );
this.$generate_placeholder.addClass( 'wpb-generating-content' );
this.timerInterval = setInterval( this.updateTimer.bind( this ), 1000 );
// we break first request if you do not get response in 20 seconds.
// if we do not get response in 20 seconds then we save response data in cache on the remote server
// and then process another bunch of ajax requests to check if cache is ready
$.ajax({
type: 'POST',
url: window.ajaxurl,
timeout: 20000,
data: data
}).done( function ( response ) {
if ( !_this.isGenerating ) {
return false;
}
if ( true === response.success ) {
_this.$generated_content.val( response.data );
_this.resetButton( true );
_this.$insert_button.show();
_this.tokenUsageUpdate();
_this.toggleCopyButton();
} else {
// error returned by wpbakery server api
if ( response && response.data && response.data[0] && response.data[0].code && response.data[0].message ) {
console.error( response.data[0].code, response.data[0].message );
_this.resetButton( false );
var message = response.data[0].message.replace( /\\/g, '' );
_this.showErrorMessage( message );
} else {
console.error( _this.getLocale().ai_response_error );
_this.resetButton( false );
_this.showErrorMessage( _this.getLocale().ai_response_error );
}
}
}).fail( function ( response ) {
if ( !_this.isGenerating ) {
return false;
}
if ( response && !response.statusText ) {
console.error( _this.getLocale().ai_response_error );
_this.resetButton( false );
_this.showErrorMessage( _this.getLocale().ai_response_error );
return;
}
if ( 'timeout' !== response.statusText ) {
console.error( _this.getLocale().ai_response_error );
_this.resetButton( false );
_this.showErrorMessage( _this.getLocale().ai_response_error );
return;
}
var data = {
action: 'wpb_ai_generate_content_check_cache',
data: {
type: 'generate-text',
messaged_data: true,
cacheId: cache_id
},
_vcnonce: window.vcAdminNonce
};
// we create a timer to check if cache is ready every 10 seconds for 5 minutes
var timeouts = [];
for ( var time_interval = 10000; time_interval <= _this.maxWaitingCacheInterval; time_interval += 10000 ) {
createTimeout( time_interval );
}
function createTimeout ( interval ) {
timeouts.push( setTimeout( function () {
var output_value = _this.$generated_content.val();
if ( output_value ) {
// stop all other timeouts related to cache checking
for ( var i = 0; i < timeouts.length; i++ ) {
if ( 'stop_cache_timeouts' === output_value ) {
_this.$generated_content.val( '' );
}
clearTimeout( timeouts[i]);
}
} else {
_this.processCachedRequest( _this, data, interval );
}
}, interval ) );
}
});
},
processCachedRequest: function ( _this, data, time_interval ) {
// last timer is pass then it mean that we still do not have a content
if ( this.maxWaitingCacheInterval === time_interval ) {
console.error( _this.getLocale().ai_response_error );
_this.resetButton( false );
_this.showErrorMessage( _this.getLocale().ai_response_error );
} else {
// any other timer then last we process request to check cache
$.ajax({
type: 'POST',
url: window.ajaxurl,
timeout: 10000,
data: data
}).done( function ( response ) {
if ( !_this.isGenerating ) {
return false;
}
if ( true === response.success && response.data && 'cache_in_process' !== response.data ) {
_this.$generated_content.val( response.data );
_this.resetButton( true );
_this.$insert_button.show();
}
// we have a cache response but first request that created cache was failed
if ( false === response.success && response && response.data && response.data[0] && response.data[0].code && response.data[0].message ) {
_this.$generated_content.val( 'stop_cache_timeouts' );
_this.resetButton( false );
var message = response.data[0].message.replace( /\\/g, '' );
_this.showErrorMessage( message );
}
});
}
},
tokenUsageUpdate: function () {
var data = {
action: 'wpb_ai_get_token_usage',
data: {},
_vcnonce: window.vcAdminNonce
};
var _this = this;
$.ajax({
type: 'POST',
url: window.ajaxurl,
data: data
}).done( function ( response ) {
var is_token_text = undefined !== response.data.tokens_left && undefined !== response.data.tokens_total;
if ( true === response.success && is_token_text ) {
var token_usage_text =
_this.getLocale().ai_credit_usage + response.data.tokens_left + ' / ' + response.data.tokens_total;
$( '.vc-ai-tokens-usage' ).text( token_usage_text );
} else {
// error returned by wpbakery server api
var is_error_message =
response &&
response.data &&
response.data[0] &&
response.data[0].code &&
response.data[0].message;
if ( is_error_message ) {
console.error( response.data[0].message );
_this.showErrorMessage( response.data[0].message );
} else {
console.error( _this.getLocale().ai_response_error );
_this.showErrorMessage( _this.getLocale().ai_response_error );
}
}
}).fail( function () {
console.error( _this.getLocale().ai_response_error );
_this.resetButton();
_this.showErrorMessage( _this.getLocale().ai_response_error );
});
},
getUniqueCacheId: function () {
return Date.now().toString( 36 ) + Math.random().toString( 36 ).slice( 2 );
},
disableButton: function () {
this.$generate_button.prop( 'disabled', function ( _, val ) {
return !val;
});
this.isGenerateDisabled = true;
},
resetButton: function ( isGenerated ) {
var buttonText = isGenerated ? 'Regenerate' : this.initialButtonText;
this.$generate_button.removeAttr( 'disabled style' );
this.$generate_button.text( buttonText );
this.$generate_button.blur();
this.clearTimer();
},
clearTimer: function () {
this.$generate_placeholder.addClass( 'vc_ui-hidden' );
this.$generate_placeholder.removeClass( 'wpb-generating-content' );
this.$generate_placeholder_timer.text( '00:00' );
clearInterval( this.timerInterval );
this.seconds = 0;
this.minutes = 0;
this.isGenerating = false;
},
updateTimer: function () {
this.seconds++;
if ( 60 === this.seconds ) {
this.seconds = 0;
this.minutes++;
}
var formattedMinutes = String( this.minutes ).padStart( 2, '0' );
var formattedSeconds = String( this.seconds ).padStart( 2, '0' );
this.$generate_placeholder_timer.text( formattedMinutes + ':' + formattedSeconds );
},
changeContentType: function ( e ) {
this.contentType = e.target.value;
var elementData = this.$el.data();
// hide form fields that do not match selected content type
var formFieldOptionalityList = $( e.target ).find( 'option:selected' ).attr( 'data-form-fields-optionality' );
formFieldOptionalityList = formFieldOptionalityList ? formFieldOptionalityList.split( '|' ) : [];
this.hideFormFields( formFieldOptionalityList );
// Set all form fields to default values except content type
this.$form.trigger( 'reset' );
this.$form.find( '[name="contentType"]' ).val( this.contentType );
// set the value of the "prompt" textarea with existing field value
if ( 'improve_existing' === e.target.value || 'translate' === e.target.value ) {
this.$generate_button.text( this.getLocale().regenerate );
var existingContent = elementData.element.val();
if ( 'textarea_raw_html' === elementData.param_type || 'textarea_ace' === elementData.param_type ) {
existingContent = rawurldecode( base64_decode( existingContent.trim() ) );
} else if ( 'textarea_html' === elementData.param_type ) {
existingContent = window.tinymce.get( elementData.element.attr( 'id' ) ).getContent();
}
this.$form.find( '[name="prompt"]' ).val( existingContent );
this.resetButton( true );
} else {
this.$generate_button.text( this.getLocale().generate );
this.$form.find( '[name="prompt"]' ).val( '' );
this.disableButton();
}
},
changePrompt: function ( e ) {
if ( this.isGenerateDisabled && e.target.value ) {
this.resetButton( false );
this.isGenerateDisabled = false;
} else if ( !e.target.value && !this.isGenerateDisabled ) {
this.disableButton();
}
// trim prompt value if it exceeds maxPromptLength
var promptWords = e.target.value.split( ' ' );
if ( promptWords.length > this.maxPromptLength ) {
e.target.value = promptWords.slice( 0, this.maxPromptLength ).join( ' ' );
}
},
showErrorMessage: function ( message ) {
window.vc.showMessage( message, 'error', 10000, '#vc_ui-helper-modal-ai .vc_ui-panel-window-inner' );
},
insertContent: function () {
var generatedContent = this.$generated_content.val();
if ( !generatedContent ) {
return false;
}
var currentParamData = this.$el.data();
var aiFields = [ 'textarea', 'textfield', 'textarea_raw_html', 'textarea_ace' ];
var aceEditorFields = [ 'wpb_css_editor', 'wpb_js_header_editor', 'wpb_js_footer_editor' ];
if ( 'textarea_html' === currentParamData.param_type || 'content' === currentParamData.fieldId ) {
var $textareaElement = currentParamData.element;
var textareaId = $textareaElement.attr( 'id' );
if ( 'new_content' === this.contentType ) {
var currentTextareaValue = $textareaElement.val();
var textAreaSpace = currentTextareaValue ? ' ' : '';
generatedContent = currentTextareaValue + textAreaSpace + generatedContent;
}
var tinyMCE = window.tinymce.get( textareaId );
if ( tinyMCE ) {
tinyMCE.setContent( generatedContent );
}
$textareaElement.val( generatedContent ).trigger( 'input' ).trigger( 'change' ).trigger( 'blur' );
} else if ( aiFields.includes( currentParamData.param_type ) ) {
var $inputElement = currentParamData.element;
if ( 'new_content' === this.contentType ) {
var currentInputValue = $inputElement.val();
if ( currentParamData.param_type !== 'textarea_ace' ) {
var inputSpace = currentInputValue ? ' ' : '';
generatedContent = currentInputValue + inputSpace + generatedContent;
}
}
if ( currentParamData.param_type === 'textarea_ace' ) {
var aceId = currentParamData.element.closest( '.edit_form_line' ).find( '.textarea_ace_container' ).attr( 'id' );
var aceEditor = window.ace.edit( aceId );
this.updateAceEditor( aceEditor, generatedContent );
} else {
$inputElement.val( generatedContent ).trigger( 'input' ).trigger( 'change' ).trigger( 'blur' );
}
} else if ( currentParamData.fieldId ) {
if ( aceEditorFields.includes( currentParamData.fieldId ) ) {
var aceEditor = window.ace.edit( currentParamData.fieldId );
this.updateAceEditor( aceEditor, generatedContent );
} else if ( currentParamData.element && currentParamData.element.length ) {
if ( 'new_content' === this.contentType ) {
var currentElementValue = currentParamData.element.val();
var elementSpace = currentElementValue ? ' ' : '';
generatedContent = currentElementValue + elementSpace + generatedContent;
}
currentParamData.element.val( generatedContent ).trigger( 'input' ).trigger( 'change' ).trigger( 'blur' );
}
}
this.$close_button.click();
},
updateAceEditor: function ( aceEditor, generatedContent ) {
var currentValue = aceEditor.getValue();
var $codeTextarea = $( aceEditor.container ).find( 'textarea' );
var emptySpace = '';
if( currentValue !== '' ) {
emptySpace = '\n\n';
}
generatedContent = currentValue + emptySpace + generatedContent;
aceEditor.setValue( generatedContent );
$codeTextarea.trigger( 'input' ).trigger( 'change' ).trigger( 'blur' );
},
toggleModalPromoClass: function ( type ) {
if ( 'promo' === type ) {
this.$el.addClass( 'vc_modal-ai-container--promo' );
} else {
this.$el.removeClass( 'vc_modal-ai-container--promo' );
}
},
hideFormFields: function ( optionalityList ) {
// hide form fields that do not match selected content type
this.$form.find( 'div[data-optional-form-field]' ).each( function () {
var $formField = $( this );
var formFieldSlug = $formField.attr( 'data-optional-form-field' );
if ( optionalityList.includes( formFieldSlug ) ) {
$formField.show();
} else {
$formField.hide();
}
});
},
getLocale: function () {
if ( window.i18nLocale ) {
return window.i18nLocale;
} else {
return window.i18nLocaleSettings;
}
},
toggleCopyButton: function () {
var $copyButton = this.$el.find( '.wpb-copy-output' );
if ( this.$generated_content.val() ) {
$copyButton.removeClass( 'disabled' );
} else {
$copyButton.addClass( 'disabled' );
}
},
copyContent: function ( e ) {
e.preventDefault();
var content = this.$generated_content.val();
if ( !content ) {
return false;
}
try {
window.vc.utils.copyTextToClipboard( content );
vc.showMessage( this.getLocale().copied, 'success', 2000, '#vc_ui-helper-modal-ai .vc_ui-panel-window-inner' );
} catch ( error ) {
console.error( 'Unable to copy content:', error );
}
}
});
})( window.jQuery );