diff --git a/amd/build/management.min.js b/amd/build/management.min.js
index 0ecd1fb..6f3228f 100644
--- a/amd/build/management.min.js
+++ b/amd/build/management.min.js
@@ -6,6 +6,6 @@ define("tiny_elements/management",["exports","tiny_elements/previewmodal","core_
* @copyright 2024 ISB Bayern
* @author Tobias Garske
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.wipe=_exports.init=_exports.duplicateItem=_exports.deleteItem=void 0,_modalform=_interopRequireDefault(_modalform),_notification=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(_notification),_log=_interopRequireDefault(_log);_exports.init=async params=>{document.getElementById("elements_import").addEventListener("click",(async e=>{importModal(e)})),document.getElementsByClassName("add").forEach((element=>{element.addEventListener("click",(async e=>{showModal(e,element.dataset.id,element.dataset.table)}))})),document.getElementsByClassName("edit").forEach((element=>{element.addEventListener("click",(async e=>{showModal(e,element.dataset.id,element.dataset.table)}))})),document.getElementsByClassName("delete").forEach((element=>{element.addEventListener("click",(async e=>{deleteModal(e,element.dataset.id,element.dataset.title,element.dataset.table)}))})),document.getElementsByClassName("preview-button").forEach((element=>{element.addEventListener("click",(async e=>{previewModal(e)}))})),document.getElementsByClassName("compcat").forEach((element=>{element.addEventListener("click",(async e=>{showItems(e,element.dataset.compcat)}))})),document.getElementsByClassName("editlicenses").forEach((element=>{element.addEventListener("click",(async e=>{editlicensesModal(e,element.dataset.id)}))})),document.querySelectorAll(".buttonicons").forEach((element=>{element.addEventListener("click",(async e=>{compflavorModal(e)}))})),document.getElementById("elements_displaynames_button").addEventListener("click",(async e=>{displaynamesModal(e)})),document.getElementById("elements_displaynames_flavor_button").addEventListener("click",(async e=>{displaynamesFlavorModal(e)})),document.getElementById("elements_displaynames_variant_button").addEventListener("click",(async e=>{displaynamesVariantModal(e)})),document.getElementsByClassName("duplicate").forEach((element=>{element.addEventListener("click",(async()=>{duplicateItem(element.dataset.id,element.dataset.table).always((()=>reload()))}))}));let wipebutton=document.getElementById("elements_wipe");if(wipebutton&&wipebutton.addEventListener("click",(async e=>{wipeModal(e)})),document.querySelectorAll(".flavor .card-body > .clickingextended, .component .card-body > .clickingextended, .variant .card-body > .clickingextended").forEach((element=>{element.addEventListener("click",(async e=>{e.target.closest(".item").querySelector("a.edit").click()}))})),params.compcatactive){let compcat=document.querySelector('.compcat[data-compcat="'+params.compcatactive+'"]');compcat&&(showItems(!1,params.compcatactive),compcat.classList.add("active"))}};const showModal=async(event,id,table)=>{let title;event.preventDefault(),title=0==id?(0,_str.get_string)("additem","tiny_elements"):(0,_str.get_string)("edititem","tiny_elements");const modalForm=new _modalform.default({formClass:"tiny_elements\\form\\management_"+table+"_form",args:{id:id,compcat:getActiveCompcatId(),categoryname:getActiveCompcatName()},modalConfig:{title:title},returnFocus:event.target});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(()=>reload())),await modalForm.show()},previewModal=async event=>{event.preventDefault();let preview=event.target.closest(".preview-button");const modal=await _previewmodal.PreviewModal.create({templateContext:{component:preview.dataset.component,flavors:preview.dataset.flavors.trim().split(" "),config:M.cfg}});await modal.show()},importModal=async event=>{event.preventDefault();let title=(0,_str.get_string)("import","tiny_elements");const modalForm=new _modalform.default({formClass:"tiny_elements\\form\\management_import_form",args:{},modalConfig:{title:title}});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,importModalSubmitted),await modalForm.show()},importModalSubmitted=async event=>{event.detail.update?location.reload():(event.stopPropagation(),(0,_templates.render)("tiny_elements/management_import_form_result",event.detail).then((async html=>(await _notification.default.alert((0,_str.get_string)("import_simulation","tiny_elements"),html,(0,_str.get_string)("close","tiny_elements")),!0))).catch((error=>{(0,_notification.exception)(error)})))},compflavorModal=async event=>{var _target$dataset$compo,_target$dataset$flavo;event.preventDefault();let title=(0,_str.get_string)("manage","tiny_elements");const target=event.target.closest(".buttonicons"),component=null!==(_target$dataset$compo=target.dataset.component)&&void 0!==_target$dataset$compo?_target$dataset$compo:"",flavor=null!==(_target$dataset$flavo=target.dataset.flavor)&&void 0!==_target$dataset$flavo?_target$dataset$flavo:"",modalForm=new _modalform.default({formClass:"tiny_elements\\form\\management_comp_flavor_form",args:{component:component,flavor:flavor},modalConfig:{title:title}});await modalForm.show()},editlicensesModal=async(event,id)=>{event.preventDefault();let title=(0,_str.get_string)("editlicenses","tiny_elements");const modalForm=new _modalform.default({formClass:"tiny_elements\\form\\management_editlicense_form",args:{id:id},modalConfig:{title:title}});await modalForm.show()},displaynamesModal=async event=>{event.preventDefault();let title=(0,_str.get_string)("manage","tiny_elements");const modalForm=new _modalform.default({formClass:"tiny_elements\\form\\management_displaynames_form",args:{},modalConfig:{title:title}});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(()=>location.reload())),await modalForm.show()},displaynamesFlavorModal=async event=>{event.preventDefault();let title=(0,_str.get_string)("manage","tiny_elements");const modalForm=new _modalform.default({formClass:"tiny_elements\\form\\management_displaynames_flavors_form",args:{},modalConfig:{title:title}});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(()=>location.reload())),await modalForm.show()},displaynamesVariantModal=async event=>{event.preventDefault();let title=(0,_str.get_string)("manage","tiny_elements");const modalForm=new _modalform.default({formClass:"tiny_elements\\form\\management_displaynames_variants_form",args:{},modalConfig:{title:title}});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(()=>location.reload())),await modalForm.show()},deleteModal=(event,id,title,table)=>{event.preventDefault(),(0,_notification.deleteCancelPromise)((0,_str.get_string)("delete","tiny_elements",title),(0,_str.get_string)("deletewarning","tiny_elements")).then((async()=>{if(0!==id)try{if(await deleteItem(id,table)){const link=document.querySelector('[data-table="'+table+'"][data-id="'+id+'"]');if(link){link.closest(".item").remove()}}}catch(error){(0,_notification.exception)(error)}})).catch((err=>{err.message&&_log.default.error(err.message)}))},wipeModal=event=>{event.preventDefault(),(0,_notification.deleteCancelPromise)((0,_str.get_string)("wipe","tiny_elements"),(0,_str.get_string)("wipewarning","tiny_elements")).then((async()=>{try{await wipe(),reload()}catch(error){(0,_notification.exception)(error)}})).catch((err=>{err.message&&_log.default.error(err.message)}))},deleteItem=(id,table)=>(0,_ajax.call)([{methodname:"tiny_elements_delete_item",args:{id:id,table:table}}])[0];_exports.deleteItem=deleteItem;const wipe=()=>(0,_ajax.call)([{methodname:"tiny_elements_wipe",args:{contextid:1}}])[0];_exports.wipe=wipe;const showItems=(event,compcat)=>{document.querySelectorAll(".flavor, .component, .variant").forEach((element=>{element.classList.add("hidden")}));let itemsShow=document.querySelectorAll('[data-categoryname="'+compcat+'"]'),usedFlavors=[];itemsShow.forEach((element=>{if(element.classList.remove("hidden"),void 0!==element.dataset.flavors){let flavors=element.dataset.flavors.split(" ");for(let value of flavors)usedFlavors.includes(value)||0==value.length||usedFlavors.push(value)}}));let flavorstring=usedFlavors.map((item=>".".concat(item))).join(", ");if(flavorstring.length){document.querySelectorAll(flavorstring).forEach((element=>{element.classList.remove("hidden")}))}if(document.getElementsByClassName("addcontainer").forEach((element=>{element.classList.remove("hidden")})),event){document.getElementsByClassName("compcat").forEach((element=>{element.classList.remove("active")})),event.target.closest(".compcat").classList.add("active")}if("found-items"==compcat){let found=document.querySelector('.compcat[data-compcat="found-items"]');if(found.dataset.loneflavors.length){document.querySelectorAll(found.dataset.loneflavors).forEach((element=>{element.classList.remove("hidden")}))}if(found.dataset.lonevariants.length){document.querySelectorAll(found.dataset.lonevariants).forEach((element=>{element.classList.remove("hidden")}))}if(found.dataset.lonecomponents.length){document.querySelectorAll(found.dataset.lonecomponents).forEach((element=>{element.classList.remove("hidden")}))}}},reload=()=>{const currentUrl=new URL(window.location.href);currentUrl.searchParams.set("compcat",getActiveCompcatName()),window.location.href=currentUrl.toString(),window.location.reload()},getActiveCompcatName=()=>{var _compcat$dataset$comp;const compcat=document.querySelector(".compcat.active");return compcat&&null!==(_compcat$dataset$comp=compcat.dataset.compcat)&&void 0!==_compcat$dataset$comp?_compcat$dataset$comp:""},getActiveCompcatId=()=>{var _compcat$dataset$id;const compcat=document.querySelector(".compcat.active");return compcat&&null!==(_compcat$dataset$id=compcat.dataset.id)&&void 0!==_compcat$dataset$id?_compcat$dataset$id:0},duplicateItem=(id,table)=>(0,_ajax.call)([{methodname:"tiny_elements_duplicate_item",args:{id:id,table:table}}])[0];_exports.duplicateItem=duplicateItem}));
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.wipe=_exports.init=_exports.duplicateItem=_exports.deleteItem=void 0,_modalform=_interopRequireDefault(_modalform),_notification=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(_notification),_log=_interopRequireDefault(_log);_exports.init=async params=>{document.getElementById("elements_import").addEventListener("click",(async e=>{importModal(e)})),document.getElementsByClassName("add").forEach((element=>{element.addEventListener("click",(async e=>{showModal(e,element.dataset.id,element.dataset.table)}))})),document.getElementsByClassName("edit").forEach((element=>{element.addEventListener("click",(async e=>{showModal(e,element.dataset.id,element.dataset.table)}))})),document.getElementsByClassName("delete").forEach((element=>{element.addEventListener("click",(async e=>{deleteModal(e,element.dataset.id,element.dataset.title,element.dataset.table)}))})),document.getElementsByClassName("preview-button").forEach((element=>{element.addEventListener("click",(async e=>{previewModal(e)}))})),document.getElementsByClassName("compcat").forEach((element=>{element.addEventListener("click",(async e=>{showItems(e,element.dataset.compcat)}))})),document.getElementsByClassName("editlicenses").forEach((element=>{element.addEventListener("click",(async e=>{editlicensesModal(e,element.dataset.id)}))})),document.querySelectorAll(".buttonicons").forEach((element=>{element.addEventListener("click",(async e=>{compflavorModal(e)}))})),document.getElementById("elements_displaynames_button").addEventListener("click",(async e=>{displaynamesModal(e)})),document.getElementById("elements_displaynames_flavor_button").addEventListener("click",(async e=>{displaynamesFlavorModal(e)})),document.getElementById("elements_displaynames_variant_button").addEventListener("click",(async e=>{displaynamesVariantModal(e)})),document.getElementsByClassName("duplicate").forEach((element=>{element.addEventListener("click",(async()=>{duplicateItem(element.dataset.id,element.dataset.table).always((()=>reload()))}))}));let wipebutton=document.getElementById("elements_wipe");if(wipebutton&&wipebutton.addEventListener("click",(async e=>{wipeModal(e)})),document.querySelectorAll(".flavor .card-body > .clickingextended, .component .card-body > .clickingextended, .variant .card-body > .clickingextended").forEach((element=>{element.addEventListener("click",(async e=>{e.target.closest(".item").querySelector("a.edit").click()}))})),params.compcatactive){let compcat=document.querySelector('.compcat[data-compcat="'+params.compcatactive+'"]');compcat&&(showItems(!1,params.compcatactive),compcat.classList.add("active"))}};const showModal=async(event,id,table)=>{let title;event.preventDefault(),title=0==id?(0,_str.get_string)("additem","tiny_elements"):(0,_str.get_string)("edititem","tiny_elements");const modalForm=new _modalform.default({formClass:"tiny_elements\\form\\management_"+table+"_form",args:{id:id,compcat:getActiveCompcatId(),categoryname:getActiveCompcatName()},modalConfig:{title:title},returnFocus:event.target});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(()=>reload())),await modalForm.show()},previewModal=async event=>{event.preventDefault();let preview=event.target.closest(".preview-button");const modal=await _previewmodal.PreviewModal.create({templateContext:{component:preview.dataset.component,flavors:preview.dataset.flavors.trim().split(" "),config:M.cfg}});await modal.show()},importModal=async event=>{event.preventDefault();let title=(0,_str.get_string)("import","tiny_elements");const modalForm=new _modalform.default({formClass:"tiny_elements\\form\\management_import_form",args:{},modalConfig:{title:title}});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,importModalSubmitted),await modalForm.show()},importModalSubmitted=async event=>{event.detail.update?location.reload():(event.stopPropagation(),(0,_templates.render)("tiny_elements/management_import_form_result",event.detail).then((async html=>(await _notification.default.alert((0,_str.get_string)("import_simulation","tiny_elements"),html,(0,_str.get_string)("close","tiny_elements")),!0))).catch((error=>{(0,_notification.exception)(error)})))},compflavorModal=async event=>{var _target$dataset$compo,_target$dataset$flavo;event.preventDefault();let title=(0,_str.get_string)("manage","tiny_elements");const target=event.target.closest(".buttonicons"),component=null!==(_target$dataset$compo=target.dataset.component)&&void 0!==_target$dataset$compo?_target$dataset$compo:"",flavor=null!==(_target$dataset$flavo=target.dataset.flavor)&&void 0!==_target$dataset$flavo?_target$dataset$flavo:"",modalForm=new _modalform.default({formClass:"tiny_elements\\form\\management_comp_flavor_form",args:{component:component,flavor:flavor},modalConfig:{title:title}});await modalForm.show()},editlicensesModal=async(event,id)=>{event.preventDefault();let title=(0,_str.get_string)("editlicenses","tiny_elements");const modalForm=new _modalform.default({formClass:"tiny_elements\\form\\management_editlicense_form",args:{id:id},modalConfig:{title:title}});await modalForm.show()},displaynamesModal=async event=>{event.preventDefault();let title=(0,_str.get_string)("manage","tiny_elements");const modalForm=new _modalform.default({formClass:"tiny_elements\\form\\management_displaynames_form",args:{},modalConfig:{title:title}});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(()=>location.reload())),await modalForm.show()},displaynamesFlavorModal=async event=>{event.preventDefault();let title=(0,_str.get_string)("manage","tiny_elements");const modalForm=new _modalform.default({formClass:"tiny_elements\\form\\management_displaynames_flavors_form",args:{},modalConfig:{title:title}});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(()=>location.reload())),await modalForm.show()},displaynamesVariantModal=async event=>{event.preventDefault();let title=(0,_str.get_string)("manage","tiny_elements");const modalForm=new _modalform.default({formClass:"tiny_elements\\form\\management_displaynames_variants_form",args:{},modalConfig:{title:title}});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(()=>location.reload())),await modalForm.show()},deleteModal=(event,id,title,table)=>{event.preventDefault(),(0,_notification.deleteCancelPromise)((0,_str.get_string)("delete","tiny_elements",title),(0,_str.get_string)("deletewarning","tiny_elements")).then((async()=>{if(0!==id)try{if(await deleteItem(id,table)){const link=document.querySelector('[data-table="'+table+'"][data-id="'+id+'"]');if(link){link.closest(".item").remove()}}}catch(error){(0,_notification.exception)(error)}})).catch((err=>{err.message&&_log.default.error(err.message)}))},wipeModal=event=>{event.preventDefault(),(0,_notification.deleteCancelPromise)((0,_str.get_string)("wipe","tiny_elements"),(0,_str.get_string)("wipewarning","tiny_elements")).then((async()=>{try{return await wipe(),void reload()}catch(error){return void(0,_notification.exception)(error)}})).catch((err=>{err.message&&_log.default.error(err.message)}))},deleteItem=(id,table)=>(0,_ajax.call)([{methodname:"tiny_elements_delete_item",args:{id:id,table:table}}])[0];_exports.deleteItem=deleteItem;const wipe=()=>(0,_ajax.call)([{methodname:"tiny_elements_wipe",args:{contextid:1}}])[0];_exports.wipe=wipe;const showItems=(event,compcat)=>{document.querySelectorAll(".flavor, .component, .variant").forEach((element=>{element.classList.add("hidden")}));let itemsShow=document.querySelectorAll('[data-categoryname="'+compcat+'"]'),usedFlavors=[];itemsShow.forEach((element=>{if(element.classList.remove("hidden"),void 0!==element.dataset.flavors){let flavors=element.dataset.flavors.split(" ");for(let value of flavors)usedFlavors.includes(value)||0==value.length||usedFlavors.push(value)}}));let flavorstring=usedFlavors.map((item=>".".concat(item))).join(", ");if(flavorstring.length){document.querySelectorAll(flavorstring).forEach((element=>{element.classList.remove("hidden")}))}if(document.getElementsByClassName("addcontainer").forEach((element=>{element.classList.remove("hidden")})),event){document.getElementsByClassName("compcat").forEach((element=>{element.classList.remove("active")})),event.target.closest(".compcat").classList.add("active")}if("found-items"==compcat){let found=document.querySelector('.compcat[data-compcat="found-items"]');if(found.dataset.loneflavors.length){document.querySelectorAll(found.dataset.loneflavors).forEach((element=>{element.classList.remove("hidden")}))}if(found.dataset.lonevariants.length){document.querySelectorAll(found.dataset.lonevariants).forEach((element=>{element.classList.remove("hidden")}))}if(found.dataset.lonecomponents.length){document.querySelectorAll(found.dataset.lonecomponents).forEach((element=>{element.classList.remove("hidden")}))}}},reload=()=>{const currentUrl=new URL(window.location.href);currentUrl.searchParams.set("compcat",getActiveCompcatName()),window.location.href=currentUrl.toString(),window.location.reload()},getActiveCompcatName=()=>{var _compcat$dataset$comp;const compcat=document.querySelector(".compcat.active");return compcat&&null!==(_compcat$dataset$comp=compcat.dataset.compcat)&&void 0!==_compcat$dataset$comp?_compcat$dataset$comp:""},getActiveCompcatId=()=>{var _compcat$dataset$id;const compcat=document.querySelector(".compcat.active");return compcat&&null!==(_compcat$dataset$id=compcat.dataset.id)&&void 0!==_compcat$dataset$id?_compcat$dataset$id:0},duplicateItem=(id,table)=>(0,_ajax.call)([{methodname:"tiny_elements_duplicate_item",args:{id:id,table:table}}])[0];_exports.duplicateItem=duplicateItem}));
//# sourceMappingURL=management.min.js.map
\ No newline at end of file
diff --git a/amd/build/management.min.js.map b/amd/build/management.min.js.map
index 5db611b..4180dd3 100644
--- a/amd/build/management.min.js.map
+++ b/amd/build/management.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"management.min.js","sources":["../src/management.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Management functions for tiny_elements admin backend.\n *\n * @module tiny_elements/management\n * @copyright 2024 ISB Bayern\n * @author Tobias Garske\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {PreviewModal} from 'tiny_elements/previewmodal';\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {get_string as getString} from 'core/str';\nimport {exception as displayException, deleteCancelPromise} from 'core/notification';\nimport {call as fetchMany} from 'core/ajax';\nimport {render as renderTemplate} from 'core/templates';\nimport Log from 'core/log';\n\nexport const init = async(params) => {\n\n // Add listener to import xml files.\n let importxml = document.getElementById('elements_import');\n importxml.addEventListener('click', async(e) => {\n importModal(e);\n });\n\n // Add listener for adding a new item.\n let additem = document.getElementsByClassName('add');\n additem.forEach(element => {\n element.addEventListener('click', async(e) => {\n showModal(e, element.dataset.id, element.dataset.table);\n });\n });\n\n // Add listener to edit items.\n let edititems = document.getElementsByClassName('edit');\n edititems.forEach(element => {\n element.addEventListener('click', async(e) => {\n showModal(e, element.dataset.id, element.dataset.table);\n });\n });\n\n // Add listener to delete items.\n let deleteitems = document.getElementsByClassName('delete');\n deleteitems.forEach(element => {\n element.addEventListener('click', async(e) => {\n deleteModal(e, element.dataset.id, element.dataset.title, element.dataset.table);\n });\n });\n\n // Add listener to preview items.\n let previewitems = document.getElementsByClassName('preview-button');\n previewitems.forEach(element => {\n element.addEventListener('click', async(e) => {\n previewModal(e);\n });\n });\n\n // Add listener to select compcat to show corresponding items.\n let compcats = document.getElementsByClassName('compcat');\n compcats.forEach(element => {\n element.addEventListener('click', async(e) => {\n showItems(e, element.dataset.compcat);\n });\n });\n\n // Add listener to edit licenses icon.\n let editlicenses = document.getElementsByClassName('editlicenses');\n editlicenses.forEach(element => {\n element.addEventListener('click', async(e) => {\n editlicensesModal(e, element.dataset.id);\n });\n });\n\n // Add listener to manage component flavor relation.\n let buttonicons = document.querySelectorAll('.buttonicons');\n buttonicons.forEach(element => {\n element.addEventListener('click', async(e) => {\n compflavorModal(e);\n });\n });\n\n let displaynamesbutton = document.getElementById('elements_displaynames_button');\n displaynamesbutton.addEventListener('click', async(e) => {\n displaynamesModal(e);\n });\n\n let displaynamesflavorbutton = document.getElementById('elements_displaynames_flavor_button');\n displaynamesflavorbutton.addEventListener('click', async(e) => {\n displaynamesFlavorModal(e);\n });\n\n let displaynamesvariantbutton = document.getElementById('elements_displaynames_variant_button');\n displaynamesvariantbutton.addEventListener('click', async(e) => {\n displaynamesVariantModal(e);\n });\n\n // Add listener to duplicate items.\n let duplicateitems = document.getElementsByClassName('duplicate');\n duplicateitems.forEach(element => {\n element.addEventListener('click', async() => {\n duplicateItem(element.dataset.id, element.dataset.table).always(() => reload());\n });\n });\n\n // Add listener to wipe all items.\n let wipebutton = document.getElementById('elements_wipe');\n if (wipebutton) {\n wipebutton.addEventListener('click', async(e) => {\n wipeModal(e);\n });\n }\n\n // Add image and text to item setting click area.\n let enlargeItems = document.querySelectorAll(\n '.flavor .card-body > .clickingextended, .component .card-body > .clickingextended, .variant .card-body > .clickingextended'\n );\n enlargeItems.forEach(element => {\n element.addEventListener('click', async(e) => {\n let item = e.target.closest('.item');\n item.querySelector('a.edit').click();\n });\n });\n\n // After submitting a new item, reset active compcat.\n if (params.compcatactive) {\n let compcat = document.querySelector('.compcat[data-compcat=\"' + params.compcatactive + '\"]');\n if (compcat) {\n showItems(false, params.compcatactive);\n compcat.classList.add('active');\n }\n }\n};\n\n/**\n * Show dynamic form to add/edit a source.\n * @param {*} event\n * @param {*} id\n * @param {*} table\n */\nconst showModal = async(event, id, table) => {\n event.preventDefault();\n let title;\n if (id == 0) {\n title = getString('additem', 'tiny_elements');\n } else {\n title = getString('edititem', 'tiny_elements');\n }\n\n const modalForm = new ModalForm({\n // Set formclass, depending on component.\n formClass: \"tiny_elements\\\\form\\\\management_\" + table + \"_form\",\n args: {\n id: id,\n compcat: getActiveCompcatId(),\n categoryname: getActiveCompcatName(),\n },\n modalConfig: {title: title},\n returnFocus: event.target,\n });\n // Conditional reload page after submit.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, () => reload());\n\n await modalForm.show();\n};\n\n/**\n * Show modal to preview css version.\n * @param {*} event\n */\nconst previewModal = async(event) => {\n event.preventDefault();\n let preview = event.target.closest(\".preview-button\");\n const modal = await PreviewModal.create({\n templateContext: {\n component: preview.dataset.component,\n flavors: preview.dataset.flavors.trim().split(\" \"),\n config: M.cfg,\n },\n });\n await modal.show();\n};\n\n/**\n * Show dynamic form to import xml backups.\n * @param {*} event\n */\nconst importModal = async(event) => {\n event.preventDefault();\n let title = getString('import', 'tiny_elements');\n\n const modalForm = new ModalForm({\n // Load import form.\n formClass: \"tiny_elements\\\\form\\\\management_import_form\",\n args: {},\n modalConfig: {title: title},\n });\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, importModalSubmitted);\n\n await modalForm.show();\n};\n\n/**\n * Process import form submit.\n * @param {*} event\n */\nconst importModalSubmitted = async(event) => {\n // Reload page after submit.\n if (event.detail.update) {\n location.reload();\n } else {\n event.stopPropagation();\n renderTemplate('tiny_elements/management_import_form_result', event.detail).then(async(html) => {\n await Notification.alert(\n getString('import_simulation', 'tiny_elements'),\n html,\n getString('close', 'tiny_elements')\n );\n return true;\n }).catch((error) => {\n displayException(error);\n });\n }\n};\n\n/**\n * Load modal to edit icon urls.\n * @param {*} event\n */\nconst compflavorModal = async(event) => {\n event.preventDefault();\n let title = getString('manage', 'tiny_elements');\n const target = event.target.closest('.buttonicons');\n const component = target.dataset.component ?? '';\n const flavor = target.dataset.flavor ?? '';\n const modalForm = new ModalForm({\n // Load import form.\n formClass: \"tiny_elements\\\\form\\\\management_comp_flavor_form\",\n args: {\n component: component,\n flavor: flavor,\n },\n modalConfig: {title: title},\n });\n\n await modalForm.show();\n};\n\n/**\n * Load modal to edit licenses of icons.\n * @param {*} event\n * @param {*} id\n */\nconst editlicensesModal = async(event, id) => {\n event.preventDefault();\n let title = getString('editlicenses', 'tiny_elements');\n const modalForm = new ModalForm({\n formClass: \"tiny_elements\\\\form\\\\management_editlicense_form\",\n args: {\n id: id,\n },\n modalConfig: {title: title},\n });\n await modalForm.show();\n};\n\n/**\n * Load modal to edit displaynames.\n * @param {*} event\n * @returns {void}\n */\nconst displaynamesModal = async(event) => {\n event.preventDefault();\n let title = getString('manage', 'tiny_elements');\n\n const modalForm = new ModalForm({\n // Load displaynames bulk edit form.\n formClass: \"tiny_elements\\\\form\\\\management_displaynames_form\",\n args: {},\n modalConfig: {title: title},\n });\n\n // Reload page after submit.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, () => location.reload());\n\n await modalForm.show();\n};\n\n/**\n * Load modal to edit displaynames.\n * @param {*} event\n * @returns {void}\n */\nconst displaynamesFlavorModal = async(event) => {\n event.preventDefault();\n let title = getString('manage', 'tiny_elements');\n\n const modalForm = new ModalForm({\n // Load displaynames bulk edit form.\n formClass: \"tiny_elements\\\\form\\\\management_displaynames_flavors_form\",\n args: {},\n modalConfig: {title: title},\n });\n\n // Reload page after submit.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, () => location.reload());\n\n await modalForm.show();\n};\n\n/**\n * Load modal to edit displaynames.\n * @param {*} event\n * @returns {void}\n */\nconst displaynamesVariantModal = async(event) => {\n event.preventDefault();\n let title = getString('manage', 'tiny_elements');\n\n const modalForm = new ModalForm({\n // Load displaynames bulk edit form.\n formClass: \"tiny_elements\\\\form\\\\management_displaynames_variants_form\",\n args: {},\n modalConfig: {title: title},\n });\n\n // Reload page after submit.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, () => location.reload());\n\n await modalForm.show();\n};\n\n/**\n * Show dynamic form to delete a source.\n * @param {*} event\n * @param {*} id\n * @param {*} title\n * @param {*} table\n */\nconst deleteModal = (event, id, title, table) => {\n event.preventDefault();\n\n deleteCancelPromise(\n getString('delete', 'tiny_elements', title),\n getString('deletewarning', 'tiny_elements'),\n ).then(async() => {\n if (id !== 0) {\n try {\n const deleted = await deleteItem(id, table);\n if (deleted) {\n const link = document.querySelector('[data-table=\"' + table + '\"][data-id=\"' + id + '\"]');\n if (link) {\n const card = link.closest(\".item\");\n card.remove();\n }\n }\n } catch (error) {\n displayException(error);\n }\n }\n return;\n }).catch((err) => {\n if (err.message) {\n Log.error(err.message);\n }\n return;\n });\n};\n\n/**\n * Show a modal to confirm wiping all items.\n * @param {*} event\n */\nconst wipeModal = (event) => {\n event.preventDefault();\n\n deleteCancelPromise(\n getString('wipe', 'tiny_elements'),\n getString('wipewarning', 'tiny_elements')\n ).then(async() => {\n try {\n await wipe();\n reload();\n } catch (error) {\n displayException(error);\n }\n }).catch((err) => {\n if (err.message) {\n Log.error(err.message);\n }\n return;\n });\n};\n\n/**\n * Delete elements items.\n * @param {*} id\n * @param {*} table\n * @returns {mixed}\n */\nexport const deleteItem = (\n id,\n table,\n) => fetchMany(\n [{\n methodname: 'tiny_elements_delete_item',\n args: {\n id,\n table,\n }\n }])[0];\n\n/**\n * Wipe all elements items.\n * @returns {mixed}\n */\nexport const wipe = () => fetchMany(\n [{\n methodname: 'tiny_elements_wipe',\n args: {\n \"contextid\": 1,\n }\n }])[0];\n\n/**\n * Show items after clicking a compcat.\n * @param {*} event\n * @param {*} compcat\n */\nconst showItems = (event, compcat) => {\n // But first hide all items.\n let itemsHide = document.querySelectorAll('.flavor, .component, .variant');\n itemsHide.forEach(element => {\n element.classList.add('hidden');\n });\n\n // Show component and variants with compcat name and read the flavors.\n let itemsShow = document.querySelectorAll('[data-categoryname=\"' + compcat + '\"]');\n let usedFlavors = [];\n itemsShow.forEach(element => {\n element.classList.remove('hidden');\n // Get all flavors to show if on compcat element.\n if (typeof element.dataset.flavors !== 'undefined') {\n let flavors = element.dataset.flavors.split(' ');\n for (let value of flavors) {\n if (!usedFlavors.includes(value) && value.length != 0) {\n usedFlavors.push(value);\n }\n }\n }\n });\n\n // Show the flavors.\n let flavorstring = usedFlavors.map(item => `.${item}`).join(', ');\n if (flavorstring.length) {\n let flavorsShow = document.querySelectorAll(flavorstring);\n flavorsShow.forEach(element => {\n element.classList.remove('hidden');\n });\n }\n\n // Show add buttons.\n let addsShow = document.getElementsByClassName('addcontainer');\n addsShow.forEach(element => {\n element.classList.remove('hidden');\n });\n\n // Unmark all and mark clicked compcat.\n if (event) {\n let items = document.getElementsByClassName('compcat');\n items.forEach(element => {\n element.classList.remove('active');\n });\n let item = event.target.closest('.compcat');\n item.classList.add('active');\n }\n\n // Special case, unassigned items, show all items without connection to compcat.\n if (compcat == 'found-items') {\n let found = document.querySelector('.compcat[data-compcat=\"found-items\"]');\n if (found.dataset.loneflavors.length) {\n let flavorsShow = document.querySelectorAll(found.dataset.loneflavors);\n flavorsShow.forEach(element => {\n element.classList.remove('hidden');\n });\n }\n if (found.dataset.lonevariants.length) {\n let variantsShow = document.querySelectorAll(found.dataset.lonevariants);\n variantsShow.forEach(element => {\n element.classList.remove('hidden');\n });\n }\n if (found.dataset.lonecomponents.length) {\n let componentsShow = document.querySelectorAll(found.dataset.lonecomponents);\n componentsShow.forEach(element => {\n element.classList.remove('hidden');\n });\n }\n }\n};\n\n/**\n * Reload page with active compcat.\n */\nconst reload = () => {\n // Reload page with active compcat.\n const currentUrl = new URL(window.location.href);\n currentUrl.searchParams.set('compcat', getActiveCompcatName());\n window.location.href = currentUrl.toString();\n window.location.reload();\n};\n\n/**\n * Get the current active compcat.\n * @returns string Name of active compcat.\n */\nconst getActiveCompcatName = () => {\n const compcat = document.querySelector('.compcat.active');\n if (!compcat) {\n return '';\n }\n return compcat.dataset.compcat ?? '';\n};\n\n/**\n * Get the current active compcat.\n * @returns int Id of active compcat.\n */\nconst getActiveCompcatId = () => {\n const compcat = document.querySelector('.compcat.active');\n if (!compcat) {\n return 0;\n }\n return compcat.dataset.id ?? 0;\n};\n\n/**\n * Duplicate elements items.\n * @param {*} id\n * @param {*} table\n * @returns {mixed}\n */\nexport const duplicateItem = (id, table) => fetchMany(\n [{\n methodname: 'tiny_elements_duplicate_item',\n args: {\n id,\n table,\n }\n }])[0];\n"],"names":["async","document","getElementById","addEventListener","importModal","e","getElementsByClassName","forEach","element","showModal","dataset","id","table","deleteModal","title","previewModal","showItems","compcat","editlicensesModal","querySelectorAll","compflavorModal","displaynamesModal","displaynamesFlavorModal","displaynamesVariantModal","duplicateItem","always","reload","wipebutton","wipeModal","target","closest","querySelector","click","params","compcatactive","classList","add","event","preventDefault","modalForm","ModalForm","formClass","args","getActiveCompcatId","categoryname","getActiveCompcatName","modalConfig","returnFocus","events","FORM_SUBMITTED","show","preview","modal","PreviewModal","create","templateContext","component","flavors","trim","split","config","M","cfg","importModalSubmitted","detail","update","location","stopPropagation","then","Notification","alert","html","catch","error","flavor","deleteItem","link","remove","err","message","wipe","methodname","itemsShow","usedFlavors","value","includes","length","push","flavorstring","map","item","join","found","loneflavors","lonevariants","lonecomponents","currentUrl","URL","window","href","searchParams","set","toString"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"management.min.js","sources":["../src/management.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Management functions for tiny_elements admin backend.\n *\n * @module tiny_elements/management\n * @copyright 2024 ISB Bayern\n * @author Tobias Garske\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {PreviewModal} from 'tiny_elements/previewmodal';\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {get_string as getString} from 'core/str';\nimport {exception as displayException, deleteCancelPromise} from 'core/notification';\nimport {call as fetchMany} from 'core/ajax';\nimport {render as renderTemplate} from 'core/templates';\nimport Log from 'core/log';\n\nexport const init = async(params) => {\n\n // Add listener to import xml files.\n let importxml = document.getElementById('elements_import');\n importxml.addEventListener('click', async(e) => {\n importModal(e);\n });\n\n // Add listener for adding a new item.\n let additem = document.getElementsByClassName('add');\n additem.forEach(element => {\n element.addEventListener('click', async(e) => {\n showModal(e, element.dataset.id, element.dataset.table);\n });\n });\n\n // Add listener to edit items.\n let edititems = document.getElementsByClassName('edit');\n edititems.forEach(element => {\n element.addEventListener('click', async(e) => {\n showModal(e, element.dataset.id, element.dataset.table);\n });\n });\n\n // Add listener to delete items.\n let deleteitems = document.getElementsByClassName('delete');\n deleteitems.forEach(element => {\n element.addEventListener('click', async(e) => {\n deleteModal(e, element.dataset.id, element.dataset.title, element.dataset.table);\n });\n });\n\n // Add listener to preview items.\n let previewitems = document.getElementsByClassName('preview-button');\n previewitems.forEach(element => {\n element.addEventListener('click', async(e) => {\n previewModal(e);\n });\n });\n\n // Add listener to select compcat to show corresponding items.\n let compcats = document.getElementsByClassName('compcat');\n compcats.forEach(element => {\n element.addEventListener('click', async(e) => {\n showItems(e, element.dataset.compcat);\n });\n });\n\n // Add listener to edit licenses icon.\n let editlicenses = document.getElementsByClassName('editlicenses');\n editlicenses.forEach(element => {\n element.addEventListener('click', async(e) => {\n editlicensesModal(e, element.dataset.id);\n });\n });\n\n // Add listener to manage component flavor relation.\n let buttonicons = document.querySelectorAll('.buttonicons');\n buttonicons.forEach(element => {\n element.addEventListener('click', async(e) => {\n compflavorModal(e);\n });\n });\n\n let displaynamesbutton = document.getElementById('elements_displaynames_button');\n displaynamesbutton.addEventListener('click', async(e) => {\n displaynamesModal(e);\n });\n\n let displaynamesflavorbutton = document.getElementById('elements_displaynames_flavor_button');\n displaynamesflavorbutton.addEventListener('click', async(e) => {\n displaynamesFlavorModal(e);\n });\n\n let displaynamesvariantbutton = document.getElementById('elements_displaynames_variant_button');\n displaynamesvariantbutton.addEventListener('click', async(e) => {\n displaynamesVariantModal(e);\n });\n\n // Add listener to duplicate items.\n let duplicateitems = document.getElementsByClassName('duplicate');\n duplicateitems.forEach(element => {\n element.addEventListener('click', async() => {\n duplicateItem(element.dataset.id, element.dataset.table).always(() => reload());\n });\n });\n\n // Add listener to wipe all items.\n let wipebutton = document.getElementById('elements_wipe');\n if (wipebutton) {\n wipebutton.addEventListener('click', async(e) => {\n wipeModal(e);\n });\n }\n\n // Add image and text to item setting click area.\n let enlargeItems = document.querySelectorAll(\n '.flavor .card-body > .clickingextended, .component .card-body > .clickingextended, .variant .card-body > .clickingextended'\n );\n enlargeItems.forEach(element => {\n element.addEventListener('click', async(e) => {\n let item = e.target.closest('.item');\n item.querySelector('a.edit').click();\n });\n });\n\n // After submitting a new item, reset active compcat.\n if (params.compcatactive) {\n let compcat = document.querySelector('.compcat[data-compcat=\"' + params.compcatactive + '\"]');\n if (compcat) {\n showItems(false, params.compcatactive);\n compcat.classList.add('active');\n }\n }\n};\n\n/**\n * Show dynamic form to add/edit a source.\n * @param {*} event\n * @param {*} id\n * @param {*} table\n */\nconst showModal = async(event, id, table) => {\n event.preventDefault();\n let title;\n if (id == 0) {\n title = getString('additem', 'tiny_elements');\n } else {\n title = getString('edititem', 'tiny_elements');\n }\n\n const modalForm = new ModalForm({\n // Set formclass, depending on component.\n formClass: \"tiny_elements\\\\form\\\\management_\" + table + \"_form\",\n args: {\n id: id,\n compcat: getActiveCompcatId(),\n categoryname: getActiveCompcatName(),\n },\n modalConfig: {title: title},\n returnFocus: event.target,\n });\n // Conditional reload page after submit.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, () => reload());\n\n await modalForm.show();\n};\n\n/**\n * Show modal to preview css version.\n * @param {*} event\n */\nconst previewModal = async(event) => {\n event.preventDefault();\n let preview = event.target.closest(\".preview-button\");\n const modal = await PreviewModal.create({\n templateContext: {\n component: preview.dataset.component,\n flavors: preview.dataset.flavors.trim().split(\" \"),\n config: M.cfg,\n },\n });\n await modal.show();\n};\n\n/**\n * Show dynamic form to import xml backups.\n * @param {*} event\n */\nconst importModal = async(event) => {\n event.preventDefault();\n let title = getString('import', 'tiny_elements');\n\n const modalForm = new ModalForm({\n // Load import form.\n formClass: \"tiny_elements\\\\form\\\\management_import_form\",\n args: {},\n modalConfig: {title: title},\n });\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, importModalSubmitted);\n\n await modalForm.show();\n};\n\n/**\n * Process import form submit.\n * @param {*} event\n */\nconst importModalSubmitted = async(event) => {\n // Reload page after submit.\n if (event.detail.update) {\n location.reload();\n } else {\n event.stopPropagation();\n renderTemplate('tiny_elements/management_import_form_result', event.detail).then(async(html) => {\n await Notification.alert(\n getString('import_simulation', 'tiny_elements'),\n html,\n getString('close', 'tiny_elements')\n );\n return true;\n }).catch((error) => {\n displayException(error);\n });\n }\n};\n\n/**\n * Load modal to edit icon urls.\n * @param {*} event\n */\nconst compflavorModal = async(event) => {\n event.preventDefault();\n let title = getString('manage', 'tiny_elements');\n const target = event.target.closest('.buttonicons');\n const component = target.dataset.component ?? '';\n const flavor = target.dataset.flavor ?? '';\n const modalForm = new ModalForm({\n // Load import form.\n formClass: \"tiny_elements\\\\form\\\\management_comp_flavor_form\",\n args: {\n component: component,\n flavor: flavor,\n },\n modalConfig: {title: title},\n });\n\n await modalForm.show();\n};\n\n/**\n * Load modal to edit licenses of icons.\n * @param {*} event\n * @param {*} id\n */\nconst editlicensesModal = async(event, id) => {\n event.preventDefault();\n let title = getString('editlicenses', 'tiny_elements');\n const modalForm = new ModalForm({\n formClass: \"tiny_elements\\\\form\\\\management_editlicense_form\",\n args: {\n id: id,\n },\n modalConfig: {title: title},\n });\n await modalForm.show();\n};\n\n/**\n * Load modal to edit displaynames.\n * @param {*} event\n * @returns {void}\n */\nconst displaynamesModal = async(event) => {\n event.preventDefault();\n let title = getString('manage', 'tiny_elements');\n\n const modalForm = new ModalForm({\n // Load displaynames bulk edit form.\n formClass: \"tiny_elements\\\\form\\\\management_displaynames_form\",\n args: {},\n modalConfig: {title: title},\n });\n\n // Reload page after submit.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, () => location.reload());\n\n await modalForm.show();\n};\n\n/**\n * Load modal to edit displaynames.\n * @param {*} event\n * @returns {void}\n */\nconst displaynamesFlavorModal = async(event) => {\n event.preventDefault();\n let title = getString('manage', 'tiny_elements');\n\n const modalForm = new ModalForm({\n // Load displaynames bulk edit form.\n formClass: \"tiny_elements\\\\form\\\\management_displaynames_flavors_form\",\n args: {},\n modalConfig: {title: title},\n });\n\n // Reload page after submit.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, () => location.reload());\n\n await modalForm.show();\n};\n\n/**\n * Load modal to edit displaynames.\n * @param {*} event\n * @returns {void}\n */\nconst displaynamesVariantModal = async(event) => {\n event.preventDefault();\n let title = getString('manage', 'tiny_elements');\n\n const modalForm = new ModalForm({\n // Load displaynames bulk edit form.\n formClass: \"tiny_elements\\\\form\\\\management_displaynames_variants_form\",\n args: {},\n modalConfig: {title: title},\n });\n\n // Reload page after submit.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, () => location.reload());\n\n await modalForm.show();\n};\n\n/**\n * Show dynamic form to delete a source.\n * @param {*} event\n * @param {*} id\n * @param {*} title\n * @param {*} table\n */\nconst deleteModal = (event, id, title, table) => {\n event.preventDefault();\n\n deleteCancelPromise(\n getString('delete', 'tiny_elements', title),\n getString('deletewarning', 'tiny_elements'),\n ).then(async() => {\n if (id !== 0) {\n try {\n const deleted = await deleteItem(id, table);\n if (deleted) {\n const link = document.querySelector('[data-table=\"' + table + '\"][data-id=\"' + id + '\"]');\n if (link) {\n const card = link.closest(\".item\");\n card.remove();\n }\n }\n } catch (error) {\n displayException(error);\n }\n }\n return;\n }).catch((err) => {\n if (err.message) {\n Log.error(err.message);\n }\n return;\n });\n};\n\n/**\n * Show a modal to confirm wiping all items.\n * @param {*} event\n */\nconst wipeModal = (event) => {\n event.preventDefault();\n\n deleteCancelPromise(\n getString('wipe', 'tiny_elements'),\n getString('wipewarning', 'tiny_elements')\n ).then(async() => {\n try {\n await wipe();\n reload();\n return;\n } catch (error) {\n displayException(error);\n return;\n }\n }).catch((err) => {\n if (err.message) {\n Log.error(err.message);\n }\n return;\n });\n};\n\n/**\n * Delete elements items.\n * @param {*} id\n * @param {*} table\n * @returns {mixed}\n */\nexport const deleteItem = (\n id,\n table,\n) => fetchMany(\n [{\n methodname: 'tiny_elements_delete_item',\n args: {\n id,\n table,\n }\n }])[0];\n\n/**\n * Wipe all elements items.\n * @returns {mixed}\n */\nexport const wipe = () => fetchMany(\n [{\n methodname: 'tiny_elements_wipe',\n args: {\n \"contextid\": 1,\n }\n }])[0];\n\n/**\n * Show items after clicking a compcat.\n * @param {*} event\n * @param {*} compcat\n */\nconst showItems = (event, compcat) => {\n // But first hide all items.\n let itemsHide = document.querySelectorAll('.flavor, .component, .variant');\n itemsHide.forEach(element => {\n element.classList.add('hidden');\n });\n\n // Show component and variants with compcat name and read the flavors.\n let itemsShow = document.querySelectorAll('[data-categoryname=\"' + compcat + '\"]');\n let usedFlavors = [];\n itemsShow.forEach(element => {\n element.classList.remove('hidden');\n // Get all flavors to show if on compcat element.\n if (typeof element.dataset.flavors !== 'undefined') {\n let flavors = element.dataset.flavors.split(' ');\n for (let value of flavors) {\n if (!usedFlavors.includes(value) && value.length != 0) {\n usedFlavors.push(value);\n }\n }\n }\n });\n\n // Show the flavors.\n let flavorstring = usedFlavors.map(item => `.${item}`).join(', ');\n if (flavorstring.length) {\n let flavorsShow = document.querySelectorAll(flavorstring);\n flavorsShow.forEach(element => {\n element.classList.remove('hidden');\n });\n }\n\n // Show add buttons.\n let addsShow = document.getElementsByClassName('addcontainer');\n addsShow.forEach(element => {\n element.classList.remove('hidden');\n });\n\n // Unmark all and mark clicked compcat.\n if (event) {\n let items = document.getElementsByClassName('compcat');\n items.forEach(element => {\n element.classList.remove('active');\n });\n let item = event.target.closest('.compcat');\n item.classList.add('active');\n }\n\n // Special case, unassigned items, show all items without connection to compcat.\n if (compcat == 'found-items') {\n let found = document.querySelector('.compcat[data-compcat=\"found-items\"]');\n if (found.dataset.loneflavors.length) {\n let flavorsShow = document.querySelectorAll(found.dataset.loneflavors);\n flavorsShow.forEach(element => {\n element.classList.remove('hidden');\n });\n }\n if (found.dataset.lonevariants.length) {\n let variantsShow = document.querySelectorAll(found.dataset.lonevariants);\n variantsShow.forEach(element => {\n element.classList.remove('hidden');\n });\n }\n if (found.dataset.lonecomponents.length) {\n let componentsShow = document.querySelectorAll(found.dataset.lonecomponents);\n componentsShow.forEach(element => {\n element.classList.remove('hidden');\n });\n }\n }\n};\n\n/**\n * Reload page with active compcat.\n */\nconst reload = () => {\n // Reload page with active compcat.\n const currentUrl = new URL(window.location.href);\n currentUrl.searchParams.set('compcat', getActiveCompcatName());\n window.location.href = currentUrl.toString();\n window.location.reload();\n};\n\n/**\n * Get the current active compcat.\n * @returns string Name of active compcat.\n */\nconst getActiveCompcatName = () => {\n const compcat = document.querySelector('.compcat.active');\n if (!compcat) {\n return '';\n }\n return compcat.dataset.compcat ?? '';\n};\n\n/**\n * Get the current active compcat.\n * @returns int Id of active compcat.\n */\nconst getActiveCompcatId = () => {\n const compcat = document.querySelector('.compcat.active');\n if (!compcat) {\n return 0;\n }\n return compcat.dataset.id ?? 0;\n};\n\n/**\n * Duplicate elements items.\n * @param {*} id\n * @param {*} table\n * @returns {mixed}\n */\nexport const duplicateItem = (id, table) => fetchMany(\n [{\n methodname: 'tiny_elements_duplicate_item',\n args: {\n id,\n table,\n }\n }])[0];\n"],"names":["async","document","getElementById","addEventListener","importModal","e","getElementsByClassName","forEach","element","showModal","dataset","id","table","deleteModal","title","previewModal","showItems","compcat","editlicensesModal","querySelectorAll","compflavorModal","displaynamesModal","displaynamesFlavorModal","displaynamesVariantModal","duplicateItem","always","reload","wipebutton","wipeModal","target","closest","querySelector","click","params","compcatactive","classList","add","event","preventDefault","modalForm","ModalForm","formClass","args","getActiveCompcatId","categoryname","getActiveCompcatName","modalConfig","returnFocus","events","FORM_SUBMITTED","show","preview","modal","PreviewModal","create","templateContext","component","flavors","trim","split","config","M","cfg","importModalSubmitted","detail","update","location","stopPropagation","then","Notification","alert","html","catch","error","flavor","deleteItem","link","remove","err","message","wipe","methodname","itemsShow","usedFlavors","value","includes","length","push","flavorstring","map","item","join","found","loneflavors","lonevariants","lonecomponents","currentUrl","URL","window","href","searchParams","set","toString"],"mappings":"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"}
\ No newline at end of file
diff --git a/amd/build/previewall.min.js b/amd/build/previewall.min.js
new file mode 100644
index 0000000..3df11bd
--- /dev/null
+++ b/amd/build/previewall.min.js
@@ -0,0 +1,3 @@
+define("tiny_elements/previewall",["exports","core/toast","core/str"],(function(_exports,_toast,_str){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0;_exports.init=async()=>{document.getElementById("elements_to_clipboard").addEventListener("click",(()=>{const allelements=document.getElementById("elements_output");navigator.clipboard.writeText(allelements.value).then((()=>{(0,_toast.add)((0,_str.get_string)("copysuccess","tiny_elements"),{type:"info"})})).catch((()=>{(0,_toast.add)((0,_str.get_string)("copyfail","tiny_elements"),{type:"warning"})}))}))}}));
+
+//# sourceMappingURL=previewall.min.js.map
\ No newline at end of file
diff --git a/amd/build/previewall.min.js.map b/amd/build/previewall.min.js.map
new file mode 100644
index 0000000..db22623
--- /dev/null
+++ b/amd/build/previewall.min.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"previewall.min.js","sources":["../src/previewall.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Elements preview all.\n *\n * @module tiny_elements/previewall\n * @copyright 2025 Tobias Garske\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {add as addToast} from 'core/toast';\nimport {get_string as getString} from \"core/str\";\n\nexport const init = async() => {\n // Add listener to copy all elements as string.\n document.getElementById(\"elements_to_clipboard\").addEventListener(\"click\", () => {\n const allelements = document.getElementById(\"elements_output\");\n navigator.clipboard.writeText(allelements.value)\n .then(() => {\n addToast(getString('copysuccess', 'tiny_elements'), {\n type: 'info',\n });\n return;\n })\n .catch(() => {\n addToast(getString('copyfail', 'tiny_elements'), {\n type: 'warning',\n });\n return;\n });\n });\n};\n"],"names":["async","document","getElementById","addEventListener","allelements","navigator","clipboard","writeText","value","then","type","catch"],"mappings":"iMA0BoBA,UAEhBC,SAASC,eAAe,yBAAyBC,iBAAiB,SAAS,WACjEC,YAAcH,SAASC,eAAe,mBAC5CG,UAAUC,UAAUC,UAAUH,YAAYI,OACzCC,MAAK,qBACO,mBAAU,cAAe,iBAAkB,CAChDC,KAAM,YAIbC,OAAM,qBACM,mBAAU,WAAY,iBAAkB,CAC7CD,KAAM"}
\ No newline at end of file
diff --git a/amd/src/management.js b/amd/src/management.js
index 6ed63d0..a5ce911 100644
--- a/amd/src/management.js
+++ b/amd/src/management.js
@@ -396,8 +396,10 @@ const wipeModal = (event) => {
try {
await wipe();
reload();
+ return;
} catch (error) {
displayException(error);
+ return;
}
}).catch((err) => {
if (err.message) {
diff --git a/amd/src/previewall.js b/amd/src/previewall.js
new file mode 100644
index 0000000..dedc851
--- /dev/null
+++ b/amd/src/previewall.js
@@ -0,0 +1,45 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle 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.
+//
+// Moodle 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 Moodle. If not, see .
+
+/**
+ * Elements preview all.
+ *
+ * @module tiny_elements/previewall
+ * @copyright 2025 Tobias Garske
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+import {add as addToast} from 'core/toast';
+import {get_string as getString} from "core/str";
+
+export const init = async() => {
+ // Add listener to copy all elements as string.
+ document.getElementById("elements_to_clipboard").addEventListener("click", () => {
+ const allelements = document.getElementById("elements_output");
+ navigator.clipboard.writeText(allelements.value)
+ .then(() => {
+ addToast(getString('copysuccess', 'tiny_elements'), {
+ type: 'info',
+ });
+ return;
+ })
+ .catch(() => {
+ addToast(getString('copyfail', 'tiny_elements'), {
+ type: 'warning',
+ });
+ return;
+ });
+ });
+};
diff --git a/classes/exporter.php b/classes/exporter.php
index f5bd451..481ce8d 100644
--- a/classes/exporter.php
+++ b/classes/exporter.php
@@ -212,7 +212,7 @@ public function export_flavors_and_variants(
$params
);
$this->write_elements($xmlwriter, constants::TABLES['compflavor'], $compflavors);
- $flavornames = array_unique(array_column($compflavors, 'flavor'));
+ $flavornames = array_unique(array_column($compflavors, 'flavorname'));
$sql = ' = name';
if (!empty($categoryname)) {
diff --git a/classes/external/wipe.php b/classes/external/wipe.php
index 12fc7cc..18c6b87 100644
--- a/classes/external/wipe.php
+++ b/classes/external/wipe.php
@@ -45,6 +45,7 @@ public static function execute_parameters(): external_function_parameters {
/**
* Implementation of web service tiny_elements_wipe
+ * @param int $contextid
* @return array
*/
public static function execute(int $contextid): array {
diff --git a/classes/form/management_editlicense_form.php b/classes/form/management_editlicense_form.php
index bf20eb9..0f74793 100644
--- a/classes/form/management_editlicense_form.php
+++ b/classes/form/management_editlicense_form.php
@@ -108,6 +108,7 @@ public function definition(): void {
$mform->removeElement('adddummy');
$mform->setAttributes(['data-formtype' => 'tiny_elements_editlicense']);
+ $mform->disable_form_change_checker();
}
/**
diff --git a/lang/de/tiny_elements.php b/lang/de/tiny_elements.php
index 32f5f42..cf779a1 100644
--- a/lang/de/tiny_elements.php
+++ b/lang/de/tiny_elements.php
@@ -47,7 +47,10 @@
$string['componentname_help'] = 'Name der Komponente zur internen Verwendung (auch als Klassenname in CSS)';
$string['components'] = 'Komponenten';
$string['content'] = 'Inhalt';
+$string['copyasstring'] = 'Alle Elemente als String in den Zwischenspeicher laden';
+$string['copyfail'] = 'Fehler beim Kopieren aufgetreten';
$string['copyof'] = 'Kopie von {$a}';
+$string['copysuccess'] = 'Elemente in den Zwischenspeicher kopiert';
$string['css'] = 'CSS';
$string['delete'] = 'Element "{$a}" löschen';
$string['deletewarning'] = 'Sind Sie sicher, dass Sie dieses Element löschen möchten?';
@@ -94,6 +97,8 @@
$string['js'] = 'JS';
$string['linktomanagerdesc'] = 'Gehen Sie zu Verwaltung, um Änderungen vorzunehmen.';
$string['linktomanagername'] = 'Link zur Verwaltung';
+$string['linktopreviewall'] = 'Link zur Vorschau';
+$string['linktopreviewall_desc'] = 'Gehen Sie zu Vorschau, um alle Elements anzusehen und als String zu exportieren.';
$string['manage'] = 'Verwalten';
$string['management'] = 'Verwaltung';
$string['menuitem_elements'] = 'Kurselemente';
diff --git a/lang/en/tiny_elements.php b/lang/en/tiny_elements.php
index 17790ea..3744797 100644
--- a/lang/en/tiny_elements.php
+++ b/lang/en/tiny_elements.php
@@ -45,7 +45,10 @@
$string['componentname_help'] = 'Name of the component use for internal use (also as a class name in CSS)';
$string['components'] = 'Components';
$string['content'] = 'Content';
+$string['copyasstring'] = 'Save all elements to clipboard';
+$string['copyfail'] = 'Error copying elements';
$string['copyof'] = 'Copy of {$a}';
+$string['copysuccess'] = 'Elements copied to clipboard';
$string['css'] = 'CSS';
$string['delete'] = 'Delete item "{$a}"';
$string['deletewarning'] = 'Are you sure you want to delete this item.';
@@ -91,6 +94,9 @@
$string['js'] = 'JS';
$string['linktomanagerdesc'] = 'Go to management page to edit categories, components, flavors and variants.';
$string['linktomanagername'] = 'Link to management';
+$string['linktopreviewall'] = 'Link to preview';
+$string['linktopreviewall_desc'] = 'Go to preview to watch all elements and export as string.';
+
$string['manage'] = 'Manage';
$string['management'] = 'Management';
$string['menuitem_elements'] = 'Course elements';
diff --git a/preview.php b/preview.php
index 30edf63..2218b5e 100644
--- a/preview.php
+++ b/preview.php
@@ -53,10 +53,6 @@
$componentdata->code = tiny_elements\local\utils::replace_pluginfile_urls($componentdata->code, true);
-if (empty($flavordata)) {
- echo str_replace('{{FLAVOR}}', '', $componentdata->code);
-} else {
- echo str_replace('{{FLAVOR}}', $flavordata->name, $componentdata->code);
-}
+echo $componentdata->code;
echo $OUTPUT->footer();
diff --git a/previewall.php b/previewall.php
new file mode 100644
index 0000000..f59d376
--- /dev/null
+++ b/previewall.php
@@ -0,0 +1,96 @@
+.
+
+/**
+ * Creates a preview for all elements components in all flavors and variants.
+ *
+ * @package tiny_elements
+ * @copyright 2024 Tobias Garske, ISB Bayern
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require('../../../../../config.php');
+
+require_login();
+
+$url = new moodle_url('/lib/editor/tiny/plugins/elements/previewall.php', []);
+$PAGE->set_url($url);
+$PAGE->set_context(context_system::instance());
+$PAGE->set_heading($SITE->fullname);
+
+echo $OUTPUT->header();
+
+// Iterate over every category.
+$categorydata = $DB->get_records('tiny_elements_compcat');
+$outputbuffer = '';
+foreach ($categorydata as $category) {
+ $outputbuffer .= '
' . $category->name . '
';
+ $category = $category->name;
+ $componentdata = $DB->get_records('tiny_elements_component', ['categoryname' => $category]);
+ // Iterate over every component.
+ foreach ($componentdata as $component) {
+ $outputbuffer .= '' . $component->name . '
';
+ // Select corresponding flavors and variants.
+ $sql = "SELECT * FROM {tiny_elements_flavor} fla
+ JOIN {tiny_elements_comp_flavor} comfla ON comfla.flavorname = fla.name
+ WHERE fla.categoryname = :categoryname AND comfla.componentname = :componentname";
+ $allflavors = $DB->get_records_sql($sql, ['categoryname' => $component->categoryname, 'componentname' => $component->name]);
+ $sql = "SELECT * FROM {tiny_elements_variant} var
+ JOIN {tiny_elements_comp_variant} covar ON var.name = covar.variant
+ WHERE covar.componentname = :componentname";
+ $allvariants = $DB->get_records_sql($sql, ['componentname' => $component->name]);
+ // Add default "variant".
+ array_unshift($allvariants, (object) ['name' => '']);
+ foreach ($allflavors as $flavordata) {
+ foreach ($allvariants as $variantdata) {
+ $tmpcomponent = clone $component;
+ $variant = '';
+ if (strlen($variantdata->name) > 0) {
+ $variant = 'elements-' . $variantdata->name . '-variant';
+ }
+ $varianthtml = '';
+ // Build elements by replacing placeholders.
+ $tmpcomponent->code = str_replace('{{CATEGORY}}', 'elements-' . $category, $tmpcomponent->code);
+ $tmpcomponent->code = str_replace('{{COMPONENT}}', 'elements-' . $tmpcomponent->name, $tmpcomponent->code);
+ $tmpcomponent->code = str_replace(
+ '{{FLAVOR}}',
+ 'elements-' . $flavordata->name . '-flavor',
+ $tmpcomponent->code
+ );
+ $tmpcomponent->code = str_replace('{{VARIANTS}}', $variant, $tmpcomponent->code);
+ $tmpcomponent->code = str_replace('{{VARIANTSHTML}}', $varianthtml, $tmpcomponent->code);
+ $tmpcomponent->code = str_replace(
+ '{{PLACEHOLDER}}',
+ $flavordata->name . ' ' . $variantdata->name,
+ $tmpcomponent->code
+ );
+ // Output element.
+ $tmpcomponent->code = tiny_elements\local\utils::replace_pluginfile_urls($tmpcomponent->code, true);
+ $outputbuffer .= $tmpcomponent->code;
+ }
+ }
+ }
+}
+
+// Create button to copy elements to clipboard.
+echo "";
+echo "";
+$PAGE->requires->js_call_amd('tiny_elements/previewall', 'init');
+
+echo $outputbuffer;
+
+echo $OUTPUT->footer();
diff --git a/settings.php b/settings.php
index 5f0fdd0..f018aae 100644
--- a/settings.php
+++ b/settings.php
@@ -51,4 +51,15 @@
(new moodle_url('/lib/editor/tiny/plugins/elements/management.php'))->out()
)
));
+
+ // Add text with link to previewpage as setting.
+ $settings->add(new admin_setting_description(
+ 'tiny_elements/previewall',
+ get_string('linktopreviewall', 'tiny_elements'),
+ get_string(
+ 'linktopreviewall_desc',
+ 'tiny_elements',
+ (new moodle_url('/lib/editor/tiny/plugins/elements/previewall.php'))->out()
+ )
+ ));
}
diff --git a/templates/management.mustache b/templates/management.mustache
index 15e61de..65ae139 100644
--- a/templates/management.mustache
+++ b/templates/management.mustache
@@ -76,7 +76,7 @@
{{#str}} compcat, tiny_elements {{/str}}
{{#compcats}}
-
@@ -154,11 +154,11 @@
-
+
-
+
{{#str}} components, tiny_elements {{/str}}
@@ -207,7 +207,7 @@
-
+
@@ -215,7 +215,7 @@
{{#str}} variants, tiny_elements {{/str}}
{{#variant}}
-
@@ -246,9 +246,8 @@
-
+
-
diff --git a/templates/management_preview.mustache b/templates/management_preview.mustache
index d8a05c2..bc4c36a 100644
--- a/templates/management_preview.mustache
+++ b/templates/management_preview.mustache
@@ -38,4 +38,4 @@
{{/flavors}}
{{/body}}
-{{/ core/modal }}
\ No newline at end of file
+{{/ core/modal }}