From 62ae26949022a46afec61452a39193524f3848f0 Mon Sep 17 00:00:00 2001 From: Andi Permana Date: Tue, 23 Sep 2025 11:50:04 +0700 Subject: [PATCH] MDL-86511 tiny_link: TinyMCE fails to insert a link using enter key --- lib/editor/tiny/plugins/link/amd/build/ui.min.js | 2 +- lib/editor/tiny/plugins/link/amd/build/ui.min.js.map | 2 +- lib/editor/tiny/plugins/link/amd/src/ui.js | 12 ++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/editor/tiny/plugins/link/amd/build/ui.min.js b/lib/editor/tiny/plugins/link/amd/build/ui.min.js index 10c3c39c547..64f93fa23eb 100644 --- a/lib/editor/tiny/plugins/link/amd/build/ui.min.js +++ b/lib/editor/tiny/plugins/link/amd/build/ui.min.js @@ -5,6 +5,6 @@ define("tiny_link/ui",["exports","editor_tiny/options","editor_tiny/utils","tiny * @module tiny_link/ui * @copyright 2023 Huong Nguyen * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.handleAction=void 0,_modal=_interopRequireDefault(_modal),_selectors=_interopRequireDefault(_selectors);_exports.handleAction=function(editor){let unlink=arguments.length>1&&void 0!==arguments[1]&&arguments[1];unlink?(0,_link.unSetLink)(editor):displayDialogue(editor)};const displayDialogue=async editor=>{const modal=await _modal.default.create({templateContext:getTemplateContext(editor)}),root=(await modal.getRoot())[0],currentForm=root.querySelector("form");root.addEventListener("click",(e=>{const submitAction=e.target.closest(_selectors.default.actions.submit),linkBrowserAction=e.target.closest(_selectors.default.actions.linkBrowser);submitAction&&(e.preventDefault(),(0,_link.setLink)(currentForm,editor),modal.destroy()),linkBrowserAction&&(e.preventDefault(),(0,_utils.displayFilepicker)(editor,"link").then((params=>(filePickerCallback(params,currentForm,editor),modal.destroy()))).catch())}));const linkTitle=root.querySelector(_selectors.default.elements.urlText),linkUrl=root.querySelector(_selectors.default.elements.urlEntry);linkTitle.addEventListener("change",(()=>{linkTitle.value.length>0?linkTitle.dataset.useLinkAsText="false":(linkTitle.dataset.useLinkAsText="true",linkTitle.value=linkUrl.value)})),linkUrl.addEventListener("keyup",(()=>{updateTextToDisplay(currentForm)}))},getTemplateContext=editor=>{const data=(0,_link.getCurrentLinkData)(editor);return Object.assign({},{elementid:editor.id,showfilepicker:(0,_options2.getPermissions)(editor).filepicker&&void 0!==(0,_options.getFilePicker)(editor,"link"),isupdating:!!data.url},data)},filePickerCallback=(params,currentForm,editor)=>{if(params.url){currentForm.querySelector(_selectors.default.elements.urlEntry).value=params.url,(0,_link.setLink)(currentForm,editor)}},updateTextToDisplay=currentForm=>{const urlEntry=currentForm.querySelector(_selectors.default.elements.urlEntry),urlText=currentForm.querySelector(_selectors.default.elements.urlText);"true"===urlText.dataset.useLinkAsText&&(urlText.value=urlEntry.value)}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.handleAction=void 0,_modal=_interopRequireDefault(_modal),_selectors=_interopRequireDefault(_selectors);_exports.handleAction=function(editor){let unlink=arguments.length>1&&void 0!==arguments[1]&&arguments[1];unlink?(0,_link.unSetLink)(editor):displayDialogue(editor)};const displayDialogue=async editor=>{const modal=await _modal.default.create({templateContext:getTemplateContext(editor)}),root=(await modal.getRoot())[0],currentForm=root.querySelector("form");root.addEventListener("click",(e=>{const submitAction=e.target.closest(_selectors.default.actions.submit),linkBrowserAction=e.target.closest(_selectors.default.actions.linkBrowser);submitAction&&(e.preventDefault(),(0,_link.setLink)(currentForm,editor),modal.destroy()),linkBrowserAction&&(e.preventDefault(),(0,_utils.displayFilepicker)(editor,"link").then((params=>(filePickerCallback(params,currentForm,editor),modal.destroy()))).catch())})),root.addEventListener("keydown",(e=>{if("Enter"===e.key||" "===e.key){e.target.closest(_selectors.default.actions.submit)&&(e.preventDefault(),(0,_link.setLink)(currentForm,editor),modal.destroy())}}));const linkTitle=root.querySelector(_selectors.default.elements.urlText),linkUrl=root.querySelector(_selectors.default.elements.urlEntry);linkTitle.addEventListener("change",(()=>{linkTitle.value.length>0?linkTitle.dataset.useLinkAsText="false":(linkTitle.dataset.useLinkAsText="true",linkTitle.value=linkUrl.value)})),linkUrl.addEventListener("keyup",(()=>{updateTextToDisplay(currentForm)}))},getTemplateContext=editor=>{const data=(0,_link.getCurrentLinkData)(editor);return Object.assign({},{elementid:editor.id,showfilepicker:(0,_options2.getPermissions)(editor).filepicker&&void 0!==(0,_options.getFilePicker)(editor,"link"),isupdating:!!data.url},data)},filePickerCallback=(params,currentForm,editor)=>{if(params.url){currentForm.querySelector(_selectors.default.elements.urlEntry).value=params.url,(0,_link.setLink)(currentForm,editor)}},updateTextToDisplay=currentForm=>{const urlEntry=currentForm.querySelector(_selectors.default.elements.urlEntry),urlText=currentForm.querySelector(_selectors.default.elements.urlText);"true"===urlText.dataset.useLinkAsText&&(urlText.value=urlEntry.value)}})); //# sourceMappingURL=ui.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/link/amd/build/ui.min.js.map b/lib/editor/tiny/plugins/link/amd/build/ui.min.js.map index e1fc3c1482e..172a55f693c 100644 --- a/lib/editor/tiny/plugins/link/amd/build/ui.min.js.map +++ b/lib/editor/tiny/plugins/link/amd/build/ui.min.js.map @@ -1 +1 @@ -{"version":3,"file":"ui.min.js","sources":["../src/ui.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 Link UI.\n *\n * @module tiny_link/ui\n * @copyright 2023 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getFilePicker} from 'editor_tiny/options';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport LinkModal from 'tiny_link/modal';\nimport {getPermissions} from \"tiny_link/options\";\nimport {setLink, getCurrentLinkData, unSetLink} from \"tiny_link/link\";\nimport Selectors from 'tiny_link/selectors';\n\n/**\n * Handle action.\n *\n * @param {TinyMCE} editor\n * @param {boolean} unlink\n */\nexport const handleAction = (editor, unlink = false) => {\n if (!unlink) {\n displayDialogue(editor);\n } else {\n unSetLink(editor);\n }\n};\n\n/**\n * Display the link dialogue.\n *\n * @param {TinyMCE} editor\n * @returns {Promise}\n */\nconst displayDialogue = async(editor) => {\n const modal = await LinkModal.create({\n templateContext: getTemplateContext(editor),\n });\n\n const $root = await modal.getRoot();\n const root = $root[0];\n const currentForm = root.querySelector('form');\n\n root.addEventListener('click', (e) => {\n const submitAction = e.target.closest(Selectors.actions.submit);\n const linkBrowserAction = e.target.closest(Selectors.actions.linkBrowser);\n if (submitAction) {\n e.preventDefault();\n setLink(currentForm, editor);\n modal.destroy();\n }\n if (linkBrowserAction) {\n e.preventDefault();\n displayFilepicker(editor, 'link').then((params) => {\n filePickerCallback(params, currentForm, editor);\n return modal.destroy();\n }).catch();\n }\n });\n\n const linkTitle = root.querySelector(Selectors.elements.urlText);\n const linkUrl = root.querySelector(Selectors.elements.urlEntry);\n linkTitle.addEventListener('change', () => {\n if (linkTitle.value.length > 0) {\n linkTitle.dataset.useLinkAsText = 'false';\n } else {\n linkTitle.dataset.useLinkAsText = 'true';\n linkTitle.value = linkUrl.value;\n }\n });\n\n linkUrl.addEventListener('keyup', () => {\n updateTextToDisplay(currentForm);\n });\n};\n\n/**\n * Get template context.\n *\n * @param {TinyMCE} editor\n * @returns {Object}\n */\nconst getTemplateContext = (editor) => {\n const data = getCurrentLinkData(editor);\n\n return Object.assign({}, {\n elementid: editor.id,\n showfilepicker: getPermissions(editor).filepicker &&\n (typeof getFilePicker(editor, 'link') !== 'undefined'),\n isupdating: !!data.url,\n }, data);\n};\n\n/**\n * Update the dialogue after a link was selected in the File Picker.\n *\n * @param {Object} params\n * @param {Element} currentForm\n * @param {TinyMCE} editor\n */\nconst filePickerCallback = (params, currentForm, editor) => {\n if (params.url) {\n const inputUrl = currentForm.querySelector(Selectors.elements.urlEntry);\n inputUrl.value = params.url;\n setLink(currentForm, editor);\n }\n};\n\n/**\n * Update the text to display if the user does not provide the custom text.\n *\n * @param {Element} currentForm\n */\nconst updateTextToDisplay = (currentForm) => {\n const urlEntry = currentForm.querySelector(Selectors.elements.urlEntry);\n const urlText = currentForm.querySelector(Selectors.elements.urlText);\n if (urlText.dataset.useLinkAsText === 'true') {\n urlText.value = urlEntry.value;\n }\n};\n"],"names":["editor","unlink","displayDialogue","async","modal","LinkModal","create","templateContext","getTemplateContext","root","getRoot","currentForm","querySelector","addEventListener","e","submitAction","target","closest","Selectors","actions","submit","linkBrowserAction","linkBrowser","preventDefault","destroy","then","params","filePickerCallback","catch","linkTitle","elements","urlText","linkUrl","urlEntry","value","length","dataset","useLinkAsText","updateTextToDisplay","data","Object","assign","elementid","id","showfilepicker","filepicker","isupdating","url"],"mappings":";;;;;;;oMAoC4B,SAACA,YAAQC,+DAC5BA,2BAGSD,QAFVE,gBAAgBF,eAYlBE,gBAAkBC,MAAAA,eACdC,YAAcC,eAAUC,OAAO,CACjCC,gBAAiBC,mBAAmBR,UAIlCS,YADcL,MAAMM,WACP,GACbC,YAAcF,KAAKG,cAAc,QAEvCH,KAAKI,iBAAiB,SAAUC,UACtBC,aAAeD,EAAEE,OAAOC,QAAQC,mBAAUC,QAAQC,QAClDC,kBAAoBP,EAAEE,OAAOC,QAAQC,mBAAUC,QAAQG,aACzDP,eACAD,EAAES,mCACMZ,YAAaX,QACrBI,MAAMoB,WAENH,oBACAP,EAAES,8CACgBvB,OAAQ,QAAQyB,MAAMC,SACpCC,mBAAmBD,OAAQf,YAAaX,QACjCI,MAAMoB,aACdI,kBAILC,UAAYpB,KAAKG,cAAcM,mBAAUY,SAASC,SAClDC,QAAUvB,KAAKG,cAAcM,mBAAUY,SAASG,UACtDJ,UAAUhB,iBAAiB,UAAU,KAC7BgB,UAAUK,MAAMC,OAAS,EACzBN,UAAUO,QAAQC,cAAgB,SAElCR,UAAUO,QAAQC,cAAgB,OAClCR,UAAUK,MAAQF,QAAQE,UAIlCF,QAAQnB,iBAAiB,SAAS,KAC9ByB,oBAAoB3B,iBAUtBH,mBAAsBR,eAClBuC,MAAO,4BAAmBvC,eAEzBwC,OAAOC,OAAO,GAAI,CACrBC,UAAW1C,OAAO2C,GAClBC,gBAAgB,4BAAe5C,QAAQ6C,iBACO,KAAlC,0BAAc7C,OAAQ,QAClC8C,aAAcP,KAAKQ,KACpBR,OAUDZ,mBAAqB,CAACD,OAAQf,YAAaX,aACzC0B,OAAOqB,IAAK,CACKpC,YAAYC,cAAcM,mBAAUY,SAASG,UACrDC,MAAQR,OAAOqB,sBAChBpC,YAAaX,UASvBsC,oBAAuB3B,oBACnBsB,SAAWtB,YAAYC,cAAcM,mBAAUY,SAASG,UACxDF,QAAUpB,YAAYC,cAAcM,mBAAUY,SAASC,SACvB,SAAlCA,QAAQK,QAAQC,gBAChBN,QAAQG,MAAQD,SAASC"} \ No newline at end of file +{"version":3,"file":"ui.min.js","sources":["../src/ui.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 Link UI.\n *\n * @module tiny_link/ui\n * @copyright 2023 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getFilePicker} from 'editor_tiny/options';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport LinkModal from 'tiny_link/modal';\nimport {getPermissions} from \"tiny_link/options\";\nimport {setLink, getCurrentLinkData, unSetLink} from \"tiny_link/link\";\nimport Selectors from 'tiny_link/selectors';\n\n/**\n * Handle action.\n *\n * @param {TinyMCE} editor\n * @param {boolean} unlink\n */\nexport const handleAction = (editor, unlink = false) => {\n if (!unlink) {\n displayDialogue(editor);\n } else {\n unSetLink(editor);\n }\n};\n\n/**\n * Display the link dialogue.\n *\n * @param {TinyMCE} editor\n * @returns {Promise}\n */\nconst displayDialogue = async(editor) => {\n const modal = await LinkModal.create({\n templateContext: getTemplateContext(editor),\n });\n\n const $root = await modal.getRoot();\n const root = $root[0];\n const currentForm = root.querySelector('form');\n\n root.addEventListener('click', (e) => {\n const submitAction = e.target.closest(Selectors.actions.submit);\n const linkBrowserAction = e.target.closest(Selectors.actions.linkBrowser);\n if (submitAction) {\n e.preventDefault();\n setLink(currentForm, editor);\n modal.destroy();\n }\n if (linkBrowserAction) {\n e.preventDefault();\n displayFilepicker(editor, 'link').then((params) => {\n filePickerCallback(params, currentForm, editor);\n return modal.destroy();\n }).catch();\n }\n });\n\n // Add keyboard event listener to handle Enter and Space keys on submit button\n root.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n const submitAction = e.target.closest(Selectors.actions.submit);\n if (submitAction) {\n e.preventDefault();\n setLink(currentForm, editor);\n modal.destroy();\n }\n }\n });\n\n const linkTitle = root.querySelector(Selectors.elements.urlText);\n const linkUrl = root.querySelector(Selectors.elements.urlEntry);\n linkTitle.addEventListener('change', () => {\n if (linkTitle.value.length > 0) {\n linkTitle.dataset.useLinkAsText = 'false';\n } else {\n linkTitle.dataset.useLinkAsText = 'true';\n linkTitle.value = linkUrl.value;\n }\n });\n\n linkUrl.addEventListener('keyup', () => {\n updateTextToDisplay(currentForm);\n });\n};\n\n/**\n * Get template context.\n *\n * @param {TinyMCE} editor\n * @returns {Object}\n */\nconst getTemplateContext = (editor) => {\n const data = getCurrentLinkData(editor);\n\n return Object.assign({}, {\n elementid: editor.id,\n showfilepicker: getPermissions(editor).filepicker &&\n (typeof getFilePicker(editor, 'link') !== 'undefined'),\n isupdating: !!data.url,\n }, data);\n};\n\n/**\n * Update the dialogue after a link was selected in the File Picker.\n *\n * @param {Object} params\n * @param {Element} currentForm\n * @param {TinyMCE} editor\n */\nconst filePickerCallback = (params, currentForm, editor) => {\n if (params.url) {\n const inputUrl = currentForm.querySelector(Selectors.elements.urlEntry);\n inputUrl.value = params.url;\n setLink(currentForm, editor);\n }\n};\n\n/**\n * Update the text to display if the user does not provide the custom text.\n *\n * @param {Element} currentForm\n */\nconst updateTextToDisplay = (currentForm) => {\n const urlEntry = currentForm.querySelector(Selectors.elements.urlEntry);\n const urlText = currentForm.querySelector(Selectors.elements.urlText);\n if (urlText.dataset.useLinkAsText === 'true') {\n urlText.value = urlEntry.value;\n }\n};\n"],"names":["editor","unlink","displayDialogue","async","modal","LinkModal","create","templateContext","getTemplateContext","root","getRoot","currentForm","querySelector","addEventListener","e","submitAction","target","closest","Selectors","actions","submit","linkBrowserAction","linkBrowser","preventDefault","destroy","then","params","filePickerCallback","catch","key","linkTitle","elements","urlText","linkUrl","urlEntry","value","length","dataset","useLinkAsText","updateTextToDisplay","data","Object","assign","elementid","id","showfilepicker","filepicker","isupdating","url"],"mappings":";;;;;;;oMAoC4B,SAACA,YAAQC,+DAC5BA,2BAGSD,QAFVE,gBAAgBF,eAYlBE,gBAAkBC,MAAAA,eACdC,YAAcC,eAAUC,OAAO,CACjCC,gBAAiBC,mBAAmBR,UAIlCS,YADcL,MAAMM,WACP,GACbC,YAAcF,KAAKG,cAAc,QAEvCH,KAAKI,iBAAiB,SAAUC,UACtBC,aAAeD,EAAEE,OAAOC,QAAQC,mBAAUC,QAAQC,QAClDC,kBAAoBP,EAAEE,OAAOC,QAAQC,mBAAUC,QAAQG,aACzDP,eACAD,EAAES,mCACMZ,YAAaX,QACrBI,MAAMoB,WAENH,oBACAP,EAAES,8CACgBvB,OAAQ,QAAQyB,MAAMC,SACpCC,mBAAmBD,OAAQf,YAAaX,QACjCI,MAAMoB,aACdI,YAKXnB,KAAKI,iBAAiB,WAAYC,OAChB,UAAVA,EAAEe,KAA6B,MAAVf,EAAEe,IAAa,CACff,EAAEE,OAAOC,QAAQC,mBAAUC,QAAQC,UAEpDN,EAAES,mCACMZ,YAAaX,QACrBI,MAAMoB,qBAKZM,UAAYrB,KAAKG,cAAcM,mBAAUa,SAASC,SAClDC,QAAUxB,KAAKG,cAAcM,mBAAUa,SAASG,UACtDJ,UAAUjB,iBAAiB,UAAU,KAC7BiB,UAAUK,MAAMC,OAAS,EACzBN,UAAUO,QAAQC,cAAgB,SAElCR,UAAUO,QAAQC,cAAgB,OAClCR,UAAUK,MAAQF,QAAQE,UAIlCF,QAAQpB,iBAAiB,SAAS,KAC9B0B,oBAAoB5B,iBAUtBH,mBAAsBR,eAClBwC,MAAO,4BAAmBxC,eAEzByC,OAAOC,OAAO,GAAI,CACrBC,UAAW3C,OAAO4C,GAClBC,gBAAgB,4BAAe7C,QAAQ8C,iBACO,KAAlC,0BAAc9C,OAAQ,QAClC+C,aAAcP,KAAKQ,KACpBR,OAUDb,mBAAqB,CAACD,OAAQf,YAAaX,aACzC0B,OAAOsB,IAAK,CACKrC,YAAYC,cAAcM,mBAAUa,SAASG,UACrDC,MAAQT,OAAOsB,sBAChBrC,YAAaX,UASvBuC,oBAAuB5B,oBACnBuB,SAAWvB,YAAYC,cAAcM,mBAAUa,SAASG,UACxDF,QAAUrB,YAAYC,cAAcM,mBAAUa,SAASC,SACvB,SAAlCA,QAAQK,QAAQC,gBAChBN,QAAQG,MAAQD,SAASC"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/link/amd/src/ui.js b/lib/editor/tiny/plugins/link/amd/src/ui.js index a9e42daf172..b75fdc2a340 100644 --- a/lib/editor/tiny/plugins/link/amd/src/ui.js +++ b/lib/editor/tiny/plugins/link/amd/src/ui.js @@ -74,6 +74,18 @@ const displayDialogue = async(editor) => { } }); + // Add keyboard event listener to handle Enter and Space keys on submit button + root.addEventListener('keydown', (e) => { + if (e.key === 'Enter' || e.key === ' ') { + const submitAction = e.target.closest(Selectors.actions.submit); + if (submitAction) { + e.preventDefault(); + setLink(currentForm, editor); + modal.destroy(); + } + } + }); + const linkTitle = root.querySelector(Selectors.elements.urlText); const linkUrl = root.querySelector(Selectors.elements.urlEntry); linkTitle.addEventListener('change', () => {