diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index e5a0c071..21e142a7 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -1575,5 +1575,21 @@
"customDataPH_Import": {
"message": "Import new data placeholders",
"description": ""
+ },
+ "customDataPH_form_label_Text": {
+ "message": "Data Placeholder Text",
+ "description": ""
+ },
+ "customDataPH_saving_custom": {
+ "message": "Saving custom data placeholders...",
+ "description": ""
+ },
+ "customDataPH_saved": {
+ "message": "Custom data placeholders saved!",
+ "description": ""
+ },
+ "customDataPH_btnAddNewCommit": {
+ "message": "Add the Data Placeholder",
+ "description": ""
}
}
\ No newline at end of file
diff --git a/pages/customdataplaceholders/mzta-custom-dataplaceholders.css b/pages/customdataplaceholders/mzta-custom-dataplaceholders.css
new file mode 100644
index 00000000..888459df
--- /dev/null
+++ b/pages/customdataplaceholders/mzta-custom-dataplaceholders.css
@@ -0,0 +1,360 @@
+@media (prefers-color-scheme: light) {
+ table th {
+ background-color: #ffffff;
+ }
+
+ #command_palette{
+ background-color: #ffffff;
+ }
+
+ table.prompts_list tr:nth-child(even) {
+ background-color: #e9e9e9;
+ }
+
+ table.prompts_list tr:nth-child(odd) {
+ background-color: #f3f3f3;
+ }
+}
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ border: 1px solid #ccc;
+}
+
+thead {
+ position: sticky;
+ top: 26px;
+ z-index: 90;
+}
+
+#command_palette{
+ position: sticky;
+ top: 0;
+ padding-top: 1px;
+ height: 26px;
+ width: 100%;
+ z-index: 90;
+}
+
+#btnNew{
+ float: right;
+}
+
+#btnSaveAll{
+ float: left;
+}
+
+#msgDisplay{
+ padding-left: 40px;
+}
+
+table tr {
+ border-bottom: 1px solid #ccc;
+}
+
+table.prompts_list th {
+ padding: 8px;
+ text-align: left;
+}
+
+table td {
+ border: 1px solid #ddd;
+ padding: 8px;
+}
+
+table td.w08{
+ width: 8vw;
+}
+
+table td.w17{
+ width: 17vw;
+}
+
+table td.w19{
+ width: 19vw;
+}
+
+table td.w25{
+ width: 25vw;
+}
+
+table td.w40{
+ width: 40vw;
+}
+
+table.prompts_list td.properties{
+ white-space: nowrap;
+}
+
+table .sort {
+ background-image: url(../images/listjs-arrow-sort-inactive.png);
+ background-repeat: no-repeat;
+ background-position: right center;
+ padding-right: 14px;
+ cursor: pointer;
+ cursor: hand;
+}
+
+table .sort.asc {
+ background-image: url(../images/listjs-arrow-sort-active-down.png);
+}
+
+table .sort.desc {
+ background-image: url(../images/listjs-arrow-sort-active-up.png);
+}
+
+tbody.list tr{
+ vertical-align: top;
+}
+
+.hiddendata{
+ display: none;
+}
+
+#formNew{
+ display: none;
+ width: 100%;
+ padding-bottom: 7px;
+}
+
+.text_output{
+ width: 98%;
+ height: 9em;
+}
+
+.go_right_float{
+ float: right;
+}
+
+.go_right{
+ width: 100%;
+ text-align: right;
+}
+
+.width_all{
+ width: 100%;
+}
+
+.small_label{
+ font-size: small;
+ font-style: italic;
+}
+
+.field_title{
+ font-weight: bold;
+}
+
+.field_title_s{
+ font-weight: bold;
+ font-size: small;
+}
+
+.field_title_us{
+ font-weight: bold;
+ font-size: smaller;
+}
+
+label, .id_show, .text_show, .name_show, .type_show, .action_show{
+ font-size: small;
+}
+
+#txtIdNew, .id_output{
+ text-transform: lowercase;
+}
+
+#import_export{
+ position: absolute;
+ right:10px;
+ top:10px;
+ text-align: right;
+}
+
+#btnExportAll{
+ margin-bottom: 3px;
+}
+
+.storage_space{
+ font-size: 0.8em;
+ font-weight: normal;
+ font-style: italic;
+ color: rgb(41, 41, 41);
+ text-align: right;
+ margin-right: 1px;
+}
+
+.page_title{
+ margin-bottom: 0px;
+}
+
+.autocomplete-container {
+ position: relative;
+}
+
+.autocomplete-list {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ right: 0;
+ background-color: white;
+ border: 1px solid #ccc;
+ z-index: 1000;
+ max-height: 200px;
+ overflow-y: auto;
+ padding: 0;
+ margin: 0;
+ list-style: none;
+ font-size: small;
+}
+
+.autocomplete-list li {
+ padding: 8px;
+ cursor: pointer;
+}
+
+.autocomplete-list li:hover {
+ background-color: #f0f0f0;
+}
+
+.autocomplete-list li.active {
+ background-color: #ddd;
+}
+
+.hidden {
+ display: none;
+}
+
+.need_custom_text_span, .need_selected_span{
+ padding-right: 2px;
+}
+
+#chatgpt_web_additional_info_toggle td{
+ padding:0px 5px;
+ font-style: italic;
+}
+
+#chatgpt_web_additional_info, #chatgpt_web_additional_info_toggle, .chatgpt_web_additional_info, .chatgpt_web_additional_info_toggle, .chatgpt_web_additional_info_show{
+ background-color: rgb(255, 209, 183);
+ display:none;
+}
+
+.chatgpt_web_models_list_table{
+ background-color: rgb(255, 209, 183);
+ border:0;
+}
+
+.chatgpt_web_models_list_table *{
+ border:0;
+}
+
+.chatgpt_web_models_list_table td{
+ padding:2px;
+}
+
+#chatgpt_web_additional_info_toggle, .chatgpt_web_additional_info_toggle{
+ cursor: pointer;
+}
+
+#chatgpt_web_additional_info_toggle:hover, .chatgpt_web_additional_info_toggle:hover{
+ background-color: rgb(255, 189, 153);
+ text-decoration: underline;
+}
+
+#chatgpt_web_additional_info_toggle td{
+ text-align: center;
+}
+
+#chatgpt_web_additional_info td{
+ vertical-align: top;
+}
+
+.chatgpt_web_additional_info_toggle{
+ width: -moz-available;
+ text-align: center;
+ margin-top: 10px;
+}
+
+.chatgpt_web_additional_info_show{
+ flex-direction: column;
+ justify-content: space-between;
+ height: 100%;
+ padding: 3px;
+ margin-top: 5px;
+}
+
+.chatgpt_web_additional_info_row{
+ display: flex;
+}
+
+.chatgpt_web_additional_info_row span{
+ margin-right: 0.4em;
+}
+
+input.input_additional[type="text"]{
+ width: -moz-available;
+}
+
+.small_info{
+ font-size:smaller;
+}
+
+.conntype_chatgpt_web_option{
+ cursor: pointer;
+}
+
+@media (prefers-color-scheme: dark) {
+ body {
+ background-color: #1C1B22;
+ color: rgb(251, 251, 254);
+ }
+
+ a:link { color: #409EFF; }
+ a:visited { color: #409EFF; }
+ a:hover { color: #66B1FF; }
+ a:active { color: #66B1FF; }
+
+ table {
+ background-color: #1C1B22;
+ border: 1px solid #ccc;
+ }
+
+ table.prompts_list th {
+ background-color: #4c4e58;
+ border: 1px solid rgb(251, 251, 254);
+ color: rgb(194, 194, 194);
+ }
+
+ #command_palette{
+ background-color: inherit;
+ }
+
+ table.prompts_list tr:nth-child(even) {
+ background-color: #2E2F36;
+ }
+
+ .storage_space{
+ color: rgb(182, 182, 182);
+ }
+
+ .autocomplete-list {
+ background-color: #2E2F36;
+ border: 1px solid #2E2F36;
+ }
+
+ .autocomplete-list li:hover {
+ background-color: #4c4e58;
+ }
+
+ .autocomplete-list li.active {
+ background-color: #4c4e58;
+ }
+
+ #chatgpt_web_additional_info, #chatgpt_web_additional_info_toggle, .chatgpt_web_additional_info, .chatgpt_web_additional_info_toggle, .chatgpt_web_additional_info_show, .chatgpt_web_models_list_table{
+ background-color: rgb(39, 11, 0);
+ color: rgb(182, 182, 182);
+ }
+
+ #chatgpt_web_additional_info_toggle:hover, .chatgpt_web_additional_info_toggle:hover{
+ background-color: rgb(88, 25, 0);
+ }
+}
\ No newline at end of file
diff --git a/pages/customdataplaceholders/mzta-custom-dataplaceholders.html b/pages/customdataplaceholders/mzta-custom-dataplaceholders.html
new file mode 100644
index 00000000..ea95d2b4
--- /dev/null
+++ b/pages/customdataplaceholders/mzta-custom-dataplaceholders.html
@@ -0,0 +1,91 @@
+
+
+
+
+ ThunderAI - __MSG_customDataPH_manageDataPH__
+
+
+
+
+
+
__MSG_customDataPH_manageDataPH__
+
__MSG_customDataPH_manageDataPH_info_default__
+ __MSG_customPrompts_managePrompts_info_default_3__
+
__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_more_info_string__
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | __MSG_customPrompts_form_label_ID__ |
+ __MSG_customPrompts_form_label_Name__ |
+ __MSG_customDataPH_form_label_Text__ |
+ __MSG_customPrompts_Properties__ |
+ |
+
+
+
+
+
+
+ __MSG_StorageSpace__:
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/customdataplaceholders/mzta-custom-dataplaceholders.js b/pages/customdataplaceholders/mzta-custom-dataplaceholders.js
new file mode 100644
index 00000000..20f89755
--- /dev/null
+++ b/pages/customdataplaceholders/mzta-custom-dataplaceholders.js
@@ -0,0 +1,628 @@
+/*
+ * ThunderAI [https://micz.it/thunderbird-addon-thunderai/]
+ * Copyright (C) 2024 - 2025 Mic (m@micz.it)
+
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+import { prefs_default } from "../../options/mzta-options-default.js";
+import { getLocalStorageUsedSpace, sanitizeHtml, isThunderbird128OrGreater } from "../../js/mzta-utils.js";
+import { taLogger } from "../../js/mzta-logger.js";
+import { getPlaceholders, setCustomPlaceholders, getCustomPlaceholders, prepareCustomDataPHsForExport, prepareCustomDataPHsForImport, placeholdersUtils } from "../../js/mzta-placeholders.js";
+import { textareaAutocomplete } from "../../js/mzta-placeholders-autocomplete.js";
+
+let prefs = null;
+var customDataPHsList = null;
+var somethingChanged = false;
+var idnumMax = 0;
+var msgTimeout = null;
+let taLog = null;
+let autocompleteSuggestions = [];
+
+document.addEventListener('DOMContentLoaded', async () => {
+
+ prefs = await browser.storage.sync.get({ do_debug: prefs_default.do_debug });
+ taLog = new taLogger("mzta-custom-dataplaceholders", prefs.do_debug);
+
+ setStorageSpace();
+
+ let values = await getCustomPlaceholders();
+
+ //console.log('>>>>>>>>>>>>>>>> values: ' + JSON.stringify(values));
+
+ loadCustomDataPHsList(values);
+
+ const btnSaveAll = document.getElementById('btnSaveAll');
+ btnSaveAll.disabled = true;
+
+ // Disable save button and handle save actions
+ function handleSaveAllClick(e) {
+ e.preventDefault();
+ saveAll();
+ clearFields();
+ }
+ btnSaveAll.addEventListener('click', handleSaveAllClick);
+
+ const btnNew = document.getElementById('btnNew');
+
+ // Show the new item form
+ function handleNewClick(e) {
+ e.preventDefault();
+ e.target.disabled = true;
+ document.getElementById('formNew').style.display = 'block';
+ window.scrollTo({
+ top: 0,
+ behavior: 'smooth'
+ });
+ }
+ btnNew.addEventListener('click', handleNewClick);
+
+ // for the new prompt form
+ let btnNew_elements = document.querySelectorAll(".input_new");
+ if(btnNew_elements) {
+ btnNew_elements.forEach(element => {
+ element.addEventListener('change', (e) => {
+ e.preventDefault();
+ checkFields();
+ });
+ });
+ }
+
+ const textareas = document.querySelectorAll('.editor');
+ autocompleteSuggestions = (await getPlaceholders())
+ .filter(p => p.is_default == "1")
+ .map(p => ({ command: '{%' + p.id + '%}', type: p.type }));
+
+ // console.log('>>>>>>>>>>> suggestions: ' + JSON.stringify(suggestions));
+
+ textareas.forEach(textarea => {
+ textareaAutocomplete(textarea, autocompleteSuggestions);
+ textareas.forEach(textarea => {
+ textarea.addEventListener('input', (e) => {
+ checkPromptsConfigForPlaceholders(e.target);
+ });
+ textareaAutocomplete(textarea, autocompleteSuggestions);
+ });
+ });
+
+ i18n.updateDocument();
+
+ //To add a new item
+ var txtIdNew = document.getElementById('txtIdNew');
+ var txtNameNew = document.getElementById('txtNameNew');
+ var txtTextNew = document.getElementById('txtTextNew');
+ var selectTypeNew = document.getElementById('selectTypeNew');
+ var checkboxNeedSelectedNew = document.getElementById('checkboxNeedSelectedNew');
+ var checkboxNeedCustomTextNew = document.getElementById('checkboxNeedCustomTextNew');
+
+ const btnAddNew = document.getElementById('btnAddNew');
+ btnAddNew.addEventListener('click', (e) => {
+ e.preventDefault();
+ if(!checkFields()) {
+ return;
+ }
+ let newItemData = {
+ id: String(txtIdNew.value.trim()).toLocaleLowerCase(),
+ name: txtNameNew.value.trim(),
+ text: txtTextNew.value.trim(),
+ type: selectTypeNew.value,
+ need_selected: (checkboxNeedSelectedNew.checked) ? 1 : 0,
+ need_custom_text: (checkboxNeedCustomTextNew.checked) ? 1 : 0,
+ enabled: 1,
+ is_default: 0,
+ idnum: idnumMax + 1,
+ };
+
+ let newItem = customDataPHsList.add(newItemData);
+ idnumMax++;
+ let curr_idnum = newItem[0].values().idnum;
+ let checkboxes = document.querySelectorAll(`tr[data-idnum="${curr_idnum}"] input[type="checkbox"]`);
+ checkSelectedBoxes(checkboxes);
+ let editBtn = document.querySelector(`tr[data-idnum="${curr_idnum}"] button.btnEditItem`);
+ //console.log(`>>>>>>>>>>>> tr[data-idnum="${curr_idnum}"] button.btnEditItem`);
+ editBtn.addEventListener('click', handleEditClick);
+ //console.log('>>>>>>>>>>>>> editBtn: ' + JSON.stringify(editBtn));
+ let deleteBtn = document.querySelector(`tr[data-idnum="${curr_idnum}"] button.btnDeleteItem`);
+ //console.log(`>>>>>>>>>>>> tr[data-idnum="${curr_idnum}"] button.btnDeleteItem`);
+ deleteBtn.addEventListener('click', handleDeleteClick);
+ let okBtn = document.querySelector(`tr[data-idnum="${curr_idnum}"] button.btnConfirmItem`);
+ okBtn.addEventListener('click', handleConfirmClick);
+ let cancelBtn = document.querySelector(`tr[data-idnum="${curr_idnum}"] button.btnCancelItem`);
+ cancelBtn.addEventListener('click', handleCancelClick);
+ // console.log('>>>>>>>>>>>>> deleteBtn: ' + JSON.stringify(deleteBtn));
+ // console.log('>>>>>>>>>>>>> newItem: ' + JSON.stringify(newItem));
+ document.getElementById('btnNew').disabled = false;
+ clearFields();
+ setSomethingChanged();
+ i18n.updateDocument();
+ window.scrollTo({
+ top: document.body.scrollHeight,
+ behavior: 'smooth'
+ });
+ });
+
+ //Import Export
+ const btnExportAll = document.getElementById('btnExportAll');
+ btnExportAll.addEventListener('click', (e) => {
+ e.preventDefault();
+ exportPrompts();
+ });
+
+ async function exportPrompts() {
+ const manifest = browser.runtime.getManifest();
+ const addonVersion = manifest.version;
+ const outputPrompts = preparePromptsForExport(await getPrompts());
+ let outputObj = {id: 'thunderai-prompts', addon_version: addonVersion, prompts: outputPrompts};
+ const blob = new Blob([JSON.stringify(outputObj, null, 2)], {
+ type: "application/json",
+ });
+ const currentDate = new Date();
+ const time_stamp = `${currentDate.getFullYear()}${String(currentDate.getMonth() + 1).padStart(2, '0')}${String(currentDate.getDate()).padStart(2, '0')}${String(currentDate.getHours()).padStart(2, '0')}${String(currentDate.getMinutes()).padStart(2, '0')}${String(currentDate.getSeconds()).padStart(2, '0')}`;
+ messenger.downloads.download({
+ url: URL.createObjectURL(blob),
+ filename: `thunderai-prompts-${time_stamp}.json`,
+ saveAs: true,
+ });
+ }
+
+ const btnImport = document.getElementById('btnImport');
+ btnImport.addEventListener('click', (e) => {
+ e.preventDefault();
+ importPrompts();
+ });
+
+ function importPrompts() {
+ if(confirm(browser.i18n.getMessage("importPrompts_confirmText") + '\n' + browser.i18n.getMessage("customPrompts_managePrompts_info_default_2") + '\n' + browser.i18n.getMessage("customPrompts_managePrompts_info_default_3"))) {
+ //ask the user to choose a JSON file, and then read it, check if the serialized JSON is valid as generated from exportPrompts(), and if so, add it to the list
+ const input = document.createElement('input');
+ input.type = 'file';
+ input.accept = '.json';
+ input.click();
+ input.onchange = async () => {
+ setMessage(browser.i18n.getMessage('customPrompts_start_import'));
+ const file = input.files[0];
+ const reader = new FileReader();
+ reader.onload = async () => {
+ const json = reader.result;
+ try {
+ const obj = JSON.parse(json);
+ if(obj.id !== 'thunderai-prompts') {
+ alert(browser.i18n.getMessage("importPrompts_invalidFile"));
+ setMessage(browser.i18n.getMessage('importPrompts_invalidFile'),'red');
+ return;
+ }
+ // if(obj.addon_version !== manifest.version) {
+ // alert(browser.i18n.getMessage("importPrompts_invalidVersion"));
+ // return;
+ // }
+ if(!Array.isArray(obj.prompts)) {
+ alert(browser.i18n.getMessage("importPrompts_invalidPrompts"));
+ setMessage(browser.i18n.getMessage('customPrompts_invalidPrompts'),'red');
+ return;
+ }
+ //setCustomPrompts(obj.prompts);
+ customDataPHsList.clear();
+ loadCustomDataPHsList(await preparePromptsForImport(obj.prompts));
+ setSomethingChanged();
+ i18n.updateDocument();
+ // browser.runtime.sendMessage({command: "reload_menus"});
+ setMessage(browser.i18n.getMessage('customPrompts_import_completed'), 'orange');
+ // msgTimeout = setTimeout(() => {
+ // clearMessage();
+ // }, 10000);
+ } catch(err) {
+ alert(browser.i18n.getMessage("importPrompts_invalidFile") + ' ' + err);
+ setMessage(browser.i18n.getMessage('importPrompts_invalidFile'),'red');
+ return;
+ }
+ };
+ reader.readAsText(file);
+ };
+
+ };
+ }
+
+}, { once: true });
+
+//========= handling an item in a row
+function handleEditClick(e) {
+ e.preventDefault();
+ const tr = e.target.parentNode.parentNode;
+ //console.log('>>>>>>>> tr: ' + tr.getAttribute('data-idnum'));
+ e.target.style.display = 'none'; // Edit btn
+ tr.querySelector('.btnConfirmItem').style.display = 'inline'; // Save btn
+ tr.querySelector('.btnCancelItem').style.display = 'inline'; // Cancel btn
+// tr.querySelector('.btnEditItem').style.display = 'none'; // Edit btn
+ tr.querySelector('.btnDeleteItem').style.display = 'none'; // Delete btn
+ showItemRowEditor(tr);
+}
+
+function showItemRowEditor(tr) {
+ tr.querySelector('.id_output').style.display = 'inline';
+ tr.querySelector('.id_show').style.display = 'none';
+ tr.querySelector('.name_output').style.display = 'inline';
+ tr.querySelector('.name_show').style.display = 'none';
+ const text_output = tr.querySelector('.text_output');
+ text_output.style.display = 'inline';
+ textareaAutocomplete(text_output, autocompleteSuggestions)
+ tr.querySelector('.text_show').style.display = 'none';
+ tr.querySelector('.type_output').style.display = 'inline';
+ tr.querySelector('.type_show').style.display = 'none';
+ tr.querySelector('input.need_selected').disabled = false;
+ tr.querySelector('input.need_custom_text').disabled = false;
+}
+
+function hideItemRowEditor(tr) {
+ tr.querySelector('.id_output').style.display = 'none';
+ tr.querySelector('.id_show').style.display = 'inline';
+ tr.querySelector('.name_output').style.display = 'none';
+ tr.querySelector('.name_show').style.display = 'inline';
+ tr.querySelector('.text_output').style.display = 'none';
+ tr.querySelector('.text_show').style.display = 'inline';
+ tr.querySelector('.type_output').style.display = 'none';
+ tr.querySelector('.type_show').style.display = 'inline';
+ tr.querySelector('input.need_selected').disabled = true;
+ tr.querySelector('input.need_custom_text').disabled = true;
+}
+
+// Confirm and log deletion action
+function handleDeleteClick(e) {
+ e.preventDefault();
+ const checkConfirm = window.confirm(browser.i18n.getMessage("customPrompts_btnDelete_confirmText"));
+ if (!checkConfirm) {
+ return;
+ }
+ const tr = e.target.parentNode.parentNode;
+ //console.log('>>>>>>>> tr: ' + tr.getAttribute('data-idnum'));
+ customDataPHsList.remove("id", tr.querySelector('span.id').innerText);
+ setSomethingChanged();
+}
+
+function handleCancelClick(e) {
+ e.preventDefault();
+ const tr = e.target.parentNode.parentNode;
+ e.target.style.display = 'none'; // Cancel btn
+ tr.querySelector('.btnConfirmItem').style.display = 'none'; // Save btn
+// tr.querySelector('.btnCancelItem').style.display = 'none'; // Cancel btn
+ tr.querySelector('.btnEditItem').style.display = 'inline'; // Edit btn
+ tr.querySelector('.btnDeleteItem').style.display = 'inline'; // Delete btn
+ tr.querySelector('.id_output').value = tr.querySelector('.id_show').innerText.toLocaleUpperCase();
+ tr.querySelector('.name_output').value = tr.querySelector('.name_show').innerText;
+ tr.querySelector('.text_output').value = sanitizeHtml(tr.querySelector('.text_show').innerHTML).replace(/
/gi, "\n");
+ tr.querySelector('.type_output').value = tr.querySelector('.type').innerText;
+ hideItemRowEditor(tr);
+}
+
+function handleConfirmClick(e) {
+ e.preventDefault();
+ const tr = e.target.parentNode.parentNode;
+ e.target.style.display = 'none'; // Ok btn
+// tr.querySelector('.btnConfirmItem').style.display = 'none'; // Ok btn
+ tr.querySelector('.btnCancelItem').style.display = 'none'; // Cancel btn
+ tr.querySelector('.btnEditItem').style.display = 'inline'; // Edit btn
+ tr.querySelector('.btnDeleteItem').style.display = 'inline'; // Delete btn
+ // Update item data
+ tr.querySelector('.id_show').innerText = String(tr.querySelector('.id_output').value).toLocaleLowerCase();
+ tr.querySelector('.name_show').innerText = tr.querySelector('.name_output').value;
+ tr.querySelector('.text_show').innerText = tr.querySelector('.text_output').value;
+ tr.querySelector('.type').innerText = tr.querySelector('.type_output').value;
+ tr.querySelector('.type_show').innerText = tr.querySelector('.type_output').selectedOptions[0].text;
+ // the checkboxes update is handled directly by themselves
+ hideItemRowEditor(tr);
+ setSomethingChanged();
+}
+
+// Handle checkbox changes and log new state
+function handleCheckboxChange(e) {
+ e.preventDefault();
+ e.target.setAttribute('checked_val', e.target.checked ? '1' : '0');
+ //console.log('>>>>>>>> checked_val: ' + e.target.getAttribute('checked_val'));
+ if (e.target.classList.contains('need_selected') || e.target.classList.contains('need_custom_text') || e.target.classList.contains('need_selected_new') || e.target.classList.contains('need_custom_text_new')) {
+ let textarea = e.target.closest('tr').querySelector('.text_output');
+ checkPromptsConfigForPlaceholders(textarea);
+ }
+
+}
+
+// Enable save button on input change
+function handleInputChange(e) {
+ e.preventDefault();
+ setSomethingChanged();
+}
+
+//========= handling an item in a row - END
+
+
+function loadCustomDataPHsList(values){
+ // console.log('>>>>>>>> loadCustomDataPHsList values: ' + JSON.stringify(values));
+ let options = {
+ valueNames: [
+ { data: ['idnum'] },
+ 'is_default',
+ 'id',
+ 'name',
+ 'text',
+ 'type',
+ { name: 'need_selected', attr: 'checked_val'},
+ { name: 'need_custom_text', attr: 'checked_val'},
+ { name: 'enabled', attr: 'checked_val'}
+ ],
+ item: function(values) {
+ values.id = placeholdersUtils.stripCustomDataPH_ID_Prefix(values.id);
+ let type_output = '';
+ switch(String(values.type)){
+ case "0":
+ type_output = `__MSG_customPrompts_add_to_menu_always__`;
+ break;
+ case "1":
+ type_output = `__MSG_customPrompts_add_to_menu_reading__`;
+ break;
+ case "2":
+ type_output = `__MSG_customPrompts_add_to_menu_composing__`;
+ break;
+ }
+ let output = `
+ | thunderai_custom_ |
+ |
+
+
+
+ |
+ __MSG_customPrompts_add_to_menu__:
+
+ ` + type_output + `
+ ` +
+ `
+ |
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+ |
+
`;
+ //console.log('>>>>>>>> values.name: ' + JSON.stringify(values.name));
+ idnumMax = Math.max(idnumMax, values.idnum);
+ return output;
+ }
+ };
+
+ // console.log('>>>>>>>>>>>>> options: ' + JSON.stringify(options));
+ // console.log('>>>>>>>>>>>>> values: ' + JSON.stringify(values));
+
+ customDataPHsList = new List('all_custom_dataplaceholders', options, values);
+
+ checkSelectedBoxes();
+ let btnEditItem_elements = document.querySelectorAll(".btnEditItem");
+ btnEditItem_elements.forEach(element => {
+ element.addEventListener('click', handleEditClick);
+ });
+
+ let btnDeleteItem_elements = document.querySelectorAll(".btnDeleteItem");
+ btnDeleteItem_elements.forEach(element => {
+ element.addEventListener('click', handleDeleteClick);
+ });
+
+ let btnCancelItem_elements = document.querySelectorAll(".btnCancelItem");
+ btnCancelItem_elements.forEach(element => {
+ element.addEventListener('click', handleCancelClick);
+ });
+
+ let btnConfirmItem_elements = document.querySelectorAll(".btnConfirmItem");
+ btnConfirmItem_elements.forEach(element => {
+ element.addEventListener('click', handleConfirmClick);
+ });
+
+ let checkbox_elements = document.querySelectorAll("input[type='checkbox']");
+ checkbox_elements.forEach(element => {
+ element.addEventListener('change', handleCheckboxChange);
+ });
+
+ document.querySelectorAll('.input_mod').forEach(element => {
+ element.addEventListener('change', handleInputChange);
+ });
+}
+
+function checkFields() {
+ //console.log('>>>>>>>>>>>>> typeof customDataPHsList: ' + typeof customDataPHsList);
+ //console.log('>>>>>>>>>>>>> Array.isArray(customDataPHsList): ' + Array.isArray(customDataPHsList));
+ // the id must be unique and without spaces
+ let is_error = false;
+ let id_value = document.getElementById('txtIdNew').value.trim();
+ if ((id_value == '')||
+ (/\s/.test(id_value))) {
+ inputSetError('txtIdNew');
+ is_error = true;
+ } else {
+ let exists = customDataPHsList.get("id", id_value);
+ //console.log('>>>>>>>>>>>>> exists: ' + JSON.stringify(exists));
+ if(exists && exists.length > 0) {
+ inputSetError('txtIdNew');
+ is_error = true;
+ } else {
+ inputClearError('txtIdNew');
+ }
+ }
+ if (document.getElementById('txtNameNew').value.trim() == '') {
+ inputSetError('txtNameNew');
+ is_error = true;
+ } else {
+ inputClearError('txtNameNew');
+ }
+ if (document.getElementById('txtTextNew').value.trim() == '') {
+ inputSetError('txtTextNew');
+ is_error = true;
+ } else {
+ inputClearError('txtTextNew');
+ }
+ document.getElementById('btnAddNew').disabled = is_error;
+ return !is_error;
+}
+
+
+function clearFields() {
+ document.getElementById('txtIdNew').value = '';
+ document.getElementById('txtNameNew').value = '';
+ document.getElementById('txtTextNew').value = '';
+ document.getElementById('selectTypeNew').value = '0';
+ document.getElementById('checkboxNeedSelectedNew').value = '0';
+ document.getElementById('checkboxNeedCustomTextNew').value = '0';
+ document.getElementById('formNew').style.display = 'none';
+}
+
+function inputSetError(input) {
+ document.getElementById(input).style.borderColor = 'red';
+}
+
+function inputClearError(input) {
+ document.getElementById(input).style.borderColor = 'green';
+}
+
+function setSomethingChanged(){
+ clearTimeout(msgTimeout);
+ somethingChanged = true;
+ document.getElementById('btnSaveAll').disabled = false;
+ let msgDisplay = document.getElementById('msgDisplay');
+ msgDisplay.textContent = browser.i18n.getMessage('customPrompts_unsaved_changes');
+ msgDisplay.style.display = 'inline';
+ msgDisplay.style.color = 'red';
+}
+
+function setNothingChanged(){
+ somethingChanged = false;
+ document.getElementById('btnSaveAll').disabled = true;
+ let msgDisplay = document.getElementById('msgDisplay');
+ msgDisplay.disabled = true;
+ msgDisplay.textContent = ''
+ msgDisplay.style.display = 'none';
+ msgDisplay.style.color = '';
+}
+
+function checkSelectedBoxes(checkboxes = null) {
+ if(checkboxes == null){
+ checkboxes = [
+ ...document.querySelectorAll('.need_selected[type="checkbox"]'),
+ ...document.querySelectorAll('.need_custom_text[type="checkbox"]'),
+ ...document.querySelectorAll('.enabled[type="checkbox"]'),
+ ];
+ }
+
+ // Iterate through the checkboxes
+ checkboxes.forEach(checkbox => {
+ // Check if the 'checked' attribute is "0"
+ if (checkbox.getAttribute('checked_val') == "0") {
+ // Uncheck the checkbox
+ checkbox.checked = false;
+ } else {
+ checkbox.checked = true;
+ }
+ });
+}
+
+//Save all custom data placeholders
+async function saveAll() {
+ setMessage(browser.i18n.getMessage('customDataPH_start_saving'));
+ setNothingChanged();
+ if(customDataPHsList != null) {
+ setMessage(browser.i18n.getMessage('customPrompts_reindexing_list'));
+ customDataPHsList.reIndex();
+ let newCustomDataPHs = customDataPHsList.items.filter(item => item.values().is_default == 0).map(item => item.values());
+ taLog.log('newCustomDataPlaceholders: ' + JSON.stringify(newCustomDataPHs));
+ // newCustomDataPHs.forEach(prompt => {
+ // console.log('>>>>>>>>>>>>> id: ' + JSON.stringify(prompt));
+ // });
+ //console.log('>>>>>>>>>>>>> saveAll: ' + JSON.stringify(newCustomDataPHs));
+ await setCustomPlaceholders(newCustomDataPHs);
+ setMessage(browser.i18n.getMessage('customDataPH_saved'),'green');
+ msgTimeout = setTimeout(() => {
+ clearMessage();
+ }, 10000)
+ }
+ setStorageSpace();
+}
+
+function setMessage(msg, color = '') {
+ clearTimeout(msgTimeout);
+ let msgDisplay = document.getElementById('msgDisplay');
+ msgDisplay.textContent = msg;
+ msgDisplay.style.display = 'inline';
+ msgDisplay.style.color = color;
+}
+
+function clearMessage() {
+ let msgDisplay = document.getElementById('msgDisplay');
+ msgDisplay.textContent = '';
+ msgDisplay.style.display = 'none';
+ msgDisplay.style.color = '';
+}
+
+async function setStorageSpace() {
+ let storage_space = await getLocalStorageUsedSpace();
+ document.getElementById('storage_space').textContent = storage_space;
+}
+
+
+if(await isThunderbird128OrGreater()){
+ window.addEventListener('beforeunload', function (event) {
+ // Check if any changes have been made (Only for Thunderbird 128+ see https://github.com/micz/ThunderAI/issues/88)
+ if (somethingChanged) {
+ event.preventDefault();
+ }
+ });
+}
+
+function checkPromptsConfigForPlaceholders(textarea){
+ // check additional_text and selected_text placeholders presence and the corrispondent checkboxes
+ let tr_ancestor = textarea.closest('tr');
+ let need_custom_text_element = tr_ancestor.querySelector('.need_custom_text') || tr_ancestor.querySelector('.need_custom_text_new');
+ if(String(textarea.value).indexOf('{%additional_text%}') != -1){
+ if(!need_custom_text_element.checked){
+ need_custom_text_element.closest('.need_custom_text_span').style.border = '2px solid red';
+ }else{
+ need_custom_text_element.closest('.need_custom_text_span').style.border = '';
+ }
+ }else{
+ need_custom_text_element.closest('.need_custom_text_span').style.border = '';
+ }
+
+ let tr_ancestor2 = textarea.closest('tr');
+ let selected_text_element = tr_ancestor2.querySelector('.need_selected') || tr_ancestor2.querySelector('.need_selected_new');
+ if((String(textarea.value).indexOf('{%selected_text%}') != -1)||(String(textarea.value).indexOf('{%selected_html%}') != -1)){
+ if(!selected_text_element.checked){
+ selected_text_element.closest('.need_selected_span').style.border = '2px solid red';
+ }else{
+ selected_text_element.closest('.need_selected_span').style.border = '';
+ }
+ }else{
+ selected_text_element.closest('.need_selected_span').style.border = '';
+ }
+}
\ No newline at end of file