From fc34c352d7192bf2262b92adf3737144155d3f8e Mon Sep 17 00:00:00 2001 From: meirzamoodle Date: Mon, 25 Nov 2024 18:37:30 +0700 Subject: [PATCH] MDL-82788 theme_boost: Obtain the correct Tiny and Atto editor elements --- theme/boost/amd/build/form-display-errors.min.js | 2 +- .../boost/amd/build/form-display-errors.min.js.map | 2 +- theme/boost/amd/src/form-display-errors.js | 14 +++++++++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/theme/boost/amd/build/form-display-errors.min.js b/theme/boost/amd/build/form-display-errors.min.js index 57b4d4f627a..6739d143ebd 100644 --- a/theme/boost/amd/build/form-display-errors.min.js +++ b/theme/boost/amd/build/form-display-errors.min.js @@ -6,6 +6,6 @@ * @copyright 2016 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("theme_boost/form-display-errors",["jquery","core_form/events"],(function($,FormEvent){let focusedAlready=!1;return{enhance:function(elementid){var element=document.getElementById(elementid);if(element){element.addEventListener(FormEvent.eventTypes.formFieldValidationFailed,(e=>{const msg=e.detail.message;e.preventDefault();var parent=$(element).closest(".fitem"),feedback=parent.find(".form-control-feedback");const feedbackId=feedback.attr("id");let describedBy=$(element).attr("aria-describedby");void 0===describedBy&&(describedBy="");let describedByIds=[];describedBy.length&&(describedByIds=describedBy.split(" "));const feedbackIndex=describedByIds.indexOf(feedbackId);"TEXTAREA"==$(element).prop("tagName")&&parent.find("[contenteditable]").length>0&&(element=parent.find("[contenteditable]")),""!==msg?(parent.addClass("has-danger"),parent.data("client-validation-error",!0),$(element).addClass("is-invalid"),-1===feedbackIndex&&(describedByIds.push(feedbackId),$(element).attr("aria-describedby",describedByIds.join(" "))),$(element).attr("aria-invalid",!0),feedback.html(msg),feedback.show(),focusedAlready||(element.scrollIntoView({behavior:"smooth",block:"center"}),focusedAlready=!0,setTimeout((()=>{element.focus({preventScroll:!0}),focusedAlready=!1}),0))):!0===parent.data("client-validation-error")&&(parent.removeClass("has-danger"),parent.data("client-validation-error",!1),$(element).removeClass("is-invalid"),feedbackIndex>-1&&describedByIds.splice(feedbackIndex,1),describedByIds.length?(describedBy=describedByIds.join(" "),$(element).attr("aria-describedby",describedBy)):$(element).removeAttr("aria-describedby"),$(element).attr("aria-invalid",!1),feedback.hide())}));var form=element.closest("form");form&&!("boostFormErrorsEnhanced"in form.dataset)&&(form.addEventListener("submit",(function(){var visibleError=$(".form-control-feedback:visible");visibleError.length&&visibleError[0].focus()})),form.dataset.boostFormErrorsEnhanced=1)}}}})); +define("theme_boost/form-display-errors",["jquery","core_form/events"],(function($,FormEvent){let focusedAlready=!1;return{enhance:function(elementid){var element=document.getElementById(elementid);if(element){element.addEventListener(FormEvent.eventTypes.formFieldValidationFailed,(e=>{const msg=e.detail.message;e.preventDefault();var parent=$(element).closest(".fitem"),feedback=parent.find(".form-control-feedback");const feedbackId=feedback.attr("id");let describedBy=$(element).attr("aria-describedby");void 0===describedBy&&(describedBy="");let describedByIds=[];describedBy.length&&(describedByIds=describedBy.split(" "));const feedbackIndex=describedByIds.indexOf(feedbackId);if("TEXTAREA"===element.tagName){const contentEditable=parent.find("[contenteditable]");element=contentEditable.length>0?contentEditable[0]:document.getElementById("".concat(element.id,"_ifr"))||element}""!==msg?(parent.addClass("has-danger"),parent.data("client-validation-error",!0),$(element).addClass("is-invalid"),-1===feedbackIndex&&(describedByIds.push(feedbackId),$(element).attr("aria-describedby",describedByIds.join(" "))),$(element).attr("aria-invalid",!0),feedback.html(msg),feedback.show(),focusedAlready||(element.scrollIntoView({behavior:"smooth",block:"center"}),focusedAlready=!0,setTimeout((()=>{element.focus({preventScroll:!0}),focusedAlready=!1}),0))):!0===parent.data("client-validation-error")&&(parent.removeClass("has-danger"),parent.data("client-validation-error",!1),$(element).removeClass("is-invalid"),feedbackIndex>-1&&describedByIds.splice(feedbackIndex,1),describedByIds.length?(describedBy=describedByIds.join(" "),$(element).attr("aria-describedby",describedBy)):$(element).removeAttr("aria-describedby"),$(element).attr("aria-invalid",!1),feedback.hide())}));var form=element.closest("form");form&&!("boostFormErrorsEnhanced"in form.dataset)&&(form.addEventListener("submit",(function(){var visibleError=$(".form-control-feedback:visible");visibleError.length&&visibleError[0].focus()})),form.dataset.boostFormErrorsEnhanced=1)}}}})); //# sourceMappingURL=form-display-errors.min.js.map \ No newline at end of file diff --git a/theme/boost/amd/build/form-display-errors.min.js.map b/theme/boost/amd/build/form-display-errors.min.js.map index 923acaef301..81641f7921a 100644 --- a/theme/boost/amd/build/form-display-errors.min.js.map +++ b/theme/boost/amd/build/form-display-errors.min.js.map @@ -1 +1 @@ -{"version":3,"file":"form-display-errors.min.js","sources":["../src/form-display-errors.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 * Custom form error event handler to manipulate the bootstrap markup and show\n * nicely styled errors in an mform.\n *\n * @module theme_boost/form-display-errors\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core_form/events'], function($, FormEvent) {\n let focusedAlready = false;\n return {\n /**\n * Enhance the supplied element to handle form field errors.\n *\n * @method\n * @param {String} elementid\n * @listens event:formFieldValidationFailed\n */\n enhance: function(elementid) {\n var element = document.getElementById(elementid);\n if (!element) {\n // Some elements (e.g. static) don't have a form field.\n // Hence there is no validation. So, no setup required here.\n return;\n }\n\n element.addEventListener(FormEvent.eventTypes.formFieldValidationFailed, e => {\n const msg = e.detail.message;\n e.preventDefault();\n\n var parent = $(element).closest('.fitem');\n var feedback = parent.find('.form-control-feedback');\n const feedbackId = feedback.attr('id');\n\n // Get current aria-describedby value.\n let describedBy = $(element).attr('aria-describedby');\n if (typeof describedBy === \"undefined\") {\n describedBy = '';\n }\n // Split aria-describedby attribute into an array of IDs if necessary.\n let describedByIds = [];\n if (describedBy.length) {\n describedByIds = describedBy.split(\" \");\n }\n // Find the the feedback container in the aria-describedby attribute.\n const feedbackIndex = describedByIds.indexOf(feedbackId);\n\n // Sometimes (atto) we have a hidden textarea backed by a real contenteditable div.\n if (($(element).prop(\"tagName\") == 'TEXTAREA') && parent.find('[contenteditable]').length > 0) {\n element = parent.find('[contenteditable]');\n }\n if (msg !== '') {\n parent.addClass('has-danger');\n parent.data('client-validation-error', true);\n $(element).addClass('is-invalid');\n // Append the feedback ID to the aria-describedby attribute if it doesn't exist yet.\n if (feedbackIndex === -1) {\n describedByIds.push(feedbackId);\n $(element).attr('aria-describedby', describedByIds.join(\" \"));\n }\n $(element).attr('aria-invalid', true);\n feedback.html(msg);\n feedback.show();\n\n // If we haven't focused anything yet, focus this one.\n if (!focusedAlready) {\n element.scrollIntoView({behavior: \"smooth\", block: \"center\"});\n focusedAlready = true;\n setTimeout(()=> {\n // Actual focus happens later in case we need to do this in response to\n // a change event which happens in the middle of changing focus.\n element.focus({preventScroll: true});\n // Let it focus again next time they submit the form.\n focusedAlready = false;\n }, 0);\n }\n\n } else {\n if (parent.data('client-validation-error') === true) {\n parent.removeClass('has-danger');\n parent.data('client-validation-error', false);\n $(element).removeClass('is-invalid');\n // If the aria-describedby attribute contains the error container's ID, remove it.\n if (feedbackIndex > -1) {\n describedByIds.splice(feedbackIndex, 1);\n }\n // Check the remaining element IDs in the aria-describedby attribute.\n if (describedByIds.length) {\n // If there's at least one, combine them with a blank space and update the aria-describedby attribute.\n describedBy = describedByIds.join(\" \");\n // Put back the new describedby attribute.\n $(element).attr('aria-describedby', describedBy);\n } else {\n // If there's none, remove the aria-describedby attribute.\n $(element).removeAttr('aria-describedby');\n }\n $(element).attr('aria-invalid', false);\n feedback.hide();\n }\n }\n });\n\n var form = element.closest('form');\n if (form && !('boostFormErrorsEnhanced' in form.dataset)) {\n form.addEventListener('submit', function() {\n var visibleError = $('.form-control-feedback:visible');\n if (visibleError.length) {\n visibleError[0].focus();\n }\n });\n form.dataset.boostFormErrorsEnhanced = 1;\n }\n }\n };\n});\n"],"names":["define","$","FormEvent","focusedAlready","enhance","elementid","element","document","getElementById","addEventListener","eventTypes","formFieldValidationFailed","e","msg","detail","message","preventDefault","parent","closest","feedback","find","feedbackId","attr","describedBy","describedByIds","length","split","feedbackIndex","indexOf","prop","addClass","data","push","join","html","show","scrollIntoView","behavior","block","setTimeout","focus","preventScroll","removeClass","splice","removeAttr","hide","form","dataset","visibleError","boostFormErrorsEnhanced"],"mappings":";;;;;;;;AAuBAA,yCAAO,CAAC,SAAU,qBAAqB,SAASC,EAAGC,eAC3CC,gBAAiB,QACd,CAQHC,QAAS,SAASC,eACVC,QAAUC,SAASC,eAAeH,cACjCC,SAMLA,QAAQG,iBAAiBP,UAAUQ,WAAWC,2BAA2BC,UAC/DC,IAAMD,EAAEE,OAAOC,QACrBH,EAAEI,qBAEEC,OAAShB,EAAEK,SAASY,QAAQ,UAC5BC,SAAWF,OAAOG,KAAK,gCACrBC,WAAaF,SAASG,KAAK,UAG7BC,YAActB,EAAEK,SAASgB,KAAK,yBACP,IAAhBC,cACPA,YAAc,QAGdC,eAAiB,GACjBD,YAAYE,SACZD,eAAiBD,YAAYG,MAAM,YAGjCC,cAAgBH,eAAeI,QAAQP,YAGV,YAA9BpB,EAAEK,SAASuB,KAAK,YAA6BZ,OAAOG,KAAK,qBAAqBK,OAAS,IACxFnB,QAAUW,OAAOG,KAAK,sBAEd,KAARP,KACAI,OAAOa,SAAS,cAChBb,OAAOc,KAAK,2BAA2B,GACvC9B,EAAEK,SAASwB,SAAS,eAEG,IAAnBH,gBACAH,eAAeQ,KAAKX,YACpBpB,EAAEK,SAASgB,KAAK,mBAAoBE,eAAeS,KAAK,OAE5DhC,EAAEK,SAASgB,KAAK,gBAAgB,GAChCH,SAASe,KAAKrB,KACdM,SAASgB,OAGJhC,iBACDG,QAAQ8B,eAAe,CAACC,SAAU,SAAUC,MAAO,WACnDnC,gBAAiB,EACjBoC,YAAW,KAGPjC,QAAQkC,MAAM,CAACC,eAAe,IAE9BtC,gBAAiB,IAClB,MAIwC,IAA3Cc,OAAOc,KAAK,6BACZd,OAAOyB,YAAY,cACnBzB,OAAOc,KAAK,2BAA2B,GACvC9B,EAAEK,SAASoC,YAAY,cAEnBf,eAAiB,GACjBH,eAAemB,OAAOhB,cAAe,GAGrCH,eAAeC,QAEfF,YAAcC,eAAeS,KAAK,KAElChC,EAAEK,SAASgB,KAAK,mBAAoBC,cAGpCtB,EAAEK,SAASsC,WAAW,oBAE1B3C,EAAEK,SAASgB,KAAK,gBAAgB,GAChCH,SAAS0B,eAKjBC,KAAOxC,QAAQY,QAAQ,QACvB4B,QAAU,4BAA6BA,KAAKC,WAC5CD,KAAKrC,iBAAiB,UAAU,eACxBuC,aAAe/C,EAAE,kCACjB+C,aAAavB,QACbuB,aAAa,GAAGR,WAGxBM,KAAKC,QAAQE,wBAA0B"} \ No newline at end of file +{"version":3,"file":"form-display-errors.min.js","sources":["../src/form-display-errors.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 * Custom form error event handler to manipulate the bootstrap markup and show\n * nicely styled errors in an mform.\n *\n * @module theme_boost/form-display-errors\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core_form/events'], function($, FormEvent) {\n let focusedAlready = false;\n return {\n /**\n * Enhance the supplied element to handle form field errors.\n *\n * @method\n * @param {String} elementid\n * @listens event:formFieldValidationFailed\n */\n enhance: function(elementid) {\n var element = document.getElementById(elementid);\n if (!element) {\n // Some elements (e.g. static) don't have a form field.\n // Hence there is no validation. So, no setup required here.\n return;\n }\n\n element.addEventListener(FormEvent.eventTypes.formFieldValidationFailed, e => {\n const msg = e.detail.message;\n e.preventDefault();\n\n var parent = $(element).closest('.fitem');\n var feedback = parent.find('.form-control-feedback');\n const feedbackId = feedback.attr('id');\n\n // Get current aria-describedby value.\n let describedBy = $(element).attr('aria-describedby');\n if (typeof describedBy === \"undefined\") {\n describedBy = '';\n }\n // Split aria-describedby attribute into an array of IDs if necessary.\n let describedByIds = [];\n if (describedBy.length) {\n describedByIds = describedBy.split(\" \");\n }\n // Find the the feedback container in the aria-describedby attribute.\n const feedbackIndex = describedByIds.indexOf(feedbackId);\n\n if (element.tagName === 'TEXTAREA') {\n // Check if the textarea is backed by a contenteditable div.\n const contentEditable = parent.find('[contenteditable]');\n if (contentEditable.length > 0) {\n // Use the contenteditable div as the target element.\n element = contentEditable[0];\n } else {\n // Use the TinyMCE iframe as the target element if it exists.\n element = document.getElementById(`${element.id}_ifr`) || element;\n }\n }\n\n if (msg !== '') {\n parent.addClass('has-danger');\n parent.data('client-validation-error', true);\n $(element).addClass('is-invalid');\n // Append the feedback ID to the aria-describedby attribute if it doesn't exist yet.\n if (feedbackIndex === -1) {\n describedByIds.push(feedbackId);\n $(element).attr('aria-describedby', describedByIds.join(\" \"));\n }\n $(element).attr('aria-invalid', true);\n feedback.html(msg);\n feedback.show();\n\n // If we haven't focused anything yet, focus this one.\n if (!focusedAlready) {\n element.scrollIntoView({behavior: \"smooth\", block: \"center\"});\n focusedAlready = true;\n setTimeout(()=> {\n // Actual focus happens later in case we need to do this in response to\n // a change event which happens in the middle of changing focus.\n element.focus({preventScroll: true});\n // Let it focus again next time they submit the form.\n focusedAlready = false;\n }, 0);\n }\n\n } else {\n if (parent.data('client-validation-error') === true) {\n parent.removeClass('has-danger');\n parent.data('client-validation-error', false);\n $(element).removeClass('is-invalid');\n // If the aria-describedby attribute contains the error container's ID, remove it.\n if (feedbackIndex > -1) {\n describedByIds.splice(feedbackIndex, 1);\n }\n // Check the remaining element IDs in the aria-describedby attribute.\n if (describedByIds.length) {\n // If there's at least one, combine them with a blank space and update the aria-describedby attribute.\n describedBy = describedByIds.join(\" \");\n // Put back the new describedby attribute.\n $(element).attr('aria-describedby', describedBy);\n } else {\n // If there's none, remove the aria-describedby attribute.\n $(element).removeAttr('aria-describedby');\n }\n $(element).attr('aria-invalid', false);\n feedback.hide();\n }\n }\n });\n\n var form = element.closest('form');\n if (form && !('boostFormErrorsEnhanced' in form.dataset)) {\n form.addEventListener('submit', function() {\n var visibleError = $('.form-control-feedback:visible');\n if (visibleError.length) {\n visibleError[0].focus();\n }\n });\n form.dataset.boostFormErrorsEnhanced = 1;\n }\n }\n };\n});\n"],"names":["define","$","FormEvent","focusedAlready","enhance","elementid","element","document","getElementById","addEventListener","eventTypes","formFieldValidationFailed","e","msg","detail","message","preventDefault","parent","closest","feedback","find","feedbackId","attr","describedBy","describedByIds","length","split","feedbackIndex","indexOf","tagName","contentEditable","id","addClass","data","push","join","html","show","scrollIntoView","behavior","block","setTimeout","focus","preventScroll","removeClass","splice","removeAttr","hide","form","dataset","visibleError","boostFormErrorsEnhanced"],"mappings":";;;;;;;;AAuBAA,yCAAO,CAAC,SAAU,qBAAqB,SAASC,EAAGC,eAC3CC,gBAAiB,QACd,CAQHC,QAAS,SAASC,eACVC,QAAUC,SAASC,eAAeH,cACjCC,SAMLA,QAAQG,iBAAiBP,UAAUQ,WAAWC,2BAA2BC,UAC/DC,IAAMD,EAAEE,OAAOC,QACrBH,EAAEI,qBAEEC,OAAShB,EAAEK,SAASY,QAAQ,UAC5BC,SAAWF,OAAOG,KAAK,gCACrBC,WAAaF,SAASG,KAAK,UAG7BC,YAActB,EAAEK,SAASgB,KAAK,yBACP,IAAhBC,cACPA,YAAc,QAGdC,eAAiB,GACjBD,YAAYE,SACZD,eAAiBD,YAAYG,MAAM,YAGjCC,cAAgBH,eAAeI,QAAQP,eAErB,aAApBf,QAAQuB,QAAwB,OAE1BC,gBAAkBb,OAAOG,KAAK,qBAGhCd,QAFAwB,gBAAgBL,OAAS,EAEfK,gBAAgB,GAGhBvB,SAASC,yBAAkBF,QAAQyB,aAAazB,QAItD,KAARO,KACAI,OAAOe,SAAS,cAChBf,OAAOgB,KAAK,2BAA2B,GACvChC,EAAEK,SAAS0B,SAAS,eAEG,IAAnBL,gBACAH,eAAeU,KAAKb,YACpBpB,EAAEK,SAASgB,KAAK,mBAAoBE,eAAeW,KAAK,OAE5DlC,EAAEK,SAASgB,KAAK,gBAAgB,GAChCH,SAASiB,KAAKvB,KACdM,SAASkB,OAGJlC,iBACDG,QAAQgC,eAAe,CAACC,SAAU,SAAUC,MAAO,WACnDrC,gBAAiB,EACjBsC,YAAW,KAGPnC,QAAQoC,MAAM,CAACC,eAAe,IAE9BxC,gBAAiB,IAClB,MAIwC,IAA3Cc,OAAOgB,KAAK,6BACZhB,OAAO2B,YAAY,cACnB3B,OAAOgB,KAAK,2BAA2B,GACvChC,EAAEK,SAASsC,YAAY,cAEnBjB,eAAiB,GACjBH,eAAeqB,OAAOlB,cAAe,GAGrCH,eAAeC,QAEfF,YAAcC,eAAeW,KAAK,KAElClC,EAAEK,SAASgB,KAAK,mBAAoBC,cAGpCtB,EAAEK,SAASwC,WAAW,oBAE1B7C,EAAEK,SAASgB,KAAK,gBAAgB,GAChCH,SAAS4B,eAKjBC,KAAO1C,QAAQY,QAAQ,QACvB8B,QAAU,4BAA6BA,KAAKC,WAC5CD,KAAKvC,iBAAiB,UAAU,eACxByC,aAAejD,EAAE,kCACjBiD,aAAazB,QACbyB,aAAa,GAAGR,WAGxBM,KAAKC,QAAQE,wBAA0B"} \ No newline at end of file diff --git a/theme/boost/amd/src/form-display-errors.js b/theme/boost/amd/src/form-display-errors.js index a6b5fb558e2..7be614d608d 100644 --- a/theme/boost/amd/src/form-display-errors.js +++ b/theme/boost/amd/src/form-display-errors.js @@ -60,10 +60,18 @@ define(['jquery', 'core_form/events'], function($, FormEvent) { // Find the the feedback container in the aria-describedby attribute. const feedbackIndex = describedByIds.indexOf(feedbackId); - // Sometimes (atto) we have a hidden textarea backed by a real contenteditable div. - if (($(element).prop("tagName") == 'TEXTAREA') && parent.find('[contenteditable]').length > 0) { - element = parent.find('[contenteditable]'); + if (element.tagName === 'TEXTAREA') { + // Check if the textarea is backed by a contenteditable div. + const contentEditable = parent.find('[contenteditable]'); + if (contentEditable.length > 0) { + // Use the contenteditable div as the target element. + element = contentEditable[0]; + } else { + // Use the TinyMCE iframe as the target element if it exists. + element = document.getElementById(`${element.id}_ifr`) || element; + } } + if (msg !== '') { parent.addClass('has-danger'); parent.data('client-validation-error', true);