diff --git a/lib/editor/tiny/plugins/aiplacement/amd/build/generatebase.min.js b/lib/editor/tiny/plugins/aiplacement/amd/build/generatebase.min.js index 0e03509cbce..538b2084ad0 100644 --- a/lib/editor/tiny/plugins/aiplacement/amd/build/generatebase.min.js +++ b/lib/editor/tiny/plugins/aiplacement/amd/build/generatebase.min.js @@ -1,3 +1,3 @@ -define("tiny_aiplacement/generatebase",["exports","tiny_aiplacement/loading","core/str","tiny_aiplacement/options","core_ai/policy","core_ai/policymodal","core/custom_interaction_events","./options"],(function(_exports,_loading,_str,_options,_policy,_policymodal,_custom_interaction_events,_options2){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_policy=_interopRequireDefault(_policy),_policymodal=_interopRequireDefault(_policymodal),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events);return _exports.default=class{constructor(editor){var obj,key,value;value=void 0,(key="modalObject")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.editor=editor,this.userid=(0,_options.getUserId)(editor),this.contextid=(0,_options.getContextId)(editor),this.responseObj=null}async displayContentModal(){if(_policy.default.preconfigurePolicyState(this.userid,(0,_options2.isPolicyAgreed)(this.editor)),await _policy.default.getPolicyStatus(this.userid))this.modalObject=await this.setupModal();else{const policyModal=await _policymodal.default.create();policyModal.getModal().on(_custom_interaction_events.default.events.activate,policyModal.getActionSelector("save"),(()=>{this.displayContentModal()}))}}getModalClass(){throw new Error("Method 'getModalClass' must be implemented.")}async setupModal(){const modal=this.getModalClass().create({templateContext:{elementid:this.editor.id}});return this.addContentEventListeners(modal),modal}async addContentEventListeners(modal){const root=(await modal).getRoot()[0];root.addEventListener("click",(e=>{this.handleContentModalClick(e,root)})),this.setupPromptArea(root),this.hideLoadingSpinner(root)}handleContentModalClick(){throw new Error("Method handleContentModalClick must be implemented.")}hideLoadingSpinner(root){const loadingSpinnerDiv=root.querySelector("#".concat(this.editor.id,"_tiny_aiplacement_spinner"));loadingSpinnerDiv.classList.add("hidden"),loadingSpinnerDiv.classList.remove("tiny-aiplacement-loading-spinner-container")}async displayLoading(root,submitBtn){let removeClass=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const loadingSpinnerDiv=root.querySelector("#".concat(this.editor.id,"_tiny_aiplacement_spinner")),overlayDiv=root.querySelector("#".concat(this.editor.id,"_tiny_aiplacement_overlay")),blurDiv=root.querySelector("#".concat(this.editor.id,"_tiny_aiplacement_blur")),loadingTextDiv=root.querySelector("#".concat(this.editor.id,"_tiny_aiplacement_loading_text")),actionButtons=root.querySelectorAll(".tiny-aiplacement-generate-footer button");(0,_loading.loadingMessages)(loadingTextDiv),removeClass&&loadingSpinnerDiv.classList.remove(removeClass),loadingSpinnerDiv.classList.remove("hidden"),overlayDiv.classList.remove("hidden"),blurDiv.classList.add("tiny-aiplacement-blur"),submitBtn.innerHTML=await(0,_str.getString)("generating","tiny_aiplacement"),actionButtons&&actionButtons.forEach((button=>{button.disabled=!0}))}async hideLoading(root,submitBtn){const loadingSpinnerDiv=root.querySelector("#".concat(this.editor.id,"_tiny_aiplacement_spinner")),overlayDiv=root.querySelector("#".concat(this.editor.id,"_tiny_aiplacement_overlay")),blurDiv=root.querySelector("#".concat(this.editor.id,"_tiny_aiplacement_blur")),actionButtons=root.querySelectorAll(".tiny-aiplacement-generate-footer button");loadingSpinnerDiv&&loadingSpinnerDiv.classList.add("hidden"),overlayDiv&&overlayDiv.classList.add("hidden"),blurDiv&&blurDiv.classList.remove("tiny-aiplacement-blur"),submitBtn.innerHTML=await(0,_str.getString)("regenerate","tiny_aiplacement"),actionButtons&&actionButtons.forEach((button=>{button.disabled=!1}))}},_exports.default})); +define("tiny_aiplacement/generatebase",["exports","tiny_aiplacement/loading","core/str","tiny_aiplacement/options","core_ai/policy","core_ai/policymodal","core/custom_interaction_events","./options"],(function(_exports,_loading,_str,_options,_policy,_policymodal,_custom_interaction_events,_options2){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_policy=_interopRequireDefault(_policy),_policymodal=_interopRequireDefault(_policymodal),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events);return _exports.default=class{constructor(editor){var obj,key,value;value=void 0,(key="modalObject")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.editor=editor,this.userid=(0,_options.getUserId)(editor),this.contextid=(0,_options.getContextId)(editor),this.responseObj=null}async displayContentModal(){if(_policy.default.preconfigurePolicyState(this.userid,(0,_options2.isPolicyAgreed)(this.editor)),await _policy.default.getPolicyStatus(this.userid))this.modalObject=await this.setupModal();else{const policyModal=await _policymodal.default.create();policyModal.getModal().on(_custom_interaction_events.default.events.activate,policyModal.getActionSelector("save"),(()=>{this.displayContentModal()}))}}getModalClass(){throw new Error("Method 'getModalClass' must be implemented.")}async setupModal(){const modal=this.getModalClass().create({templateContext:{elementid:this.editor.id}});return this.addContentEventListeners(modal),modal}async addContentEventListeners(modal){const root=(await modal).getRoot()[0];root.addEventListener("click",(e=>{this.handleContentModalClick(e,root)})),this.setupPromptArea(root),this.hideLoadingSpinner(root)}handleContentModalClick(){throw new Error("Method handleContentModalClick must be implemented.")}hideLoadingSpinner(root){const loadingSpinnerDiv=root.querySelector('[id="'.concat(this.editor.id,'_tiny_aiplacement_spinner"]'));loadingSpinnerDiv.classList.add("hidden"),loadingSpinnerDiv.classList.remove("tiny-aiplacement-loading-spinner-container")}async displayLoading(root,submitBtn){let removeClass=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const loadingSpinnerDiv=root.querySelector('[id="'.concat(this.editor.id,'_tiny_aiplacement_spinner"]')),overlayDiv=root.querySelector('[id="'.concat(this.editor.id,'_tiny_aiplacement_overlay"]')),blurDiv=root.querySelector('[id="'.concat(this.editor.id,'_tiny_aiplacement_blur"]')),loadingTextDiv=root.querySelector('[id="'.concat(this.editor.id,'_tiny_aiplacement_loading_text"]')),actionButtons=root.querySelectorAll(".tiny-aiplacement-generate-footer button");(0,_loading.loadingMessages)(loadingTextDiv),removeClass&&loadingSpinnerDiv.classList.remove(removeClass),loadingSpinnerDiv.classList.remove("hidden"),overlayDiv.classList.remove("hidden"),blurDiv.classList.add("tiny-aiplacement-blur"),submitBtn.innerHTML=await(0,_str.getString)("generating","tiny_aiplacement"),actionButtons&&actionButtons.forEach((button=>{button.disabled=!0}))}async hideLoading(root,submitBtn){const loadingSpinnerDiv=root.querySelector('[id="'.concat(this.editor.id,'_tiny_aiplacement_spinner"]')),overlayDiv=root.querySelector('[id="'.concat(this.editor.id,'_tiny_aiplacement_overlay"]')),blurDiv=root.querySelector('[id="'.concat(this.editor.id,'_tiny_aiplacement_blur"]')),actionButtons=root.querySelectorAll(".tiny-aiplacement-generate-footer button");loadingSpinnerDiv&&loadingSpinnerDiv.classList.add("hidden"),overlayDiv&&overlayDiv.classList.add("hidden"),blurDiv&&blurDiv.classList.remove("tiny-aiplacement-blur"),submitBtn.innerHTML=await(0,_str.getString)("regenerate","tiny_aiplacement"),actionButtons&&actionButtons.forEach((button=>{button.disabled=!1}))}},_exports.default})); //# sourceMappingURL=generatebase.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/aiplacement/amd/build/generatebase.min.js.map b/lib/editor/tiny/plugins/aiplacement/amd/build/generatebase.min.js.map index 114fbab3670..151595ea6be 100644 --- a/lib/editor/tiny/plugins/aiplacement/amd/build/generatebase.min.js.map +++ b/lib/editor/tiny/plugins/aiplacement/amd/build/generatebase.min.js.map @@ -1 +1 @@ -{"version":3,"file":"generatebase.min.js","sources":["../src/generatebase.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 * Tiny AI base generate class.\n *\n * @module tiny_aiplacement/generatebase\n * @copyright 2024 Matt Porritt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {loadingMessages} from 'tiny_aiplacement/loading';\nimport {getString} from 'core/str';\nimport {\n getContextId,\n getUserId,\n} from 'tiny_aiplacement/options';\nimport Policy from 'core_ai/policy';\nimport PolicyModal from 'core_ai/policymodal';\nimport CustomEvents from 'core/custom_interaction_events';\nimport {isPolicyAgreed} from './options';\n\nexport default class GenerateBase {\n modalObject;\n\n /**\n * Class constructor.\n *\n * @param {TinyMCE.editor} editor The tinyMCE editor instance.\n */\n constructor(editor) {\n this.editor = editor;\n this.userid = getUserId(editor);\n this.contextid = getContextId(editor);\n this.responseObj = null;\n }\n\n /**\n * Display the modal when the AI button is clicked.\n *\n */\n async displayContentModal() {\n Policy.preconfigurePolicyState(this.userid, isPolicyAgreed(this.editor));\n if (!await Policy.getPolicyStatus(this.userid)) {\n const policyModal = await PolicyModal.create();\n policyModal.getModal().on(CustomEvents.events.activate, policyModal.getActionSelector('save'), () => {\n this.displayContentModal();\n });\n return;\n }\n\n this.modalObject = await this.setupModal();\n }\n\n getModalClass() {\n throw new Error(\"Method 'getModalClass' must be implemented.\");\n }\n\n\n /**\n * Set up the base text generation modal with default body content.\n *\n * @returns {TextModal} The image modal object.\n */\n async setupModal() {\n const modal = this.getModalClass().create({\n templateContext: {\n elementid: this.editor.id,\n },\n });\n\n this.addContentEventListeners(modal);\n\n return modal;\n }\n\n /**\n * Add event listeners for the text modal.\n *\n * @param {Modal} modal\n */\n async addContentEventListeners(modal) {\n const modalRoot = (await modal).getRoot();\n const root = modalRoot[0];\n\n root.addEventListener('click', (e) => {\n this.handleContentModalClick(e, root);\n });\n\n this.setupPromptArea(root);\n this.hideLoadingSpinner(root);\n }\n\n handleContentModalClick() {\n throw new Error('Method handleContentModalClick must be implemented.');\n }\n\n /**\n * Hide the loading spinner.\n *\n * @param {Object} root The root element of the modal.\n */\n hideLoadingSpinner(root) {\n const loadingSpinnerDiv = root.querySelector(`#${this.editor.id}_tiny_aiplacement_spinner`);\n loadingSpinnerDiv.classList.add('hidden');\n loadingSpinnerDiv.classList.remove('tiny-aiplacement-loading-spinner-container');\n }\n\n /**\n * Display the loading state in the modal.\n *\n * @param {HTMLElement} root - The root element of the modal.\n * @param {HTMLElement} submitBtn - The submit button element.\n * @param {String|null} removeClass - The class to be removed from the loading spinner div, if any.\n */\n async displayLoading(root, submitBtn, removeClass = null) {\n const loadingSpinnerDiv = root.querySelector(`#${this.editor.id}_tiny_aiplacement_spinner`);\n const overlayDiv = root.querySelector(`#${this.editor.id}_tiny_aiplacement_overlay`);\n const blurDiv = root.querySelector(`#${this.editor.id}_tiny_aiplacement_blur`);\n const loadingTextDiv = root.querySelector(`#${this.editor.id}_tiny_aiplacement_loading_text`);\n const actionButtons = root.querySelectorAll('.tiny-aiplacement-generate-footer button');\n\n loadingMessages(loadingTextDiv);\n\n if (removeClass) {\n loadingSpinnerDiv.classList.remove(removeClass);\n }\n\n loadingSpinnerDiv.classList.remove('hidden');\n overlayDiv.classList.remove('hidden');\n blurDiv.classList.add('tiny-aiplacement-blur');\n submitBtn.innerHTML = await getString('generating', 'tiny_aiplacement');\n\n if (actionButtons) {\n actionButtons.forEach((button) => {\n button.disabled = true;\n });\n }\n }\n\n /**\n * Hide the loading action in the modal.\n *\n * @param {Object} root The root element of the modal.\n * @param {Object} submitBtn The submit button element.\n */\n async hideLoading(root, submitBtn) {\n const loadingSpinnerDiv = root.querySelector(`#${this.editor.id}_tiny_aiplacement_spinner`);\n const overlayDiv = root.querySelector(`#${this.editor.id}_tiny_aiplacement_overlay`);\n const blurDiv = root.querySelector(`#${this.editor.id}_tiny_aiplacement_blur`);\n const actionButtons = root.querySelectorAll('.tiny-aiplacement-generate-footer button');\n if (loadingSpinnerDiv) {\n loadingSpinnerDiv.classList.add('hidden');\n }\n if (overlayDiv) {\n overlayDiv.classList.add('hidden');\n }\n if (blurDiv) {\n blurDiv.classList.remove('tiny-aiplacement-blur');\n }\n submitBtn.innerHTML = await getString('regenerate', 'tiny_aiplacement');\n\n if (actionButtons) {\n actionButtons.forEach((button) => {\n button.disabled = false;\n });\n }\n }\n}\n"],"names":["constructor","editor","userid","contextid","responseObj","preconfigurePolicyState","this","Policy","getPolicyStatus","modalObject","setupModal","policyModal","PolicyModal","create","getModal","on","CustomEvents","events","activate","getActionSelector","displayContentModal","getModalClass","Error","modal","templateContext","elementid","id","addContentEventListeners","root","getRoot","addEventListener","e","handleContentModalClick","setupPromptArea","hideLoadingSpinner","loadingSpinnerDiv","querySelector","classList","add","remove","submitBtn","removeClass","overlayDiv","blurDiv","loadingTextDiv","actionButtons","querySelectorAll","innerHTML","forEach","button","disabled"],"mappings":"qpBA0CIA,YAAYC,kLACHA,OAASA,YACTC,QAAS,sBAAUD,aACnBE,WAAY,yBAAaF,aACzBG,YAAc,oDAQZC,wBAAwBC,KAAKJ,QAAQ,4BAAeI,KAAKL,eACrDM,gBAAOC,gBAAgBF,KAAKJ,aAQlCO,kBAAoBH,KAAKI,wBAPpBC,kBAAoBC,qBAAYC,SACtCF,YAAYG,WAAWC,GAAGC,mCAAaC,OAAOC,SAAUP,YAAYQ,kBAAkB,SAAS,UACtFC,0BAQjBC,sBACU,IAAIC,MAAM,wEAUVC,MAAQjB,KAAKe,gBAAgBR,OAAO,CACtCW,gBAAiB,CACbC,UAAWnB,KAAKL,OAAOyB,kBAI1BC,yBAAyBJ,OAEvBA,qCAQoBA,aAErBK,YADmBL,OAAOM,UACT,GAEvBD,KAAKE,iBAAiB,SAAUC,SACvBC,wBAAwBD,EAAGH,cAG/BK,gBAAgBL,WAChBM,mBAAmBN,MAG5BI,gCACU,IAAIV,MAAM,uDAQpBY,mBAAmBN,YACTO,kBAAoBP,KAAKQ,yBAAkB9B,KAAKL,OAAOyB,iCAC7DS,kBAAkBE,UAAUC,IAAI,UAChCH,kBAAkBE,UAAUE,OAAO,mEAUlBX,KAAMY,eAAWC,mEAAc,WAC1CN,kBAAoBP,KAAKQ,yBAAkB9B,KAAKL,OAAOyB,iCACvDgB,WAAad,KAAKQ,yBAAkB9B,KAAKL,OAAOyB,iCAChDiB,QAAUf,KAAKQ,yBAAkB9B,KAAKL,OAAOyB,8BAC7CkB,eAAiBhB,KAAKQ,yBAAkB9B,KAAKL,OAAOyB,sCACpDmB,cAAgBjB,KAAKkB,iBAAiB,yEAE5BF,gBAEZH,aACAN,kBAAkBE,UAAUE,OAAOE,aAGvCN,kBAAkBE,UAAUE,OAAO,UACnCG,WAAWL,UAAUE,OAAO,UAC5BI,QAAQN,UAAUC,IAAI,yBACtBE,UAAUO,gBAAkB,kBAAU,aAAc,oBAEhDF,eACAA,cAAcG,SAASC,SACnBA,OAAOC,UAAW,uBAWZtB,KAAMY,iBACdL,kBAAoBP,KAAKQ,yBAAkB9B,KAAKL,OAAOyB,iCACvDgB,WAAad,KAAKQ,yBAAkB9B,KAAKL,OAAOyB,iCAChDiB,QAAUf,KAAKQ,yBAAkB9B,KAAKL,OAAOyB,8BAC7CmB,cAAgBjB,KAAKkB,iBAAiB,4CACxCX,mBACAA,kBAAkBE,UAAUC,IAAI,UAEhCI,YACAA,WAAWL,UAAUC,IAAI,UAEzBK,SACAA,QAAQN,UAAUE,OAAO,yBAE7BC,UAAUO,gBAAkB,kBAAU,aAAc,oBAEhDF,eACAA,cAAcG,SAASC,SACnBA,OAAOC,UAAW"} \ No newline at end of file +{"version":3,"file":"generatebase.min.js","sources":["../src/generatebase.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 * Tiny AI base generate class.\n *\n * @module tiny_aiplacement/generatebase\n * @copyright 2024 Matt Porritt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {loadingMessages} from 'tiny_aiplacement/loading';\nimport {getString} from 'core/str';\nimport {\n getContextId,\n getUserId,\n} from 'tiny_aiplacement/options';\nimport Policy from 'core_ai/policy';\nimport PolicyModal from 'core_ai/policymodal';\nimport CustomEvents from 'core/custom_interaction_events';\nimport {isPolicyAgreed} from './options';\n\nexport default class GenerateBase {\n modalObject;\n\n /**\n * Class constructor.\n *\n * @param {TinyMCE.editor} editor The tinyMCE editor instance.\n */\n constructor(editor) {\n this.editor = editor;\n this.userid = getUserId(editor);\n this.contextid = getContextId(editor);\n this.responseObj = null;\n }\n\n /**\n * Display the modal when the AI button is clicked.\n *\n */\n async displayContentModal() {\n Policy.preconfigurePolicyState(this.userid, isPolicyAgreed(this.editor));\n if (!await Policy.getPolicyStatus(this.userid)) {\n const policyModal = await PolicyModal.create();\n policyModal.getModal().on(CustomEvents.events.activate, policyModal.getActionSelector('save'), () => {\n this.displayContentModal();\n });\n return;\n }\n\n this.modalObject = await this.setupModal();\n }\n\n getModalClass() {\n throw new Error(\"Method 'getModalClass' must be implemented.\");\n }\n\n\n /**\n * Set up the base text generation modal with default body content.\n *\n * @returns {TextModal} The image modal object.\n */\n async setupModal() {\n const modal = this.getModalClass().create({\n templateContext: {\n elementid: this.editor.id,\n },\n });\n\n this.addContentEventListeners(modal);\n\n return modal;\n }\n\n /**\n * Add event listeners for the text modal.\n *\n * @param {Modal} modal\n */\n async addContentEventListeners(modal) {\n const modalRoot = (await modal).getRoot();\n const root = modalRoot[0];\n\n root.addEventListener('click', (e) => {\n this.handleContentModalClick(e, root);\n });\n\n this.setupPromptArea(root);\n this.hideLoadingSpinner(root);\n }\n\n handleContentModalClick() {\n throw new Error('Method handleContentModalClick must be implemented.');\n }\n\n /**\n * Hide the loading spinner.\n *\n * @param {Object} root The root element of the modal.\n */\n hideLoadingSpinner(root) {\n const loadingSpinnerDiv = root.querySelector(`[id=\"${this.editor.id}_tiny_aiplacement_spinner\"]`);\n loadingSpinnerDiv.classList.add('hidden');\n loadingSpinnerDiv.classList.remove('tiny-aiplacement-loading-spinner-container');\n }\n\n /**\n * Display the loading state in the modal.\n *\n * @param {HTMLElement} root - The root element of the modal.\n * @param {HTMLElement} submitBtn - The submit button element.\n * @param {String|null} removeClass - The class to be removed from the loading spinner div, if any.\n */\n async displayLoading(root, submitBtn, removeClass = null) {\n const loadingSpinnerDiv = root.querySelector(`[id=\"${this.editor.id}_tiny_aiplacement_spinner\"]`);\n const overlayDiv = root.querySelector(`[id=\"${this.editor.id}_tiny_aiplacement_overlay\"]`);\n const blurDiv = root.querySelector(`[id=\"${this.editor.id}_tiny_aiplacement_blur\"]`);\n const loadingTextDiv = root.querySelector(`[id=\"${this.editor.id}_tiny_aiplacement_loading_text\"]`);\n const actionButtons = root.querySelectorAll('.tiny-aiplacement-generate-footer button');\n\n loadingMessages(loadingTextDiv);\n\n if (removeClass) {\n loadingSpinnerDiv.classList.remove(removeClass);\n }\n\n loadingSpinnerDiv.classList.remove('hidden');\n overlayDiv.classList.remove('hidden');\n blurDiv.classList.add('tiny-aiplacement-blur');\n submitBtn.innerHTML = await getString('generating', 'tiny_aiplacement');\n\n if (actionButtons) {\n actionButtons.forEach((button) => {\n button.disabled = true;\n });\n }\n }\n\n /**\n * Hide the loading action in the modal.\n *\n * @param {Object} root The root element of the modal.\n * @param {Object} submitBtn The submit button element.\n */\n async hideLoading(root, submitBtn) {\n const loadingSpinnerDiv = root.querySelector(`[id=\"${this.editor.id}_tiny_aiplacement_spinner\"]`);\n const overlayDiv = root.querySelector(`[id=\"${this.editor.id}_tiny_aiplacement_overlay\"]`);\n const blurDiv = root.querySelector(`[id=\"${this.editor.id}_tiny_aiplacement_blur\"]`);\n const actionButtons = root.querySelectorAll('.tiny-aiplacement-generate-footer button');\n if (loadingSpinnerDiv) {\n loadingSpinnerDiv.classList.add('hidden');\n }\n if (overlayDiv) {\n overlayDiv.classList.add('hidden');\n }\n if (blurDiv) {\n blurDiv.classList.remove('tiny-aiplacement-blur');\n }\n submitBtn.innerHTML = await getString('regenerate', 'tiny_aiplacement');\n\n if (actionButtons) {\n actionButtons.forEach((button) => {\n button.disabled = false;\n });\n }\n }\n}\n"],"names":["constructor","editor","userid","contextid","responseObj","preconfigurePolicyState","this","Policy","getPolicyStatus","modalObject","setupModal","policyModal","PolicyModal","create","getModal","on","CustomEvents","events","activate","getActionSelector","displayContentModal","getModalClass","Error","modal","templateContext","elementid","id","addContentEventListeners","root","getRoot","addEventListener","e","handleContentModalClick","setupPromptArea","hideLoadingSpinner","loadingSpinnerDiv","querySelector","classList","add","remove","submitBtn","removeClass","overlayDiv","blurDiv","loadingTextDiv","actionButtons","querySelectorAll","innerHTML","forEach","button","disabled"],"mappings":"qpBA0CIA,YAAYC,kLACHA,OAASA,YACTC,QAAS,sBAAUD,aACnBE,WAAY,yBAAaF,aACzBG,YAAc,oDAQZC,wBAAwBC,KAAKJ,QAAQ,4BAAeI,KAAKL,eACrDM,gBAAOC,gBAAgBF,KAAKJ,aAQlCO,kBAAoBH,KAAKI,wBAPpBC,kBAAoBC,qBAAYC,SACtCF,YAAYG,WAAWC,GAAGC,mCAAaC,OAAOC,SAAUP,YAAYQ,kBAAkB,SAAS,UACtFC,0BAQjBC,sBACU,IAAIC,MAAM,wEAUVC,MAAQjB,KAAKe,gBAAgBR,OAAO,CACtCW,gBAAiB,CACbC,UAAWnB,KAAKL,OAAOyB,kBAI1BC,yBAAyBJ,OAEvBA,qCAQoBA,aAErBK,YADmBL,OAAOM,UACT,GAEvBD,KAAKE,iBAAiB,SAAUC,SACvBC,wBAAwBD,EAAGH,cAG/BK,gBAAgBL,WAChBM,mBAAmBN,MAG5BI,gCACU,IAAIV,MAAM,uDAQpBY,mBAAmBN,YACTO,kBAAoBP,KAAKQ,6BAAsB9B,KAAKL,OAAOyB,mCACjES,kBAAkBE,UAAUC,IAAI,UAChCH,kBAAkBE,UAAUE,OAAO,mEAUlBX,KAAMY,eAAWC,mEAAc,WAC1CN,kBAAoBP,KAAKQ,6BAAsB9B,KAAKL,OAAOyB,mCAC3DgB,WAAad,KAAKQ,6BAAsB9B,KAAKL,OAAOyB,mCACpDiB,QAAUf,KAAKQ,6BAAsB9B,KAAKL,OAAOyB,gCACjDkB,eAAiBhB,KAAKQ,6BAAsB9B,KAAKL,OAAOyB,wCACxDmB,cAAgBjB,KAAKkB,iBAAiB,yEAE5BF,gBAEZH,aACAN,kBAAkBE,UAAUE,OAAOE,aAGvCN,kBAAkBE,UAAUE,OAAO,UACnCG,WAAWL,UAAUE,OAAO,UAC5BI,QAAQN,UAAUC,IAAI,yBACtBE,UAAUO,gBAAkB,kBAAU,aAAc,oBAEhDF,eACAA,cAAcG,SAASC,SACnBA,OAAOC,UAAW,uBAWZtB,KAAMY,iBACdL,kBAAoBP,KAAKQ,6BAAsB9B,KAAKL,OAAOyB,mCAC3DgB,WAAad,KAAKQ,6BAAsB9B,KAAKL,OAAOyB,mCACpDiB,QAAUf,KAAKQ,6BAAsB9B,KAAKL,OAAOyB,gCACjDmB,cAAgBjB,KAAKkB,iBAAiB,4CACxCX,mBACAA,kBAAkBE,UAAUC,IAAI,UAEhCI,YACAA,WAAWL,UAAUC,IAAI,UAEzBK,SACAA,QAAQN,UAAUE,OAAO,yBAE7BC,UAAUO,gBAAkB,kBAAU,aAAc,oBAEhDF,eACAA,cAAcG,SAASC,SACnBA,OAAOC,UAAW"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/aiplacement/amd/build/generateimage.min.js b/lib/editor/tiny/plugins/aiplacement/amd/build/generateimage.min.js index 8fc5c743464..68fae4d05d7 100644 --- a/lib/editor/tiny/plugins/aiplacement/amd/build/generateimage.min.js +++ b/lib/editor/tiny/plugins/aiplacement/amd/build/generateimage.min.js @@ -1,3 +1,3 @@ -define("tiny_aiplacement/generateimage",["exports","tiny_aiplacement/imagemodal","core/ajax","core/str","core/templates","./mediaimage","tiny_aiplacement/options","tiny_aiplacement/generatebase"],(function(_exports,_imagemodal,_ajax,_str,_templates,_mediaimage,_options,_generatebase){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_imagemodal=_interopRequireDefault(_imagemodal),_ajax=_interopRequireDefault(_ajax),_templates=_interopRequireDefault(_templates),_mediaimage=_interopRequireDefault(_mediaimage),_generatebase=_interopRequireDefault(_generatebase);class GenerateImage extends _generatebase.default{constructor(){super(...arguments),_defineProperty(this,"SELECTORS",{GENERATEBUTTON:()=>"#".concat(this.editor.id,"_tiny_aiplacement_generatebutton"),PROMPTAREA:()=>"#".concat(this.editor.id,"_tiny_aiplacement_imageprompt"),IMAGECONTAINER:()=>"#".concat(this.editor.id,"_tiny_aiplacement_generate_image"),GENERATEBTN:'[data-action="generate"]',INSERTBTN:'[data-action="inserter"]',BACKTBTN:'[data-action="back"]',GENERATEDIMAGE:()=>"#".concat(this.editor.id,"_tiny_generated_image")}),_defineProperty(this,"imageURL",null)}getModalClass(){return _imagemodal.default}handleContentModalClick(e,root){const actions={generate:()=>this.handleSubmit(root,e.target),inserter:()=>this.handleInsert(),cancel:()=>this.modalObject.destroy(),back:()=>{this.modalObject.destroy(),this.displayContentModal()}},actionKey=Object.keys(actions).find((key=>e.target.closest('[data-action="'.concat(key,'"]'))));actionKey&&(e.preventDefault(),actions[actionKey]())}setupPromptArea(root){const generateBtn=root.querySelector(this.SELECTORS.GENERATEBUTTON()),promptArea=root.querySelector(this.SELECTORS.PROMPTAREA());promptArea.addEventListener("input",(()=>{generateBtn.disabled=""===promptArea.value.trim()}))}async handleSubmit(root,submitBtn){await this.displayLoading(root,submitBtn);const request={methodname:"aiplacement_editor_generate_image",args:this.getDisplayArgs(root)};try{this.responseObj=await _ajax.default.call([request])[0],this.responseObj.error?this.handleGenerationError(root,submitBtn,""):(await this.displayGeneratedImage(root),this.hideLoading(root,submitBtn),window.console.log(this.responseObj))}catch(error){this.handleGenerationError(root,submitBtn,"")}}async handleInsert(){const mediaImage=new _mediaimage.default(this.editor,this.imageURL,this.promptText);await mediaImage.displayDialogue(),this.modalObject.destroy()}async handleGenerationError(root,submitBtn){let errorMessage=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";errorMessage||(errorMessage=await(0,_str.getString)("errorgeneral","tiny_aiplacement")),this.hideLoading(root,submitBtn),this.modalObject.setBody(_templates.default.render("tiny_aiplacement/modalbodyerror",{errorMessage:errorMessage}));const backBtn=root.querySelector(this.SELECTORS.BACKTBTN),generateBtn=root.querySelector(this.SELECTORS.GENERATEBUTTON());backBtn.classList.remove("hidden"),generateBtn.classList.add("hidden")}async displayGeneratedImage(root){const imageDisplayContainer=root.querySelector(this.SELECTORS.IMAGECONTAINER()),insertBtn=root.querySelector(this.SELECTORS.INSERTBTN);this.imageURL=this.responseObj.drafturl,imageDisplayContainer.innerHTML=await _templates.default.render("tiny_aiplacement/image",{url:this.responseObj.drafturl,elementid:this.editor.id,alt:this.promptText});const imagElement=root.querySelector(this.SELECTORS.GENERATEDIMAGE());return new Promise(((resolve,reject)=>{imagElement.onload=()=>{insertBtn.classList.remove("hidden"),resolve()},imagElement.onerror=error=>{reject(error)}}))}getDisplayArgs(root){const contextId=(0,_options.getContextId)(this.editor),promptText=root.querySelector(this.SELECTORS.PROMPTAREA()).value;this.promptText=promptText;return{contextid:contextId,prompttext:promptText,aspectratio:this.getSelectedRadioValue("aspect-ratio","square"),quality:this.getSelectedRadioValue("quality","standard")?"hd":"standard",numimages:1}}getSelectedRadioValue(radioName){let defaultValue=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;const radios=document.getElementsByName(radioName);for(const radio of radios)if(radio.checked)return radio.value;return defaultValue}}return _exports.default=GenerateImage,_exports.default})); +define("tiny_aiplacement/generateimage",["exports","tiny_aiplacement/imagemodal","core/ajax","core/str","core/templates","./mediaimage","tiny_aiplacement/options","tiny_aiplacement/generatebase"],(function(_exports,_imagemodal,_ajax,_str,_templates,_mediaimage,_options,_generatebase){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_imagemodal=_interopRequireDefault(_imagemodal),_ajax=_interopRequireDefault(_ajax),_templates=_interopRequireDefault(_templates),_mediaimage=_interopRequireDefault(_mediaimage),_generatebase=_interopRequireDefault(_generatebase);class GenerateImage extends _generatebase.default{constructor(){super(...arguments),_defineProperty(this,"SELECTORS",{GENERATEBUTTON:()=>'[id="'.concat(this.editor.id,'_tiny_aiplacement_generatebutton"]'),PROMPTAREA:()=>'[id="'.concat(this.editor.id,'_tiny_aiplacement_imageprompt"]'),IMAGECONTAINER:()=>'[id="'.concat(this.editor.id,'_tiny_aiplacement_generate_image"]'),GENERATEBTN:'[data-action="generate"]',INSERTBTN:'[data-action="inserter"]',BACKTBTN:'[data-action="back"]',GENERATEDIMAGE:()=>'[id="'.concat(this.editor.id,'_tiny_generated_image"]')}),_defineProperty(this,"imageURL",null)}getModalClass(){return _imagemodal.default}handleContentModalClick(e,root){const actions={generate:()=>this.handleSubmit(root,e.target),inserter:()=>this.handleInsert(),cancel:()=>this.modalObject.destroy(),back:()=>{this.modalObject.destroy(),this.displayContentModal()}},actionKey=Object.keys(actions).find((key=>e.target.closest('[data-action="'.concat(key,'"]'))));actionKey&&(e.preventDefault(),actions[actionKey]())}setupPromptArea(root){const generateBtn=root.querySelector(this.SELECTORS.GENERATEBUTTON()),promptArea=root.querySelector(this.SELECTORS.PROMPTAREA());promptArea.addEventListener("input",(()=>{generateBtn.disabled=""===promptArea.value.trim()}))}async handleSubmit(root,submitBtn){await this.displayLoading(root,submitBtn);const request={methodname:"aiplacement_editor_generate_image",args:this.getDisplayArgs(root)};try{this.responseObj=await _ajax.default.call([request])[0],this.responseObj.error?this.handleGenerationError(root,submitBtn,""):(await this.displayGeneratedImage(root),this.hideLoading(root,submitBtn),window.console.log(this.responseObj))}catch(error){this.handleGenerationError(root,submitBtn,"")}}async handleInsert(){const mediaImage=new _mediaimage.default(this.editor,this.imageURL,this.promptText);await mediaImage.displayDialogue(),this.modalObject.destroy()}async handleGenerationError(root,submitBtn){let errorMessage=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";errorMessage||(errorMessage=await(0,_str.getString)("errorgeneral","tiny_aiplacement")),this.hideLoading(root,submitBtn),this.modalObject.setBody(_templates.default.render("tiny_aiplacement/modalbodyerror",{errorMessage:errorMessage}));const backBtn=root.querySelector(this.SELECTORS.BACKTBTN),generateBtn=root.querySelector(this.SELECTORS.GENERATEBUTTON());backBtn.classList.remove("hidden"),generateBtn.classList.add("hidden")}async displayGeneratedImage(root){const imageDisplayContainer=root.querySelector(this.SELECTORS.IMAGECONTAINER()),insertBtn=root.querySelector(this.SELECTORS.INSERTBTN);this.imageURL=this.responseObj.drafturl,imageDisplayContainer.innerHTML=await _templates.default.render("tiny_aiplacement/image",{url:this.responseObj.drafturl,elementid:this.editor.id,alt:this.promptText});const imagElement=root.querySelector(this.SELECTORS.GENERATEDIMAGE());return new Promise(((resolve,reject)=>{imagElement.onload=()=>{insertBtn.classList.remove("hidden"),resolve()},imagElement.onerror=error=>{reject(error)}}))}getDisplayArgs(root){const contextId=(0,_options.getContextId)(this.editor),promptText=root.querySelector(this.SELECTORS.PROMPTAREA()).value;this.promptText=promptText;return{contextid:contextId,prompttext:promptText,aspectratio:this.getSelectedRadioValue("aspect-ratio","square"),quality:this.getSelectedRadioValue("quality","standard")?"hd":"standard",numimages:1}}getSelectedRadioValue(radioName){let defaultValue=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;const radios=document.getElementsByName(radioName);for(const radio of radios)if(radio.checked)return radio.value;return defaultValue}}return _exports.default=GenerateImage,_exports.default})); //# sourceMappingURL=generateimage.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/aiplacement/amd/build/generateimage.min.js.map b/lib/editor/tiny/plugins/aiplacement/amd/build/generateimage.min.js.map index 62d988304b9..ce4c7404007 100644 --- a/lib/editor/tiny/plugins/aiplacement/amd/build/generateimage.min.js.map +++ b/lib/editor/tiny/plugins/aiplacement/amd/build/generateimage.min.js.map @@ -1 +1 @@ -{"version":3,"file":"generateimage.min.js","sources":["../src/generateimage.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 * Tiny AI generate images.\n *\n * @module tiny_aiplacement/generateimage\n * @copyright 2024 Matt Porritt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ImageModal from 'tiny_aiplacement/imagemodal';\nimport Ajax from 'core/ajax';\nimport {getString} from 'core/str';\nimport Templates from 'core/templates';\nimport AiMediaImage from './mediaimage';\nimport {getContextId} from 'tiny_aiplacement/options';\nimport GenerateBase from 'tiny_aiplacement/generatebase';\n\nexport default class GenerateImage extends GenerateBase {\n SELECTORS = {\n GENERATEBUTTON: () => `#${this.editor.id}_tiny_aiplacement_generatebutton`,\n PROMPTAREA: () => `#${this.editor.id}_tiny_aiplacement_imageprompt`,\n IMAGECONTAINER: () => `#${this.editor.id}_tiny_aiplacement_generate_image`,\n GENERATEBTN: '[data-action=\"generate\"]',\n INSERTBTN: '[data-action=\"inserter\"]',\n BACKTBTN: '[data-action=\"back\"]',\n GENERATEDIMAGE: () => `#${this.editor.id}_tiny_generated_image`,\n };\n\n imageURL = null;\n\n getModalClass() {\n return ImageModal;\n }\n\n /**\n * Handle click events within the image modal.\n *\n * @param {Event} e - The click event object.\n * @param {HTMLElement} root - The root element of the modal.\n */\n handleContentModalClick(e, root) {\n const actions = {\n generate: () => this.handleSubmit(root, e.target),\n inserter: () => this.handleInsert(),\n cancel: () => this.modalObject.destroy(),\n back: () => {\n this.modalObject.destroy();\n this.displayContentModal();\n },\n };\n\n const actionKey = Object.keys(actions).find(key => e.target.closest(`[data-action=\"${key}\"]`));\n if (actionKey) {\n e.preventDefault();\n actions[actionKey]();\n }\n }\n\n /**\n * Set up the prompt area in the modal, adding necessary event listeners.\n *\n * @param {HTMLElement} root - The root element of the modal.\n */\n setupPromptArea(root) {\n const generateBtn = root.querySelector(this.SELECTORS.GENERATEBUTTON());\n const promptArea = root.querySelector(this.SELECTORS.PROMPTAREA());\n\n promptArea.addEventListener('input', () => {\n generateBtn.disabled = promptArea.value.trim() === '';\n });\n }\n\n /**\n * Handle the submit action.\n *\n * @param {Object} root The root element of the modal.\n * @param {Object} submitBtn The submit button element.\n */\n async handleSubmit(root, submitBtn) {\n await this.displayLoading(root, submitBtn);\n\n const displayArgs = this.getDisplayArgs(root);\n const request = {\n methodname: 'aiplacement_editor_generate_image',\n args: displayArgs\n };\n\n try {\n this.responseObj = await Ajax.call([request])[0];\n if (this.responseObj.error) {\n this.handleGenerationError(root, submitBtn, '');\n } else {\n await this.displayGeneratedImage(root);\n this.hideLoading(root, submitBtn);\n window.console.log(this.responseObj);\n }\n } catch (error) {\n this.handleGenerationError(root, submitBtn, '');\n }\n }\n\n /**\n * Handle the insert action.\n *\n */\n async handleInsert() {\n const mediaImage = new AiMediaImage(this.editor, this.imageURL, this.promptText);\n await mediaImage.displayDialogue();\n this.modalObject.destroy();\n }\n\n /**\n * Handle a generation error.\n *\n * @param {Object} root The root element of the modal.\n * @param {Object} submitBtn The submit button element.\n * @param {String} errorMessage The error message to display.\n */\n async handleGenerationError(root, submitBtn, errorMessage = '') {\n if (!errorMessage) {\n // Get the default error message.\n errorMessage = await getString('errorgeneral', 'tiny_aiplacement');\n }\n this.hideLoading(root, submitBtn);\n this.modalObject.setBody(Templates.render('tiny_aiplacement/modalbodyerror', {'errorMessage': errorMessage}));\n const backBtn = root.querySelector(this.SELECTORS.BACKTBTN);\n const generateBtn = root.querySelector(this.SELECTORS.GENERATEBUTTON());\n backBtn.classList.remove('hidden');\n generateBtn.classList.add('hidden');\n }\n\n /**\n * Display the generated image in the modal.\n *\n * @param {HTMLElement} root - The root element of the modal.\n */\n async displayGeneratedImage(root) {\n const imageDisplayContainer = root.querySelector(this.SELECTORS.IMAGECONTAINER());\n const insertBtn = root.querySelector(this.SELECTORS.INSERTBTN);\n // Set the draft URL as it's used elsewhere.\n this.imageURL = this.responseObj.drafturl;\n\n // Render the image template and insert it into the modal.\n imageDisplayContainer.innerHTML = await Templates.render('tiny_aiplacement/image', {\n url: this.responseObj.drafturl,\n elementid: this.editor.id,\n alt: this.promptText,\n });\n const imagElement = root.querySelector(this.SELECTORS.GENERATEDIMAGE());\n\n return new Promise((resolve, reject) => {\n imagElement.onload = () => {\n insertBtn.classList.remove('hidden');\n resolve(); // Resolve the promise when the image is loaded.\n };\n imagElement.onerror = (error) => {\n reject(error); // Reject the promise if there is an error loading the image.\n };\n });\n }\n\n /**\n * Get the display args for the image.\n *\n * @param {Object} root The root element of the modal.\n */\n getDisplayArgs(root) {\n const contextId = getContextId(this.editor);\n const promptText = root.querySelector(this.SELECTORS.PROMPTAREA()).value;\n this.promptText = promptText;\n\n const aspectRatio = this.getSelectedRadioValue('aspect-ratio', 'square');\n const imageQuality = this.getSelectedRadioValue('quality', 'standard');\n\n return {\n contextid: contextId,\n prompttext: promptText,\n aspectratio: aspectRatio,\n quality: imageQuality ? 'hd' : 'standard',\n numimages: 1\n };\n }\n\n /**\n * Get the value of the selected radio button.\n *\n * @param {String} radioName The name of the radio button group.\n * @param {String} defaultValue The default value of the radio button.\n */\n getSelectedRadioValue(radioName, defaultValue = null) {\n const radios = document.getElementsByName(radioName);\n for (const radio of radios) {\n if (radio.checked) {\n return radio.value;\n }\n }\n return defaultValue;\n }\n}\n"],"names":["GenerateImage","GenerateBase","GENERATEBUTTON","this","editor","id","PROMPTAREA","IMAGECONTAINER","GENERATEBTN","INSERTBTN","BACKTBTN","GENERATEDIMAGE","getModalClass","ImageModal","handleContentModalClick","e","root","actions","generate","handleSubmit","target","inserter","handleInsert","cancel","modalObject","destroy","back","displayContentModal","actionKey","Object","keys","find","key","closest","preventDefault","setupPromptArea","generateBtn","querySelector","SELECTORS","promptArea","addEventListener","disabled","value","trim","submitBtn","displayLoading","request","methodname","args","getDisplayArgs","responseObj","Ajax","call","error","handleGenerationError","displayGeneratedImage","hideLoading","window","console","log","mediaImage","AiMediaImage","imageURL","promptText","displayDialogue","errorMessage","setBody","Templates","render","backBtn","classList","remove","add","imageDisplayContainer","insertBtn","drafturl","innerHTML","url","elementid","alt","imagElement","Promise","resolve","reject","onload","onerror","contextId","contextid","prompttext","aspectratio","getSelectedRadioValue","quality","numimages","radioName","defaultValue","radios","document","getElementsByName","radio","checked"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"generateimage.min.js","sources":["../src/generateimage.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 * Tiny AI generate images.\n *\n * @module tiny_aiplacement/generateimage\n * @copyright 2024 Matt Porritt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ImageModal from 'tiny_aiplacement/imagemodal';\nimport Ajax from 'core/ajax';\nimport {getString} from 'core/str';\nimport Templates from 'core/templates';\nimport AiMediaImage from './mediaimage';\nimport {getContextId} from 'tiny_aiplacement/options';\nimport GenerateBase from 'tiny_aiplacement/generatebase';\n\nexport default class GenerateImage extends GenerateBase {\n SELECTORS = {\n GENERATEBUTTON: () => `[id=\"${this.editor.id}_tiny_aiplacement_generatebutton\"]`,\n PROMPTAREA: () => `[id=\"${this.editor.id}_tiny_aiplacement_imageprompt\"]`,\n IMAGECONTAINER: () => `[id=\"${this.editor.id}_tiny_aiplacement_generate_image\"]`,\n GENERATEBTN: '[data-action=\"generate\"]',\n INSERTBTN: '[data-action=\"inserter\"]',\n BACKTBTN: '[data-action=\"back\"]',\n GENERATEDIMAGE: () => `[id=\"${this.editor.id}_tiny_generated_image\"]`,\n };\n\n imageURL = null;\n\n getModalClass() {\n return ImageModal;\n }\n\n /**\n * Handle click events within the image modal.\n *\n * @param {Event} e - The click event object.\n * @param {HTMLElement} root - The root element of the modal.\n */\n handleContentModalClick(e, root) {\n const actions = {\n generate: () => this.handleSubmit(root, e.target),\n inserter: () => this.handleInsert(),\n cancel: () => this.modalObject.destroy(),\n back: () => {\n this.modalObject.destroy();\n this.displayContentModal();\n },\n };\n\n const actionKey = Object.keys(actions).find(key => e.target.closest(`[data-action=\"${key}\"]`));\n if (actionKey) {\n e.preventDefault();\n actions[actionKey]();\n }\n }\n\n /**\n * Set up the prompt area in the modal, adding necessary event listeners.\n *\n * @param {HTMLElement} root - The root element of the modal.\n */\n setupPromptArea(root) {\n const generateBtn = root.querySelector(this.SELECTORS.GENERATEBUTTON());\n const promptArea = root.querySelector(this.SELECTORS.PROMPTAREA());\n\n promptArea.addEventListener('input', () => {\n generateBtn.disabled = promptArea.value.trim() === '';\n });\n }\n\n /**\n * Handle the submit action.\n *\n * @param {Object} root The root element of the modal.\n * @param {Object} submitBtn The submit button element.\n */\n async handleSubmit(root, submitBtn) {\n await this.displayLoading(root, submitBtn);\n\n const displayArgs = this.getDisplayArgs(root);\n const request = {\n methodname: 'aiplacement_editor_generate_image',\n args: displayArgs\n };\n\n try {\n this.responseObj = await Ajax.call([request])[0];\n if (this.responseObj.error) {\n this.handleGenerationError(root, submitBtn, '');\n } else {\n await this.displayGeneratedImage(root);\n this.hideLoading(root, submitBtn);\n window.console.log(this.responseObj);\n }\n } catch (error) {\n this.handleGenerationError(root, submitBtn, '');\n }\n }\n\n /**\n * Handle the insert action.\n *\n */\n async handleInsert() {\n const mediaImage = new AiMediaImage(this.editor, this.imageURL, this.promptText);\n await mediaImage.displayDialogue();\n this.modalObject.destroy();\n }\n\n /**\n * Handle a generation error.\n *\n * @param {Object} root The root element of the modal.\n * @param {Object} submitBtn The submit button element.\n * @param {String} errorMessage The error message to display.\n */\n async handleGenerationError(root, submitBtn, errorMessage = '') {\n if (!errorMessage) {\n // Get the default error message.\n errorMessage = await getString('errorgeneral', 'tiny_aiplacement');\n }\n this.hideLoading(root, submitBtn);\n this.modalObject.setBody(Templates.render('tiny_aiplacement/modalbodyerror', {'errorMessage': errorMessage}));\n const backBtn = root.querySelector(this.SELECTORS.BACKTBTN);\n const generateBtn = root.querySelector(this.SELECTORS.GENERATEBUTTON());\n backBtn.classList.remove('hidden');\n generateBtn.classList.add('hidden');\n }\n\n /**\n * Display the generated image in the modal.\n *\n * @param {HTMLElement} root - The root element of the modal.\n */\n async displayGeneratedImage(root) {\n const imageDisplayContainer = root.querySelector(this.SELECTORS.IMAGECONTAINER());\n const insertBtn = root.querySelector(this.SELECTORS.INSERTBTN);\n // Set the draft URL as it's used elsewhere.\n this.imageURL = this.responseObj.drafturl;\n\n // Render the image template and insert it into the modal.\n imageDisplayContainer.innerHTML = await Templates.render('tiny_aiplacement/image', {\n url: this.responseObj.drafturl,\n elementid: this.editor.id,\n alt: this.promptText,\n });\n const imagElement = root.querySelector(this.SELECTORS.GENERATEDIMAGE());\n\n return new Promise((resolve, reject) => {\n imagElement.onload = () => {\n insertBtn.classList.remove('hidden');\n resolve(); // Resolve the promise when the image is loaded.\n };\n imagElement.onerror = (error) => {\n reject(error); // Reject the promise if there is an error loading the image.\n };\n });\n }\n\n /**\n * Get the display args for the image.\n *\n * @param {Object} root The root element of the modal.\n */\n getDisplayArgs(root) {\n const contextId = getContextId(this.editor);\n const promptText = root.querySelector(this.SELECTORS.PROMPTAREA()).value;\n this.promptText = promptText;\n\n const aspectRatio = this.getSelectedRadioValue('aspect-ratio', 'square');\n const imageQuality = this.getSelectedRadioValue('quality', 'standard');\n\n return {\n contextid: contextId,\n prompttext: promptText,\n aspectratio: aspectRatio,\n quality: imageQuality ? 'hd' : 'standard',\n numimages: 1\n };\n }\n\n /**\n * Get the value of the selected radio button.\n *\n * @param {String} radioName The name of the radio button group.\n * @param {String} defaultValue The default value of the radio button.\n */\n getSelectedRadioValue(radioName, defaultValue = null) {\n const radios = document.getElementsByName(radioName);\n for (const radio of radios) {\n if (radio.checked) {\n return radio.value;\n }\n }\n return defaultValue;\n }\n}\n"],"names":["GenerateImage","GenerateBase","GENERATEBUTTON","this","editor","id","PROMPTAREA","IMAGECONTAINER","GENERATEBTN","INSERTBTN","BACKTBTN","GENERATEDIMAGE","getModalClass","ImageModal","handleContentModalClick","e","root","actions","generate","handleSubmit","target","inserter","handleInsert","cancel","modalObject","destroy","back","displayContentModal","actionKey","Object","keys","find","key","closest","preventDefault","setupPromptArea","generateBtn","querySelector","SELECTORS","promptArea","addEventListener","disabled","value","trim","submitBtn","displayLoading","request","methodname","args","getDisplayArgs","responseObj","Ajax","call","error","handleGenerationError","displayGeneratedImage","hideLoading","window","console","log","mediaImage","AiMediaImage","imageURL","promptText","displayDialogue","errorMessage","setBody","Templates","render","backBtn","classList","remove","add","imageDisplayContainer","insertBtn","drafturl","innerHTML","url","elementid","alt","imagElement","Promise","resolve","reject","onload","onerror","contextId","contextid","prompttext","aspectratio","getSelectedRadioValue","quality","numimages","radioName","defaultValue","radios","document","getElementsByName","radio","checked"],"mappings":"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"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/aiplacement/amd/build/generatetext.min.js b/lib/editor/tiny/plugins/aiplacement/amd/build/generatetext.min.js index 91ea47a9625..f99cb03fbe7 100644 --- a/lib/editor/tiny/plugins/aiplacement/amd/build/generatetext.min.js +++ b/lib/editor/tiny/plugins/aiplacement/amd/build/generatetext.min.js @@ -1,3 +1,3 @@ -define("tiny_aiplacement/generatetext",["exports","./textmodal","core/ajax","core/str","core/templates","./options","./textmark","./generatebase"],(function(_exports,_textmodal,_ajax,_str,_templates,_options,_textmark,_generatebase){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_textmodal=_interopRequireDefault(_textmodal),_ajax=_interopRequireDefault(_ajax),_templates=_interopRequireDefault(_templates),_textmark=_interopRequireDefault(_textmark),_generatebase=_interopRequireDefault(_generatebase);class GenerateText extends _generatebase.default{constructor(){var obj,key,value;super(...arguments),value={GENERATEBUTTON:()=>"#".concat(this.editor.id,"_tiny_aiplacement_generatebutton"),PROMPTAREA:()=>"#".concat(this.editor.id,"_tiny_aiplacement_textprompt"),RESPONSEWRAPPER:".tiny_aiplacement_textresponse",RESPONSEPLACEHOLDER:".tiny_aiplacement_textresponse_placeholder",GENERATEDRESPONSE:()=>"#".concat(this.editor.id,"_tiny_aiplacement_textresponse"),INSERTBTN:'[data-action="inserter"]',BACKTBTN:'[data-action="back"]'},(key="SELECTORS")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}getModalClass(){return _textmodal.default}handleContentModalClick(e,root){const actions={generate:()=>this.handleSubmit(root,e.target),inserter:()=>this.handleInsert(root,e.target),cancel:()=>this.modalObject.destroy(),back:()=>{this.modalObject.destroy(),this.displayContentModal()}},actionKey=Object.keys(actions).find((key=>e.target.closest('[data-action="'.concat(key,'"]'))));actionKey&&(e.preventDefault(),actions[actionKey]())}setupPromptArea(root){const generateBtn=root.querySelector(this.SELECTORS.GENERATEBUTTON()),promptArea=root.querySelector(this.SELECTORS.PROMPTAREA());promptArea.addEventListener("input",(()=>{generateBtn.disabled=""===promptArea.value.trim()}))}async handleSubmit(root,submitBtn){await this.displayLoading(root,submitBtn);const request={methodname:"aiplacement_editor_generate_text",args:this.getRequestArgs(root)};try{this.responseObj=await _ajax.default.call([request])[0],this.responseObj.error?this.handleGenerationError(root,submitBtn,""):(await this.displayGeneratedText(root),this.hideLoading(root,submitBtn))}catch(error){this.handleGenerationError(root,submitBtn,"")}}async handleInsert(root,submitBtn){await this.displayLoading(root,submitBtn);const generatedResponseDiv=root.querySelector(this.SELECTORS.GENERATEDRESPONSE()),wrappedEditedResponse=await _textmark.default.wrapEditedSections(this.responseObj.generatedcontent,generatedResponseDiv.value);this.responseObj.editedtext=this.replaceLineBreaks(wrappedEditedResponse);const formattedResponse=await _templates.default.render("tiny_aiplacement/textinsert",this.responseObj);this.editor.insertContent(formattedResponse),this.editor.execCommand("mceRepaint"),this.editor.windowManager.close(),this.modalObject.hide()}async handleGenerationError(root,submitBtn){let errorMessage=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";errorMessage||(errorMessage=await(0,_str.getString)("errorgeneral","tiny_aiplacement")),this.modalObject.setBody(_templates.default.render("tiny_aiplacement/modalbodyerror",{errorMessage:errorMessage}));const backBtn=root.querySelector(this.SELECTORS.BACKTBTN),generateBtn=root.querySelector(this.SELECTORS.GENERATEBUTTON());backBtn.classList.remove("hidden"),generateBtn.classList.add("hidden"),this.hideLoading(root,submitBtn)}async displayGeneratedText(root){root.querySelector(this.SELECTORS.INSERTBTN).classList.remove("hidden");root.querySelector(this.SELECTORS.GENERATEDRESPONSE()).value=this.responseObj.generatedcontent;root.querySelector(this.SELECTORS.RESPONSEWRAPPER).classList.remove("hidden");root.querySelector(this.SELECTORS.RESPONSEPLACEHOLDER).classList.add("hidden")}getRequestArgs(root){return{contextid:(0,_options.getContextId)(this.editor),prompttext:root.querySelector(this.SELECTORS.PROMPTAREA()).value}}replaceLineBreaks(text){const textWithBreaks=text.replace(/\n{2,}|\r\n/g,"

").replace(/\n/g,"
");return"

".concat(textWithBreaks,"

")}}return _exports.default=GenerateText,_exports.default})); +define("tiny_aiplacement/generatetext",["exports","./textmodal","core/ajax","core/str","core/templates","./options","./textmark","./generatebase"],(function(_exports,_textmodal,_ajax,_str,_templates,_options,_textmark,_generatebase){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_textmodal=_interopRequireDefault(_textmodal),_ajax=_interopRequireDefault(_ajax),_templates=_interopRequireDefault(_templates),_textmark=_interopRequireDefault(_textmark),_generatebase=_interopRequireDefault(_generatebase);class GenerateText extends _generatebase.default{constructor(){var obj,key,value;super(...arguments),value={GENERATEBUTTON:()=>'[id="'.concat(this.editor.id,'_tiny_aiplacement_generatebutton"]'),PROMPTAREA:()=>'[id="'.concat(this.editor.id,'_tiny_aiplacement_textprompt"]'),RESPONSEWRAPPER:".tiny_aiplacement_textresponse",RESPONSEPLACEHOLDER:".tiny_aiplacement_textresponse_placeholder",GENERATEDRESPONSE:()=>'[id="'.concat(this.editor.id,'_tiny_aiplacement_textresponse"]'),INSERTBTN:'[data-action="inserter"]',BACKTBTN:'[data-action="back"]'},(key="SELECTORS")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}getModalClass(){return _textmodal.default}handleContentModalClick(e,root){const actions={generate:()=>this.handleSubmit(root,e.target),inserter:()=>this.handleInsert(root,e.target),cancel:()=>this.modalObject.destroy(),back:()=>{this.modalObject.destroy(),this.displayContentModal()}},actionKey=Object.keys(actions).find((key=>e.target.closest('[data-action="'.concat(key,'"]'))));actionKey&&(e.preventDefault(),actions[actionKey]())}setupPromptArea(root){const generateBtn=root.querySelector(this.SELECTORS.GENERATEBUTTON()),promptArea=root.querySelector(this.SELECTORS.PROMPTAREA());promptArea.addEventListener("input",(()=>{generateBtn.disabled=""===promptArea.value.trim()}))}async handleSubmit(root,submitBtn){await this.displayLoading(root,submitBtn);const request={methodname:"aiplacement_editor_generate_text",args:this.getRequestArgs(root)};try{this.responseObj=await _ajax.default.call([request])[0],this.responseObj.error?this.handleGenerationError(root,submitBtn,""):(await this.displayGeneratedText(root),this.hideLoading(root,submitBtn))}catch(error){this.handleGenerationError(root,submitBtn,"")}}async handleInsert(root,submitBtn){await this.displayLoading(root,submitBtn);const generatedResponseDiv=root.querySelector(this.SELECTORS.GENERATEDRESPONSE()),wrappedEditedResponse=await _textmark.default.wrapEditedSections(this.responseObj.generatedcontent,generatedResponseDiv.value);this.responseObj.editedtext=this.replaceLineBreaks(wrappedEditedResponse);const formattedResponse=await _templates.default.render("tiny_aiplacement/textinsert",this.responseObj);this.editor.insertContent(formattedResponse),this.editor.execCommand("mceRepaint"),this.editor.windowManager.close(),this.modalObject.hide()}async handleGenerationError(root,submitBtn){let errorMessage=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";errorMessage||(errorMessage=await(0,_str.getString)("errorgeneral","tiny_aiplacement")),this.modalObject.setBody(_templates.default.render("tiny_aiplacement/modalbodyerror",{errorMessage:errorMessage}));const backBtn=root.querySelector(this.SELECTORS.BACKTBTN),generateBtn=root.querySelector(this.SELECTORS.GENERATEBUTTON());backBtn.classList.remove("hidden"),generateBtn.classList.add("hidden"),this.hideLoading(root,submitBtn)}async displayGeneratedText(root){root.querySelector(this.SELECTORS.INSERTBTN).classList.remove("hidden");root.querySelector(this.SELECTORS.GENERATEDRESPONSE()).value=this.responseObj.generatedcontent;root.querySelector(this.SELECTORS.RESPONSEWRAPPER).classList.remove("hidden");root.querySelector(this.SELECTORS.RESPONSEPLACEHOLDER).classList.add("hidden")}getRequestArgs(root){return{contextid:(0,_options.getContextId)(this.editor),prompttext:root.querySelector(this.SELECTORS.PROMPTAREA()).value}}replaceLineBreaks(text){const textWithBreaks=text.replace(/\n{2,}|\r\n/g,"

").replace(/\n/g,"
");return"

".concat(textWithBreaks,"

")}}return _exports.default=GenerateText,_exports.default})); //# sourceMappingURL=generatetext.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/aiplacement/amd/build/generatetext.min.js.map b/lib/editor/tiny/plugins/aiplacement/amd/build/generatetext.min.js.map index c5449209513..59729634072 100644 --- a/lib/editor/tiny/plugins/aiplacement/amd/build/generatetext.min.js.map +++ b/lib/editor/tiny/plugins/aiplacement/amd/build/generatetext.min.js.map @@ -1 +1 @@ -{"version":3,"file":"generatetext.min.js","sources":["../src/generatetext.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 * Tiny AI generate text.\n *\n * @module tiny_aiplacement/generatetext\n * @copyright 2024 Matt Porritt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport TextModal from './textmodal';\nimport Ajax from 'core/ajax';\nimport {getString} from 'core/str';\nimport Templates from 'core/templates';\nimport {getContextId} from './options';\nimport TinyAiTextMarker from './textmark';\nimport GenerateBase from './generatebase';\n\nexport default class GenerateText extends GenerateBase {\n SELECTORS = {\n GENERATEBUTTON: () => `#${this.editor.id}_tiny_aiplacement_generatebutton`,\n PROMPTAREA: () => `#${this.editor.id}_tiny_aiplacement_textprompt`,\n RESPONSEWRAPPER: '.tiny_aiplacement_textresponse',\n RESPONSEPLACEHOLDER: '.tiny_aiplacement_textresponse_placeholder',\n GENERATEDRESPONSE: () => `#${this.editor.id}_tiny_aiplacement_textresponse`,\n INSERTBTN: '[data-action=\"inserter\"]',\n BACKTBTN: '[data-action=\"back\"]',\n };\n\n getModalClass() {\n return TextModal;\n }\n\n /**\n * Handle click events within the text modal.\n *\n * @param {Event} e - The click event object.\n * @param {HTMLElement} root - The root element of the modal.\n */\n handleContentModalClick(e, root) {\n const actions = {\n generate: () => this.handleSubmit(root, e.target),\n inserter: () => this.handleInsert(root, e.target),\n cancel: () => this.modalObject.destroy(),\n back: () => {\n this.modalObject.destroy();\n this.displayContentModal();\n },\n };\n\n const actionKey = Object.keys(actions).find(key => e.target.closest(`[data-action=\"${key}\"]`));\n if (actionKey) {\n e.preventDefault();\n actions[actionKey]();\n }\n }\n\n /**\n * Set up the prompt area in the modal, adding necessary event listeners.\n *\n * @param {HTMLElement} root - The root element of the modal.\n */\n setupPromptArea(root) {\n const generateBtn = root.querySelector(this.SELECTORS.GENERATEBUTTON());\n const promptArea = root.querySelector(this.SELECTORS.PROMPTAREA());\n\n promptArea.addEventListener('input', () => {\n generateBtn.disabled = promptArea.value.trim() === '';\n });\n }\n\n /**\n * Handle the submit action.\n *\n * @param {Object} root The root element of the modal.\n * @param {Object} submitBtn The submit button element.\n */\n async handleSubmit(root, submitBtn) {\n await this.displayLoading(root, submitBtn);\n\n const requestArgs = this.getRequestArgs(root);\n const request = {\n methodname: 'aiplacement_editor_generate_text',\n args: requestArgs\n };\n\n try {\n this.responseObj = await Ajax.call([request])[0];\n if (this.responseObj.error) {\n this.handleGenerationError(root, submitBtn, '');\n } else {\n await this.displayGeneratedText(root);\n this.hideLoading(root, submitBtn);\n }\n } catch (error) {\n this.handleGenerationError(root, submitBtn, '');\n }\n }\n\n /**\n * Handle the insert action.\n *\n * @param {Object} root The root element of the modal.\n * @param {HTMLElement} submitBtn - The submit button element.\n */\n async handleInsert(root, submitBtn) {\n await this.displayLoading(root, submitBtn);\n\n // Update the generated response with the content from the form.\n // In case the user has edited the response.\n const generatedResponseDiv = root.querySelector(this.SELECTORS.GENERATEDRESPONSE());\n\n // Wrap the edited sections in the response with tags.\n // This is so we can differentiate between the edited sections and the generated content.\n const wrappedEditedResponse = await TinyAiTextMarker.wrapEditedSections(\n this.responseObj.generatedcontent,\n generatedResponseDiv.value)\n ;\n\n // Replace double line breaks with
and with

for paragraphs.\n this.responseObj.editedtext = this.replaceLineBreaks(wrappedEditedResponse);\n\n // Generate the HTML for the response.\n const formattedResponse = await Templates.render('tiny_aiplacement/textinsert', this.responseObj);\n\n // Insert the response into the editor.\n this.editor.insertContent(formattedResponse);\n this.editor.execCommand('mceRepaint');\n this.editor.windowManager.close();\n\n // Close the modal and return to the editor.\n this.modalObject.hide();\n }\n\n /**\n * Handle a generation error.\n *\n * @param {Object} root The root element of the modal.\n * @param {Object} submitBtn The submit button element.\n * @param {String} errorMessage The error message to display.\n */\n async handleGenerationError(root, submitBtn, errorMessage = '') {\n if (!errorMessage) {\n // Get the default error message.\n errorMessage = await getString('errorgeneral', 'tiny_aiplacement');\n }\n this.modalObject.setBody(Templates.render('tiny_aiplacement/modalbodyerror', {'errorMessage': errorMessage}));\n const backBtn = root.querySelector(this.SELECTORS.BACKTBTN);\n const generateBtn = root.querySelector(this.SELECTORS.GENERATEBUTTON());\n backBtn.classList.remove('hidden');\n generateBtn.classList.add('hidden');\n this.hideLoading(root, submitBtn);\n }\n\n /**\n * Display the generated image in the modal.\n *\n * @param {HTMLElement} root - The root element of the modal.\n */\n async displayGeneratedText(root) {\n const insertBtn = root.querySelector(this.SELECTORS.INSERTBTN);\n insertBtn.classList.remove('hidden');\n\n // Add generated text to the modal.\n const generatedResponseDiv = root.querySelector(this.SELECTORS.GENERATEDRESPONSE());\n generatedResponseDiv.value = this.responseObj.generatedcontent;\n const responseWrapper = root.querySelector(this.SELECTORS.RESPONSEWRAPPER);\n responseWrapper.classList.remove('hidden');\n const responsePlaceholder = root.querySelector(this.SELECTORS.RESPONSEPLACEHOLDER);\n responsePlaceholder.classList.add('hidden');\n }\n\n /**\n * Get the request args for the generated text.\n *\n * @param {Object} root The root element of the modal.\n */\n getRequestArgs(root) {\n const contextId = getContextId(this.editor);\n const promptText = root.querySelector(this.SELECTORS.PROMPTAREA()).value;\n\n return {\n contextid: contextId,\n prompttext: promptText\n };\n }\n\n /**\n * Replace double line breaks with
and with

for paragraphs.\n * This is to handle the difference in response from the AI to what is expected by the editor.\n *\n * @param {String} text The text to replace.\n * @returns {String}\n */\n replaceLineBreaks(text) {\n // Replace double line breaks with

for paragraphs\n const textWithParagraphs = text.replace(/\\n{2,}|\\r\\n/g, '

');\n\n // Replace remaining single line breaks with
tags\n const textWithBreaks = textWithParagraphs.replace(/\\n/g, '
');\n\n // Add opening and closing

tags to wrap the entire content\n return `

${textWithBreaks}

`;\n }\n}\n"],"names":["GenerateText","GenerateBase","GENERATEBUTTON","this","editor","id","PROMPTAREA","RESPONSEWRAPPER","RESPONSEPLACEHOLDER","GENERATEDRESPONSE","INSERTBTN","BACKTBTN","getModalClass","TextModal","handleContentModalClick","e","root","actions","generate","handleSubmit","target","inserter","handleInsert","cancel","modalObject","destroy","back","displayContentModal","actionKey","Object","keys","find","key","closest","preventDefault","setupPromptArea","generateBtn","querySelector","SELECTORS","promptArea","addEventListener","disabled","value","trim","submitBtn","displayLoading","request","methodname","args","getRequestArgs","responseObj","Ajax","call","error","handleGenerationError","displayGeneratedText","hideLoading","generatedResponseDiv","wrappedEditedResponse","TinyAiTextMarker","wrapEditedSections","generatedcontent","editedtext","replaceLineBreaks","formattedResponse","Templates","render","insertContent","execCommand","windowManager","close","hide","errorMessage","setBody","backBtn","classList","remove","add","contextid","prompttext","text","textWithBreaks","replace"],"mappings":"inBA+BqBA,qBAAqBC,gFAC1B,CACRC,eAAgB,eAAUC,KAAKC,OAAOC,uCACtCC,WAAY,eAAUH,KAAKC,OAAOC,mCAClCE,gBAAiB,iCACjBC,oBAAqB,6CACrBC,kBAAmB,eAAUN,KAAKC,OAAOC,qCACzCK,UAAW,2BACXC,SAAU,4JAGdC,uBACWC,mBASXC,wBAAwBC,EAAGC,YACjBC,QAAU,CACZC,SAAU,IAAMf,KAAKgB,aAAaH,KAAMD,EAAEK,QAC1CC,SAAU,IAAMlB,KAAKmB,aAAaN,KAAMD,EAAEK,QAC1CG,OAAQ,IAAMpB,KAAKqB,YAAYC,UAC/BC,KAAM,UACGF,YAAYC,eACZE,wBAIPC,UAAYC,OAAOC,KAAKb,SAASc,MAAKC,KAAOjB,EAAEK,OAAOa,gCAAyBD,aACjFJ,YACAb,EAAEmB,iBACFjB,QAAQW,cAShBO,gBAAgBnB,YACNoB,YAAcpB,KAAKqB,cAAclC,KAAKmC,UAAUpC,kBAChDqC,WAAavB,KAAKqB,cAAclC,KAAKmC,UAAUhC,cAErDiC,WAAWC,iBAAiB,SAAS,KACjCJ,YAAYK,SAAuC,KAA5BF,WAAWG,MAAMC,6BAU7B3B,KAAM4B,iBACfzC,KAAK0C,eAAe7B,KAAM4B,iBAG1BE,QAAU,CACZC,WAAY,mCACZC,KAHgB7C,KAAK8C,eAAejC,gBAO/BkC,kBAAoBC,cAAKC,KAAK,CAACN,UAAU,GAC1C3C,KAAK+C,YAAYG,WACZC,sBAAsBtC,KAAM4B,UAAW,WAEtCzC,KAAKoD,qBAAqBvC,WAC3BwC,YAAYxC,KAAM4B,YAE7B,MAAOS,YACAC,sBAAsBtC,KAAM4B,UAAW,wBAUjC5B,KAAM4B,iBACfzC,KAAK0C,eAAe7B,KAAM4B,iBAI1Ba,qBAAuBzC,KAAKqB,cAAclC,KAAKmC,UAAU7B,qBAIzDiD,4BAA8BC,kBAAiBC,mBACjDzD,KAAK+C,YAAYW,iBACjBJ,qBAAqBf,YAIpBQ,YAAYY,WAAa3D,KAAK4D,kBAAkBL,6BAG/CM,wBAA0BC,mBAAUC,OAAO,8BAA+B/D,KAAK+C,kBAGhF9C,OAAO+D,cAAcH,wBACrB5D,OAAOgE,YAAY,mBACnBhE,OAAOiE,cAAcC,aAGrB9C,YAAY+C,mCAUOvD,KAAM4B,eAAW4B,oEAAe,GACnDA,eAEDA,mBAAqB,kBAAU,eAAgB,0BAE9ChD,YAAYiD,QAAQR,mBAAUC,OAAO,kCAAmC,cAAiBM,sBACxFE,QAAU1D,KAAKqB,cAAclC,KAAKmC,UAAU3B,UAC5CyB,YAAcpB,KAAKqB,cAAclC,KAAKmC,UAAUpC,kBACtDwE,QAAQC,UAAUC,OAAO,UACzBxC,YAAYuC,UAAUE,IAAI,eACrBrB,YAAYxC,KAAM4B,sCAQA5B,MACLA,KAAKqB,cAAclC,KAAKmC,UAAU5B,WAC1CiE,UAAUC,OAAO,UAGE5D,KAAKqB,cAAclC,KAAKmC,UAAU7B,qBAC1CiC,MAAQvC,KAAK+C,YAAYW,iBACtB7C,KAAKqB,cAAclC,KAAKmC,UAAU/B,iBAC1CoE,UAAUC,OAAO,UACL5D,KAAKqB,cAAclC,KAAKmC,UAAU9B,qBAC1CmE,UAAUE,IAAI,UAQtC5B,eAAejC,YAIJ,CACH8D,WAJc,yBAAa3E,KAAKC,QAKhC2E,WAJe/D,KAAKqB,cAAclC,KAAKmC,UAAUhC,cAAcoC,OAevEqB,kBAAkBiB,YAKRC,eAHqBD,KAAKE,QAAQ,eAAgB,cAGdA,QAAQ,MAAO,4BAG5CD"} \ No newline at end of file +{"version":3,"file":"generatetext.min.js","sources":["../src/generatetext.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 * Tiny AI generate text.\n *\n * @module tiny_aiplacement/generatetext\n * @copyright 2024 Matt Porritt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport TextModal from './textmodal';\nimport Ajax from 'core/ajax';\nimport {getString} from 'core/str';\nimport Templates from 'core/templates';\nimport {getContextId} from './options';\nimport TinyAiTextMarker from './textmark';\nimport GenerateBase from './generatebase';\n\nexport default class GenerateText extends GenerateBase {\n SELECTORS = {\n GENERATEBUTTON: () => `[id=\"${this.editor.id}_tiny_aiplacement_generatebutton\"]`,\n PROMPTAREA: () => `[id=\"${this.editor.id}_tiny_aiplacement_textprompt\"]`,\n RESPONSEWRAPPER: '.tiny_aiplacement_textresponse',\n RESPONSEPLACEHOLDER: '.tiny_aiplacement_textresponse_placeholder',\n GENERATEDRESPONSE: () => `[id=\"${this.editor.id}_tiny_aiplacement_textresponse\"]`,\n INSERTBTN: '[data-action=\"inserter\"]',\n BACKTBTN: '[data-action=\"back\"]',\n };\n\n getModalClass() {\n return TextModal;\n }\n\n /**\n * Handle click events within the text modal.\n *\n * @param {Event} e - The click event object.\n * @param {HTMLElement} root - The root element of the modal.\n */\n handleContentModalClick(e, root) {\n const actions = {\n generate: () => this.handleSubmit(root, e.target),\n inserter: () => this.handleInsert(root, e.target),\n cancel: () => this.modalObject.destroy(),\n back: () => {\n this.modalObject.destroy();\n this.displayContentModal();\n },\n };\n\n const actionKey = Object.keys(actions).find(key => e.target.closest(`[data-action=\"${key}\"]`));\n if (actionKey) {\n e.preventDefault();\n actions[actionKey]();\n }\n }\n\n /**\n * Set up the prompt area in the modal, adding necessary event listeners.\n *\n * @param {HTMLElement} root - The root element of the modal.\n */\n setupPromptArea(root) {\n const generateBtn = root.querySelector(this.SELECTORS.GENERATEBUTTON());\n const promptArea = root.querySelector(this.SELECTORS.PROMPTAREA());\n\n promptArea.addEventListener('input', () => {\n generateBtn.disabled = promptArea.value.trim() === '';\n });\n }\n\n /**\n * Handle the submit action.\n *\n * @param {Object} root The root element of the modal.\n * @param {Object} submitBtn The submit button element.\n */\n async handleSubmit(root, submitBtn) {\n await this.displayLoading(root, submitBtn);\n\n const requestArgs = this.getRequestArgs(root);\n const request = {\n methodname: 'aiplacement_editor_generate_text',\n args: requestArgs\n };\n\n try {\n this.responseObj = await Ajax.call([request])[0];\n if (this.responseObj.error) {\n this.handleGenerationError(root, submitBtn, '');\n } else {\n await this.displayGeneratedText(root);\n this.hideLoading(root, submitBtn);\n }\n } catch (error) {\n this.handleGenerationError(root, submitBtn, '');\n }\n }\n\n /**\n * Handle the insert action.\n *\n * @param {Object} root The root element of the modal.\n * @param {HTMLElement} submitBtn - The submit button element.\n */\n async handleInsert(root, submitBtn) {\n await this.displayLoading(root, submitBtn);\n\n // Update the generated response with the content from the form.\n // In case the user has edited the response.\n const generatedResponseDiv = root.querySelector(this.SELECTORS.GENERATEDRESPONSE());\n\n // Wrap the edited sections in the response with tags.\n // This is so we can differentiate between the edited sections and the generated content.\n const wrappedEditedResponse = await TinyAiTextMarker.wrapEditedSections(\n this.responseObj.generatedcontent,\n generatedResponseDiv.value)\n ;\n\n // Replace double line breaks with
and with

for paragraphs.\n this.responseObj.editedtext = this.replaceLineBreaks(wrappedEditedResponse);\n\n // Generate the HTML for the response.\n const formattedResponse = await Templates.render('tiny_aiplacement/textinsert', this.responseObj);\n\n // Insert the response into the editor.\n this.editor.insertContent(formattedResponse);\n this.editor.execCommand('mceRepaint');\n this.editor.windowManager.close();\n\n // Close the modal and return to the editor.\n this.modalObject.hide();\n }\n\n /**\n * Handle a generation error.\n *\n * @param {Object} root The root element of the modal.\n * @param {Object} submitBtn The submit button element.\n * @param {String} errorMessage The error message to display.\n */\n async handleGenerationError(root, submitBtn, errorMessage = '') {\n if (!errorMessage) {\n // Get the default error message.\n errorMessage = await getString('errorgeneral', 'tiny_aiplacement');\n }\n this.modalObject.setBody(Templates.render('tiny_aiplacement/modalbodyerror', {'errorMessage': errorMessage}));\n const backBtn = root.querySelector(this.SELECTORS.BACKTBTN);\n const generateBtn = root.querySelector(this.SELECTORS.GENERATEBUTTON());\n backBtn.classList.remove('hidden');\n generateBtn.classList.add('hidden');\n this.hideLoading(root, submitBtn);\n }\n\n /**\n * Display the generated image in the modal.\n *\n * @param {HTMLElement} root - The root element of the modal.\n */\n async displayGeneratedText(root) {\n const insertBtn = root.querySelector(this.SELECTORS.INSERTBTN);\n insertBtn.classList.remove('hidden');\n\n // Add generated text to the modal.\n const generatedResponseDiv = root.querySelector(this.SELECTORS.GENERATEDRESPONSE());\n generatedResponseDiv.value = this.responseObj.generatedcontent;\n const responseWrapper = root.querySelector(this.SELECTORS.RESPONSEWRAPPER);\n responseWrapper.classList.remove('hidden');\n const responsePlaceholder = root.querySelector(this.SELECTORS.RESPONSEPLACEHOLDER);\n responsePlaceholder.classList.add('hidden');\n }\n\n /**\n * Get the request args for the generated text.\n *\n * @param {Object} root The root element of the modal.\n */\n getRequestArgs(root) {\n const contextId = getContextId(this.editor);\n const promptText = root.querySelector(this.SELECTORS.PROMPTAREA()).value;\n\n return {\n contextid: contextId,\n prompttext: promptText\n };\n }\n\n /**\n * Replace double line breaks with
and with

for paragraphs.\n * This is to handle the difference in response from the AI to what is expected by the editor.\n *\n * @param {String} text The text to replace.\n * @returns {String}\n */\n replaceLineBreaks(text) {\n // Replace double line breaks with

for paragraphs\n const textWithParagraphs = text.replace(/\\n{2,}|\\r\\n/g, '

');\n\n // Replace remaining single line breaks with
tags\n const textWithBreaks = textWithParagraphs.replace(/\\n/g, '
');\n\n // Add opening and closing

tags to wrap the entire content\n return `

${textWithBreaks}

`;\n }\n}\n"],"names":["GenerateText","GenerateBase","GENERATEBUTTON","this","editor","id","PROMPTAREA","RESPONSEWRAPPER","RESPONSEPLACEHOLDER","GENERATEDRESPONSE","INSERTBTN","BACKTBTN","getModalClass","TextModal","handleContentModalClick","e","root","actions","generate","handleSubmit","target","inserter","handleInsert","cancel","modalObject","destroy","back","displayContentModal","actionKey","Object","keys","find","key","closest","preventDefault","setupPromptArea","generateBtn","querySelector","SELECTORS","promptArea","addEventListener","disabled","value","trim","submitBtn","displayLoading","request","methodname","args","getRequestArgs","responseObj","Ajax","call","error","handleGenerationError","displayGeneratedText","hideLoading","generatedResponseDiv","wrappedEditedResponse","TinyAiTextMarker","wrapEditedSections","generatedcontent","editedtext","replaceLineBreaks","formattedResponse","Templates","render","insertContent","execCommand","windowManager","close","hide","errorMessage","setBody","backBtn","classList","remove","add","contextid","prompttext","text","textWithBreaks","replace"],"mappings":"inBA+BqBA,qBAAqBC,gFAC1B,CACRC,eAAgB,mBAAcC,KAAKC,OAAOC,yCAC1CC,WAAY,mBAAcH,KAAKC,OAAOC,qCACtCE,gBAAiB,iCACjBC,oBAAqB,6CACrBC,kBAAmB,mBAAcN,KAAKC,OAAOC,uCAC7CK,UAAW,2BACXC,SAAU,4JAGdC,uBACWC,mBASXC,wBAAwBC,EAAGC,YACjBC,QAAU,CACZC,SAAU,IAAMf,KAAKgB,aAAaH,KAAMD,EAAEK,QAC1CC,SAAU,IAAMlB,KAAKmB,aAAaN,KAAMD,EAAEK,QAC1CG,OAAQ,IAAMpB,KAAKqB,YAAYC,UAC/BC,KAAM,UACGF,YAAYC,eACZE,wBAIPC,UAAYC,OAAOC,KAAKb,SAASc,MAAKC,KAAOjB,EAAEK,OAAOa,gCAAyBD,aACjFJ,YACAb,EAAEmB,iBACFjB,QAAQW,cAShBO,gBAAgBnB,YACNoB,YAAcpB,KAAKqB,cAAclC,KAAKmC,UAAUpC,kBAChDqC,WAAavB,KAAKqB,cAAclC,KAAKmC,UAAUhC,cAErDiC,WAAWC,iBAAiB,SAAS,KACjCJ,YAAYK,SAAuC,KAA5BF,WAAWG,MAAMC,6BAU7B3B,KAAM4B,iBACfzC,KAAK0C,eAAe7B,KAAM4B,iBAG1BE,QAAU,CACZC,WAAY,mCACZC,KAHgB7C,KAAK8C,eAAejC,gBAO/BkC,kBAAoBC,cAAKC,KAAK,CAACN,UAAU,GAC1C3C,KAAK+C,YAAYG,WACZC,sBAAsBtC,KAAM4B,UAAW,WAEtCzC,KAAKoD,qBAAqBvC,WAC3BwC,YAAYxC,KAAM4B,YAE7B,MAAOS,YACAC,sBAAsBtC,KAAM4B,UAAW,wBAUjC5B,KAAM4B,iBACfzC,KAAK0C,eAAe7B,KAAM4B,iBAI1Ba,qBAAuBzC,KAAKqB,cAAclC,KAAKmC,UAAU7B,qBAIzDiD,4BAA8BC,kBAAiBC,mBACjDzD,KAAK+C,YAAYW,iBACjBJ,qBAAqBf,YAIpBQ,YAAYY,WAAa3D,KAAK4D,kBAAkBL,6BAG/CM,wBAA0BC,mBAAUC,OAAO,8BAA+B/D,KAAK+C,kBAGhF9C,OAAO+D,cAAcH,wBACrB5D,OAAOgE,YAAY,mBACnBhE,OAAOiE,cAAcC,aAGrB9C,YAAY+C,mCAUOvD,KAAM4B,eAAW4B,oEAAe,GACnDA,eAEDA,mBAAqB,kBAAU,eAAgB,0BAE9ChD,YAAYiD,QAAQR,mBAAUC,OAAO,kCAAmC,cAAiBM,sBACxFE,QAAU1D,KAAKqB,cAAclC,KAAKmC,UAAU3B,UAC5CyB,YAAcpB,KAAKqB,cAAclC,KAAKmC,UAAUpC,kBACtDwE,QAAQC,UAAUC,OAAO,UACzBxC,YAAYuC,UAAUE,IAAI,eACrBrB,YAAYxC,KAAM4B,sCAQA5B,MACLA,KAAKqB,cAAclC,KAAKmC,UAAU5B,WAC1CiE,UAAUC,OAAO,UAGE5D,KAAKqB,cAAclC,KAAKmC,UAAU7B,qBAC1CiC,MAAQvC,KAAK+C,YAAYW,iBACtB7C,KAAKqB,cAAclC,KAAKmC,UAAU/B,iBAC1CoE,UAAUC,OAAO,UACL5D,KAAKqB,cAAclC,KAAKmC,UAAU9B,qBAC1CmE,UAAUE,IAAI,UAQtC5B,eAAejC,YAIJ,CACH8D,WAJc,yBAAa3E,KAAKC,QAKhC2E,WAJe/D,KAAKqB,cAAclC,KAAKmC,UAAUhC,cAAcoC,OAevEqB,kBAAkBiB,YAKRC,eAHqBD,KAAKE,QAAQ,eAAgB,cAGdA,QAAQ,MAAO,4BAG5CD"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/aiplacement/amd/src/generatebase.js b/lib/editor/tiny/plugins/aiplacement/amd/src/generatebase.js index 1c4532959fe..81ce58224db 100644 --- a/lib/editor/tiny/plugins/aiplacement/amd/src/generatebase.js +++ b/lib/editor/tiny/plugins/aiplacement/amd/src/generatebase.js @@ -113,7 +113,7 @@ export default class GenerateBase { * @param {Object} root The root element of the modal. */ hideLoadingSpinner(root) { - const loadingSpinnerDiv = root.querySelector(`#${this.editor.id}_tiny_aiplacement_spinner`); + const loadingSpinnerDiv = root.querySelector(`[id="${this.editor.id}_tiny_aiplacement_spinner"]`); loadingSpinnerDiv.classList.add('hidden'); loadingSpinnerDiv.classList.remove('tiny-aiplacement-loading-spinner-container'); } @@ -126,10 +126,10 @@ export default class GenerateBase { * @param {String|null} removeClass - The class to be removed from the loading spinner div, if any. */ async displayLoading(root, submitBtn, removeClass = null) { - const loadingSpinnerDiv = root.querySelector(`#${this.editor.id}_tiny_aiplacement_spinner`); - const overlayDiv = root.querySelector(`#${this.editor.id}_tiny_aiplacement_overlay`); - const blurDiv = root.querySelector(`#${this.editor.id}_tiny_aiplacement_blur`); - const loadingTextDiv = root.querySelector(`#${this.editor.id}_tiny_aiplacement_loading_text`); + const loadingSpinnerDiv = root.querySelector(`[id="${this.editor.id}_tiny_aiplacement_spinner"]`); + const overlayDiv = root.querySelector(`[id="${this.editor.id}_tiny_aiplacement_overlay"]`); + const blurDiv = root.querySelector(`[id="${this.editor.id}_tiny_aiplacement_blur"]`); + const loadingTextDiv = root.querySelector(`[id="${this.editor.id}_tiny_aiplacement_loading_text"]`); const actionButtons = root.querySelectorAll('.tiny-aiplacement-generate-footer button'); loadingMessages(loadingTextDiv); @@ -157,9 +157,9 @@ export default class GenerateBase { * @param {Object} submitBtn The submit button element. */ async hideLoading(root, submitBtn) { - const loadingSpinnerDiv = root.querySelector(`#${this.editor.id}_tiny_aiplacement_spinner`); - const overlayDiv = root.querySelector(`#${this.editor.id}_tiny_aiplacement_overlay`); - const blurDiv = root.querySelector(`#${this.editor.id}_tiny_aiplacement_blur`); + const loadingSpinnerDiv = root.querySelector(`[id="${this.editor.id}_tiny_aiplacement_spinner"]`); + const overlayDiv = root.querySelector(`[id="${this.editor.id}_tiny_aiplacement_overlay"]`); + const blurDiv = root.querySelector(`[id="${this.editor.id}_tiny_aiplacement_blur"]`); const actionButtons = root.querySelectorAll('.tiny-aiplacement-generate-footer button'); if (loadingSpinnerDiv) { loadingSpinnerDiv.classList.add('hidden'); diff --git a/lib/editor/tiny/plugins/aiplacement/amd/src/generateimage.js b/lib/editor/tiny/plugins/aiplacement/amd/src/generateimage.js index ddfc9bc6931..0329dc30d4c 100644 --- a/lib/editor/tiny/plugins/aiplacement/amd/src/generateimage.js +++ b/lib/editor/tiny/plugins/aiplacement/amd/src/generateimage.js @@ -31,13 +31,13 @@ import GenerateBase from 'tiny_aiplacement/generatebase'; export default class GenerateImage extends GenerateBase { SELECTORS = { - GENERATEBUTTON: () => `#${this.editor.id}_tiny_aiplacement_generatebutton`, - PROMPTAREA: () => `#${this.editor.id}_tiny_aiplacement_imageprompt`, - IMAGECONTAINER: () => `#${this.editor.id}_tiny_aiplacement_generate_image`, + GENERATEBUTTON: () => `[id="${this.editor.id}_tiny_aiplacement_generatebutton"]`, + PROMPTAREA: () => `[id="${this.editor.id}_tiny_aiplacement_imageprompt"]`, + IMAGECONTAINER: () => `[id="${this.editor.id}_tiny_aiplacement_generate_image"]`, GENERATEBTN: '[data-action="generate"]', INSERTBTN: '[data-action="inserter"]', BACKTBTN: '[data-action="back"]', - GENERATEDIMAGE: () => `#${this.editor.id}_tiny_generated_image`, + GENERATEDIMAGE: () => `[id="${this.editor.id}_tiny_generated_image"]`, }; imageURL = null; diff --git a/lib/editor/tiny/plugins/aiplacement/amd/src/generatetext.js b/lib/editor/tiny/plugins/aiplacement/amd/src/generatetext.js index 0372d344474..64dbc5b9ec6 100644 --- a/lib/editor/tiny/plugins/aiplacement/amd/src/generatetext.js +++ b/lib/editor/tiny/plugins/aiplacement/amd/src/generatetext.js @@ -31,11 +31,11 @@ import GenerateBase from './generatebase'; export default class GenerateText extends GenerateBase { SELECTORS = { - GENERATEBUTTON: () => `#${this.editor.id}_tiny_aiplacement_generatebutton`, - PROMPTAREA: () => `#${this.editor.id}_tiny_aiplacement_textprompt`, + GENERATEBUTTON: () => `[id="${this.editor.id}_tiny_aiplacement_generatebutton"]`, + PROMPTAREA: () => `[id="${this.editor.id}_tiny_aiplacement_textprompt"]`, RESPONSEWRAPPER: '.tiny_aiplacement_textresponse', RESPONSEPLACEHOLDER: '.tiny_aiplacement_textresponse_placeholder', - GENERATEDRESPONSE: () => `#${this.editor.id}_tiny_aiplacement_textresponse`, + GENERATEDRESPONSE: () => `[id="${this.editor.id}_tiny_aiplacement_textresponse"]`, INSERTBTN: '[data-action="inserter"]', BACKTBTN: '[data-action="back"]', }; diff --git a/lib/editor/tiny/plugins/aiplacement/classes/plugininfo.php b/lib/editor/tiny/plugins/aiplacement/classes/plugininfo.php index 1e5a3633d6e..bd36643e9c9 100644 --- a/lib/editor/tiny/plugins/aiplacement/classes/plugininfo.php +++ b/lib/editor/tiny/plugins/aiplacement/classes/plugininfo.php @@ -49,7 +49,7 @@ class plugininfo extends plugin implements plugin_with_buttons, plugin_with_menu array $fpoptions, ?editor $editor = null ): bool { - return in_array(true, self::get_allowed_actions($context)); + return in_array(true, self::get_allowed_actions($context, $options)); } #[\Override] @@ -72,7 +72,7 @@ class plugininfo extends plugin implements plugin_with_buttons, plugin_with_menu global $USER; $userid = (int) $USER->id; - $allowedactions = self::get_allowed_actions($context); + $allowedactions = self::get_allowed_actions($context, $options); return array_merge([ 'contextid' => $context->id, @@ -85,9 +85,10 @@ class plugininfo extends plugin implements plugin_with_buttons, plugin_with_menu * Get the allowed actions for the plugin. * * @param context $context The context that the editor is used within + * @param array $options The options passed in when requesting the editor * @return array The allowed actions. */ - private static function get_allowed_actions(context $context): array { + private static function get_allowed_actions(context $context, array $options): array { [$plugintype, $pluginname] = explode('_', \core_component::normalize_componentname('aiplacement_editor'), 2); $manager = \core_plugin_manager::resolve_plugininfo_class($plugintype); $allowedactions = []; @@ -99,7 +100,14 @@ class plugininfo extends plugin implements plugin_with_buttons, plugin_with_menu && manager::is_action_enabled('aiplacement_editor', $action) && !empty($providers[$providerclass]) ) { - $allowedactions[$action] = true; + if ($action == 'generate_image') { + // For generate image, we need to check if the user has the capability to upload files. + $canhavefiles = !empty($options['maxfiles']); + $canhaveexternalfiles = !empty($options['return_types']) && ($options['return_types'] & FILE_EXTERNAL); + $allowedactions[$action] = $canhavefiles || $canhaveexternalfiles; + } else { + $allowedactions[$action] = true; + } } else { $allowedactions[$action] = false; }