The JS mabol.js
The js file contains all the functions, small or large that go to initiate/terminate the completion of MabolAi actions.
Many functions are simply show/hide while others make calls to the endpoint to get values.
Some in detail are in the topic dedicated to problems.
/* ==================================================================
VAR GLOBALI
================================================================== */
//il converter di showdown
var converter = new showdown.Converter();
converter.setOption('noHeaderId', true);
//converter.setOption('ghCodeBlocks', false);
/* ==================================================================
MABOL AI Funzioni utilizzate da Mabol AI
================================================================== */
MabolAI = {
//Limita inserimento a numeri
soloNum: function (event)
{
allowedKeys = ['Backspace','1','2','3','4','5','6','7','8','9','0']
if (!allowedKeys.includes(event.key)) {
event.preventDefault();
return false;
} ;
},
//Limita inserimento a numeri e punto
soloNumPunto: function (event)
{
allowedKeys = ['Backspace','.','1','2','3','4','5','6','7','8','9','0']
if (!allowedKeys.includes(event.key)) {
event.preventDefault();
return false;
} ;
},
//Limita inserimento a numeri, punto e meno per i negativi
soloNumPuntoMeno: function (event)
{
allowedKeys = ['Backspace','.','1','2','3','4','5','6','7','8','9','0','-']
if (!allowedKeys.includes(event.key)) {
event.preventDefault();
return false;
} ;
},
//Crea src img da sola stringa base64
getBase64Src: function (base64String)
{
const binString = atob(base64String);
const byteArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
const arr = (new Uint8Array(byteArray)).subarray(0, 4);
let header = "";
for (let i = 0; i < arr.length; i++) {
header += arr[i].toString(16);
}
var base64Src = 0;
switch (header) {
case "89504e47":
base64Src = "data:image/png;";
break;
case "47494638":
base64Src = "data:image/gif;";
break;
case "ffd8ffe0":
case "ffd8ffe1":
case "ffd8ffe2":
case "ffd8ffe3":
case "ffd8ffe8":
base64Src = "data:image/jpeg;";
break;
case "52494646": // RIFF
if (byteArray[8] === 87 && byteArray[9] === 69 && byteArray[10] === 66 && byteArray[11] === 80) { // 'WEBP'
base64Src = "data:image/webp;";
}
break
}
if(base64Src){
base64Src += 'base64,'+base64String;
}
return base64Src;
},
//Apre messaggio informativo in base al tipo 1 ok, 2 errore
apriMess: function (classe, frase1, frase2)
{
const contMess = document.getElementById("mabolai-mess");
//Lavoro per icona
var icona = '';
if(classe = 'messOk'){
icona = '<i class="icon fa-check"></i>';
}else{
icona = '<i class="icon fa-times"></i>';
}
//la div col messaggio
var htmlDiv = '<div class="'+classe+'">';
htmlDiv += '<div class="messIco">'+icona+'</div>';
htmlDiv += '<div class="contTest">';
htmlDiv += '<div class="messTest messBold">'+frase1+'</div>';
htmlDiv += '<div class="messTest">'+frase2+'</div>';
htmlDiv += '</div>';
htmlDiv += '</div>';
contMess.innerHTML += htmlDiv;
contMess.style.display="flex";
setTimeout(()=> contMess.style.display="none", 1000);
setTimeout(()=> contMess.querySelector('.'+classe).remove(), 1100)
},
[...]
};
//fine MabolAI
When the generate button is clicked, the following function is called, which shunts, as appropriate, to other specifics for Ai and type of request.
//SMISTA LA Generazione dei Dati in base alla AI
getDataAI: function (tipoRic, att=false, obj=false)
{
//Altre var
const box = document.getElementById(tipoRic);
const selAi = box.querySelector(".selAi").value;
const boxAi = box.querySelector("."+selAi);
const errPrompt = boxAi.querySelector(".errori_prompt");
const errPara = boxAi.querySelector(".errori_para");
//apro loader
document.getElementById("mabolai-loader").style.display="block";
//Cancello eventuali errori nella finestra
boxAi.querySelector(".mabolai_errore").innerHTML = "";
if(tipoRic != 'desc_img') {
//svuoto ris generati e nascondo div dei risu
boxAi.querySelector(".cont-risu-gen-data").innerHTML = "";
boxAi.querySelector(".cont-risu").classList.add("chiusa");
}
//Reset errori su "input"
boxAi.querySelectorAll('.mai-textarea').forEach(item => {
item.classList.remove("errore");
})
if(tipoRic == 'gen_cont' || tipoRic == 'gen_campi' || tipoRic == 'mig_campi' || tipoRic == 'text_free' || tipoRic == 'trad_ris' || tipoRic == 'trad_free'){
boxAi.querySelectorAll('.mai_para').forEach(item => {
item.classList.remove("errore");
})
boxAi.querySelectorAll('.selRole').forEach(item => {
item.classList.remove("errore");
})
boxAi.querySelectorAll('.selModel').forEach(item => {
item.classList.remove("errore");
})
//Svuoto div errori
errPara.innerHTML = "";
errPara.style.display="none";
}
//Svuoto div errori
errPrompt.innerHTML = "";
errPrompt.style.display="none";
//decido dove smistare
if(selAi == 'aiOpen'){
if(tipoRic == 'gen_cont' || tipoRic == 'gen_campi' || tipoRic == 'mig_campi' || tipoRic == 'text_free' || tipoRic == 'trad_ris' || tipoRic == 'trad_free'){
MabolAI.getDataAITextOpen(boxAi, selAi, tipoRic);
}
if(tipoRic == 'gen_img'){
MabolAI.getDataAIGenImgOpen(boxAi, selAi, tipoRic);
}
if(tipoRic == 'desc_img'){
MabolAI.getDataAIDescImgOpen(boxAi, selAi, tipoRic, att, obj);
}
if(tipoRic == 'audio_ris' || tipoRic == 'audio_free'){
MabolAI.getDataAIAudioOpen(boxAi, selAi, tipoRic);
}
}
if(selAi == 'aiClaude'){
if(tipoRic == 'gen_cont' || tipoRic == 'gen_campi' || tipoRic == 'mig_campi' || tipoRic == 'text_free' || tipoRic == 'trad_ris' || tipoRic == 'trad_free'){
MabolAI.getDataAITextClaude(boxAi, selAi, tipoRic);
}
if(tipoRic == 'desc_img'){
MabolAI.getDataAIDescImgClaude(boxAi, selAi, tipoRic, att, obj);
}
}
if(selAi == 'aiMistral'){
if(tipoRic == 'gen_cont' || tipoRic == 'gen_campi' || tipoRic == 'mig_campi' || tipoRic == 'text_free' || tipoRic == 'trad_ris' || tipoRic == 'trad_free'){
MabolAI.getDataAITextMistral(boxAi, selAi, tipoRic);
}
if(tipoRic == 'desc_img'){
MabolAI.getDataAIDescImgMistral(boxAi, selAi, tipoRic, att, obj);
}
}
if(selAi == 'aiGemini'){
if(tipoRic == 'gen_cont' || tipoRic == 'gen_campi' || tipoRic == 'mig_campi' || tipoRic == 'text_free' || tipoRic == 'trad_ris' || tipoRic == 'trad_free'){
MabolAI.getDataAITextGemini(boxAi, selAi, tipoRic);
}
if(tipoRic == 'gen_img'){
MabolAI.getDataAIGenImgGemini(boxAi, selAi, tipoRic);
}
if(tipoRic == 'desc_img'){
MabolAI.getDataAIDescImgGemini(boxAi, selAi, tipoRic, att, obj);
}
}
if(selAi == 'aiDeepl'){
if(tipoRic == 'mig_campi' || tipoRic == 'trad_ris' || tipoRic == 'trad_free'){
MabolAI.getDataAITextDeepl(boxAi, selAi, tipoRic);
}
}
if(selAi == 'aiGetimg'){
if(tipoRic == 'gen_img'){
MabolAI.getDataAIGenImgGetimg(boxAi, selAi, tipoRic);
}
}
if(selAi == 'aiStability'){
if(tipoRic == 'gen_img'){
MabolAI.getDataAIGenImgStability(boxAi, selAi, tipoRic);
}
}
if(selAi == 'aiEleven'){
if(tipoRic == 'audio_ris' || tipoRic == 'audio_free'){
MabolAI.getDataAIAudioEleven(boxAi, selAi, tipoRic);
}
}
},
The following is the one specific to openAi that has text as the answer.
//OPEN AI - OPEN AI - OPEN AI - OPEN AI - OPEN AI - OPEN AI - OPEN AI - OPEN AI
getDataAITextOpen: function (boxAi, qualeAi, tipoRic)
{
//Loader aperto, errori resettati, risultati chiusi
//Altre var
const errPrompt = boxAi.querySelector(".errori_prompt");
const errPara = boxAi.querySelector(".errori_para");
var errore = false;
//Parametri senza controllo di errore, eventualmente ritorna errore da API Open AI
const modelLink = boxAi.querySelector(".selService").value;
const model = boxAi.querySelector(".selModel").value;
var store = boxAi.querySelector(".selStore").value;
if(store == 'false'){
store = false;
}else{
store = true;
}
//Parametri numerici senza controllo di errore, vanno lavorati a seconda dei casi
var tokens = parseInt(boxAi.querySelector(".para_tokens").innerText.trim());
var temp = parseFloat(boxAi.querySelector(".para_temp").innerText.trim());
if(temp < 0){temp = 0}
if(temp > 2){temp = 2}
var p = parseFloat(boxAi.querySelector(".para_p").innerText.trim());
if(p < 0){p = 0}
if(p > 1){p = 1}
var n = parseInt(boxAi.querySelector(".para_n").innerText.trim());
if(n > 10){n = 10}
var frequency_penalty = parseFloat(boxAi.querySelector(".para_frequency_penalty").innerText.trim());
if(frequency_penalty < -2){frequency_penalty = -2}
if(frequency_penalty > 2){frequency_penalty = 2}
var presence_penalty = parseFloat(boxAi.querySelector(".para_presence_penalty").innerText.trim());
if(presence_penalty < -2){presence_penalty = -2}
if(presence_penalty > 2){presence_penalty = 2}
//Creo il prompt
//PROMPT !
if(tipoRic == 'trad_ris' || tipoRic == 'trad_free'){
var usaSelLang = boxAi.querySelector(".checkSelLangTrad");
const promptPre = boxAi.querySelector(".prompt_pre");
const promptPreFissa = boxAi.querySelector(".prompt_pre_fissa");
if(usaSelLang.checked == true){
var prompt1 = promptPreFissa.innerText.trim();
}else{
var prompt1 = promptPre.innerText.trim();
}
prompt1 += boxAi.querySelector(".prompt_ini").innerText.trim();
}else{
var prompt1 = boxAi.querySelector(".prompt_ini").innerText.trim();
}
//PROMPT 2
if(tipoRic == 'text_free'){
var prompt2 = boxAi.querySelector(".prompt_fine").innerText.trim();
var prompt2PerErr = boxAi.querySelector(".prompt_fine").innerText.trim();
}else if(tipoRic == 'trad_free'){
var prompt2 = boxAi.querySelector(".prompt_fine").innerHTML.trim();
var prompt2PerErr = boxAi.querySelector(".prompt_fine").innerText.trim();
}else{
var prompt2 = boxAi.querySelector(".prompt_dati").innerHTML.trim();
prompt2 += boxAi.querySelector(".prompt_fine").innerText.trim();
var prompt2PerErr = boxAi.querySelector(".prompt_dati").innerText.trim()+boxAi.querySelector(".prompt_fine").innerText.trim();
}
var promptAi = prompt1 + prompt2
var promptAiError = prompt1 + prompt2PerErr
//Prendo i role
const selRole1 = parseInt(boxAi.querySelector(".selRole1").value);
const selRole2 = parseInt(boxAi.querySelector(".selRole2").value);
var role1 = '';
var role2 = '';
switch(selRole1) {
case 1:
role1 = 'developer';
break;
case 2:
role1 = 'user';
break;
}
if(selRole2){role2 = 'user';}
//Errore stessi linguaggi in trad_ris e trad_free
if(tipoRic == 'trad_ris' || tipoRic == 'trad_free'){
var usaSelLang = boxAi.querySelector(".checkSelLangTrad");
if(usaSelLang.checked == true){
const langDa = boxAi.querySelector(".selLangTradDa").value;
const langA = boxAi.querySelector(".selLangTradA").value;
if(langDa == langA){
errore = true;
errPrompt.innerHTML += '<div class="singErr">'+_("mabolai.err_stessa_lingua")+'</div>';
}
}
}
//Errore prompt totale
if (!promptAiError){
errore = true;
errPrompt.innerHTML += '<div class="singErr">'+_("mabolai.err_no_prompt")+'</div>';
boxAi.querySelectorAll('.mai-textarea').forEach(item => {
item.classList.add("errore");
})
}
//Errore Ruoli, già lo fa onchange sulla select ma ridondiamo. Ruolo 1 sempre presente, la select impone un valore
//In pratica ruolo 2 non deve mai essere uguale o minore di ruolo 1, se esiste ruolo 2
if (selRole2 && selRole2 <= selRole1 ){
errore = true;
errPara.innerHTML += '<div class="singErr">'+_("mabolai.err_role")+'</div>';
boxAi.querySelector(".selRole1").classList.add("errore");
boxAi.querySelector(".selRole2").classList.add("errore");
}
//se role2 c'è anche role 1 e si procede con due role, else esiste solo role 1 che è fissato dalla select
if(role2){
//controllo che esista il prompt 1 e prompt 2, se tutto vuoto già controllato
if(!prompt1){
errore = true;
errPrompt.innerHTML += '<div class="singErr">'+_("mabolai.err_no_prompt1")+'</div>';
boxAi.querySelector(".prompt_ini").classList.add("errore");
}
if(!prompt2PerErr){
errore = true;
errPrompt.innerHTML += '<div class="singErr">'+_("mabolai.err_no_prompt2")+'</div>';
boxAi.querySelector(".prompt_fine").classList.add("errore");
}
//Creo oggetto data da inviare alla AI, con 2 role e 2 prompt
if(modelLink == 'responses'){
var data = {
model: model,
store: store,
text: {format: {type: "text"}},
instructions: prompt1,
input: prompt2,
max_output_tokens: tokens,
temperature: temp,
top_p: p,
modelLink: modelLink,
miauscita: modelLink
};
}else{
var data = {
model: model,
store: store,
response_format: { "type": "text" },
messages: [{role: role1, content: prompt1},{role: role2, content: prompt2}],
max_completion_tokens: tokens,
temperature: temp,
top_p: p,
frequency_penalty: frequency_penalty,
presence_penalty: presence_penalty,
n: n,
modelLink: modelLink
};
}
}else{
//Un solo prompt già controllato
//Creo oggetto data da inviare alla AI, un solo ruolo e tutto il prompt
if(modelLink == 'responses'){
var data = {
model: model,
store: store,
text: {format: {type: "text"}},
input: [{role: role1, content: promptAi}],
max_output_tokens: tokens,
temperature: temp,
top_p: p,
modelLink: modelLink,
miauscita: modelLink
};
}else{
var data = {
model: model,
store: store,
response_format: { "type": "text" },
messages: [{role: role1, content: promptAi}],
max_completion_tokens: tokens,
temperature: temp,
top_p: p,
frequency_penalty: frequency_penalty,
presence_penalty: presence_penalty,
n: n,
modelLink: modelLink
};
}
}
// se errori presenti ritorno false e mostro div errori
if (errore) {
errPrompt.style.display="flex";
errPara.style.display="flex";
//Chiudo loader e ritorno
document.getElementById("mabolai-loader").style.display="none";
document.querySelector(".mabolai-actions-box").scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });
return false;
}
MabolAI.inviaAiRic(boxAi, qualeAi, tipoRic, data);
},
All AI and request type specific functions (genimg, gencont etc etc) send to the function calling endpoint.
//INVIO RICHIESTA - INVIO RICHIESTA - INVIO RICHIESTA - INVIO RICHIESTA - INVIO RICHIESTA - INVIO RICHIESTA - INVIO RICHIESTA - INVIO RICHIESTA - INVIO RICHIESTA
//Vale per tutti
async inviaAiRic(boxAi, qualeAi, tipoRic, data, att=false, obj=false)
{
var inizioT = new Date().getTime();
//cancello eventuale input rimasto in precedenza
const jsFileTemp = document.getElementById("jsFileTemp");
if(jsFileTemp){jsFileTemp.remove();}
const tab = document.querySelector(".mabolai-actions-box");
const loader = document.getElementById("mabolai-loader");
const contErr = boxAi.querySelector(".mabolai_errore");
//Gestione errori mio endpoint
const erroreGest = async (response) => {
if (!response.ok) {
const ris = await response.json();
if (ris?.error) {
loader.style.display="none";
contErr.innerHTML = "<pre>" + ris.error.message + "</pre>";
tab.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });
throw new Error(ris.error.message);
}
loader.style.display="none";
contErr.innerHTML = `${response.status} ${response.statusText}`;
tab.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });
throw new Error(`${response.status} ${response.statusText}`);
}
}
//la fetch che rimanda al mio endpoint
const response = await fetch(MODx.config.mabol_url_fun_mabolai + 'mabolAiFun.php', {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
funzione: 'inviaRicAi',
qualeAi: qualeAi,
tipoRic: tipoRic,
dataRic:data
})
});
//Errori dal mio endpoint
await erroreGest(response);
const contentType = response.headers.get('content-type');
if(contentType && contentType.includes('application/json')){
var ris = await response.json();
}else{
//un file
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const fileSize = blob.size;
// costruisco ris che viene poi passato
const fineT = new Date().getTime();
const tempo = ((fineT - inizioT) / 1000).toFixed(3);
const info = '<div class="ai_tempo">'+_("mabolai.ai_tempo")+' <span>'+tempo+'</span> '+_("mabolai.ai_sec")+'</div>';
var ris = {ok: true, data:[url], info:info, fileSize:fileSize};
//Creazione file temporaneo da prelevare in seguito
const file = new File([blob], "file_scaricato", { type: blob.type });
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
//creo input file dove inserire file
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.id = 'jsFileTemp';
fileInput.style.display = 'none';
boxAi.appendChild(fileInput);
fileInput.files = dataTransfer.files;
}
//Errori dalla chiamata
if (!ris.ok) {
if (ris?.error) {
loader.style.display="none";
contErr.innerHTML = "<pre>" + ris.error.message + "</pre>";
tab.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });
throw new Error(ris.error.message);
}
}
//lavoro con ris e ritorno a seconda dei casi
if(tipoRic == 'gen_cont' || tipoRic == 'gen_campi' || tipoRic == 'mig_campi' || tipoRic == 'text_free' || tipoRic == 'trad_ris' || tipoRic == 'trad_free'){
MabolAI.rispostaAIText(boxAi, qualeAi, tipoRic, ris);
}
if(tipoRic == 'gen_img'){
MabolAI.rispostaAIGenImg(boxAi, qualeAi, tipoRic, ris);
}
if(tipoRic == 'desc_img'){
MabolAI.rispostaAIDescImg(boxAi, qualeAi, tipoRic, ris, att, obj);
}
if(tipoRic == 'audio_ris' || tipoRic == 'audio_free'){
MabolAI.rispostaAIAudio(boxAi, qualeAi, tipoRic, ris);
}
},
As you can see depending on the case it uses certain parameters, or makes call with formdata or json.
All of these functions started from something simpler that then expanded so maybe in addition to being improvable, they contain elements that might not seem to make sense.
The send request function then sorts to show the result to other functions that depend ONLY on the type of request (response) and are independent of the AI type, even if the parameter is sent.
Next is the one that creates the response for an image generation
//RISPOSTA GEN IMG - RISPOSTA GEN IMG - RISPOSTA GEN IMG - RISPOSTA GEN IMG -
rispostaAIGenImg: function (boxAi, qualeAi, tipoRic, ris)
{
//Costruiamo il risultato almeno 1 sempre presente
const box = document.getElementById(tipoRic);
const divGen = boxAi.querySelector(".cont-risu-gen-data");
const divInfo = boxAi.querySelector(".cont-ai");
const tab = document.querySelector(".mabolai-actions-box");
const loader = document.getElementById("mabolai-loader");
const contErr = boxAi.querySelector(".mabolai_errore");
//Prendo dimensione immagine e tipo di uscita ed eventuale popup
var dimImg = 0;
var tipoUscita = 0;
var popUp = 0;
var titPopUp = '';
if(qualeAi == 'aiOpen') {
const selTipoUscita = boxAi.querySelector(".selFormRes");
const selModel = boxAi.querySelector(".selModel");
tipoUscita = selTipoUscita.value;
var titoloF = _("mabolai.dim_img");
if(selModel.value == 'dall-e-2'){
dimImg = boxAi.querySelector(".selSize2").value;
}else{
dimImg = boxAi.querySelector(".selSize3").value;
}
if(dimImg != '256x256' && dimImg != '512x512'){
popUp = 1;
titPopUp = titoloF+' '+dimImg;
}
}
if(qualeAi == 'aiGetimg') {
const selTipoUscita = boxAi.querySelector(".selFormRes");
const selModel = boxAi.querySelector(".selModel");
tipoUscita = selTipoUscita.value;
if(selModel.value == 'essential-v2'){
dimImg = boxAi.querySelector(".selaspect_ratio").value;
var titoloF = _("mabolai.rapporto_img");
}else{
var width = boxAi.querySelector(".para_width").innerText.trim();
var height = boxAi.querySelector(".para_height").innerText.trim();
dimImg = width+'x'+height;
var titoloF = _("mabolai.dim_img");
}
if(dimImg != '256x256' && dimImg != '512x512'){
popUp = 1;
titPopUp = titoloF+' '+dimImg;
}
}
if(qualeAi == 'aiStability') {
const selTipoUscita = boxAi.querySelector(".selFormRes");
tipoUscita = selTipoUscita.value;
var titoloF = _("mabolai.rapporto_img");
dimImg = boxAi.querySelector(".selaspect_ratio").value;
popUp = 1;
titPopUp = titoloF+' '+dimImg;
}
if(qualeAi == 'aiGemini') {
tipoUscita = 'b64';
var titoloF = _("mabolai.rapporto_img");
const asr = boxAi.querySelector(".selaspect_ratio");
if(asr){
dimImg = asr.value;
}else{
dimImg = '1:1';
}
popUp = 1;
titPopUp = titoloF+' '+dimImg;
}
//vedo eventuali tv da settare da settare
var setTV = 0;
const divSetTV = box.querySelector(".contSetTV");
if(divSetTV){
setTV = 1;
var contSetTV = divSetTV.innerHTML.trim();
}
//Ciclo sui risultati, le immagini
for (i=0; i<ris.data.length; i++) {
//vedo tipo uscita
if( tipoUscita == 'b64_json' || tipoUscita == 'b64') {
//routine per cercare MimeType, guardo che non sia presente
const mime = ris.data[i].trim().slice(0, 4);
if(mime == 'data'){
var imgSrc = ris.data[i].trim();
}else{
var res = MabolAI.getBase64Src(ris.data[i].trim());
if (res){
var imgSrc = res;
}else{
loader.style.display="none";
contErr.innerHTML = "<pre>"+_("mabolai.err_formato_uscita")+"</pre>";
tab.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });
return true;
}
}
}else if( tipoUscita == 'url' || tipoUscita == 'image') {
var imgSrc = ris.data[i].trim();
}else{
loader.style.display="none";
contErr.innerHTML = "<pre>"+_("mabolai.err_formato_uscita")+"</pre>";
tab.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });
return true;
}
var htmlDiv = '<div class="sing-gen">';
htmlDiv += '<div class="sing-gen-data"><img src="'+ imgSrc +'"></div>';
htmlDiv += '<div class="sing-gen-cont-desc">';
//Se c'è dimensione, praticamentre sempre
if(dimImg){
//Se c'è popUp
if(popUp){
htmlDiv += '<button class="sing-gen-popup" type="button" onclick="MabolAI.apriPopUp(this,\''+tipoRic+'\',\''+titPopUp+'\',\'src\',\''+imgSrc+'\')"><i class="icon fa-search-plus"></i></button>';
}
htmlDiv += '<div class="sing-gen-desc">'+titoloF+' <span>'+dimImg+'</span></div>';
}
htmlDiv += '<div class="contSettaMulti">';
//se base 64 copio altrimenti salvo e nel caso aggiungo setTv
if(tipoUscita == 'b64_json' || tipoUscita == 'b64'){
htmlDiv += '<div class="sing-gen-copia"><div class="nomeTV">'+ _("mabolai.copia_src_base64") +'</div><button type="button" onclick="MabolAI.copiaBase64(this)"><i class="icon icon-files-o"></i></button></div>';
}
htmlDiv += '<div class="sing-gen-copia"><div class="nomeTV">'+ _("mabolai.salva_img") +'<span>'+ _("mabolai.salva_img_percorso") +'</span></div><button type="button" onclick="MabolAI.salvaFile(this,\'img\')"><i class="icon fa-save"></i></button></div>';
//Se SetTv inserisco div coi set di TV
if(setTV){
htmlDiv += contSetTV;
}
htmlDiv += '</div></div></div>';
divGen.innerHTML += htmlDiv;
if( tipoUscita == 'image') {
//URL.revokeObjectURL(imgSrc);
}
}
divInfo.innerHTML = ris.info;
//Mostro e ritorno
boxAi.querySelector(".cont-risu").classList.remove("chiusa");
loader.style.display="none";
tab.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });
return true;
},