From 1a32074522e1b04f347c82e63e8bbf4d970387e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikel=20Mart=C3=ADn?= Date: Wed, 27 Nov 2024 09:59:07 +0100 Subject: [PATCH] MDL-75669 theme_boost: Fix Bootstrap related JS --- .../content/library/moodle-javascript.md | 6 +- .../moodlenet/amd/build/instance_form.min.js | 2 +- .../amd/build/instance_form.min.js.map | 2 +- admin/tool/moodlenet/amd/src/instance_form.js | 26 ++--- blocks/timeline/amd/build/view_nav.min.js | 2 +- blocks/timeline/amd/build/view_nav.min.js.map | 2 +- blocks/timeline/amd/src/view_nav.js | 2 +- calendar/amd/build/popover.min.js | 4 +- calendar/amd/build/popover.min.js.map | 2 +- calendar/amd/src/popover.js | 15 ++- .../templates/minicalendar_day_link.mustache | 4 +- .../local/activitychooser/dialogue.min.js | 2 +- .../local/activitychooser/dialogue.min.js.map | 2 +- .../amd/src/local/activitychooser/dialogue.js | 68 ++++++------- course/format/amd/build/local/content.min.js | 4 +- .../format/amd/build/local/content.min.js.map | 2 +- .../amd/build/local/content/actions.min.js | 4 +- .../build/local/content/actions.min.js.map | 2 +- .../local/courseeditor/contenttree.min.js | 4 +- .../local/courseeditor/contenttree.min.js.map | 2 +- .../local/courseindex/courseindex.min.js | 4 +- .../local/courseindex/courseindex.min.js.map | 2 +- course/format/amd/src/local/content.js | 13 ++- .../format/amd/src/local/content/actions.js | 10 +- .../amd/src/local/courseeditor/contenttree.js | 16 ++-- .../amd/src/local/courseindex/courseindex.js | 12 +-- lib/amd/build/dynamic_tabs.min.js | 2 +- lib/amd/build/dynamic_tabs.min.js.map | 2 +- lib/amd/build/moremenu.min.js | 2 +- lib/amd/build/moremenu.min.js.map | 2 +- lib/amd/src/dynamic_tabs.js | 61 ++++++------ lib/amd/src/moremenu.js | 2 +- lib/form/amd/build/collapsesections.min.js | 4 +- .../amd/build/collapsesections.min.js.map | 2 +- lib/form/amd/src/collapsesections.js | 40 ++++---- lib/templates/help_icon.mustache | 6 +- lib/templates/local/toast/message.mustache | 11 ++- .../amd/build/newchild.min.js | 2 +- .../amd/build/newchild.min.js.map | 2 +- .../bank/managecategories/amd/src/newchild.js | 6 +- .../multianswer/amd/build/feedback.min.js | 4 +- .../multianswer/amd/build/feedback.min.js.map | 2 +- question/type/multianswer/amd/src/feedback.js | 5 +- theme/boost/amd/build/aria.min.js | 4 +- theme/boost/amd/build/aria.min.js.map | 2 +- theme/boost/amd/build/drawers.min.js | 2 +- theme/boost/amd/build/drawers.min.js.map | 2 +- theme/boost/amd/build/footer-popover.min.js | 4 +- .../boost/amd/build/footer-popover.min.js.map | 2 +- theme/boost/amd/build/loader.min.js | 4 +- theme/boost/amd/build/loader.min.js.map | 2 +- theme/boost/amd/src/aria.js | 3 +- theme/boost/amd/src/drawers.js | 21 ++-- theme/boost/amd/src/footer-popover.js | 16 ++-- theme/boost/amd/src/loader.js | 96 +++++++++---------- theme/boost/templates/drawer.mustache | 4 +- theme/boost/templates/drawers.mustache | 8 +- 57 files changed, 263 insertions(+), 276 deletions(-) diff --git a/admin/tool/componentlibrary/content/library/moodle-javascript.md b/admin/tool/componentlibrary/content/library/moodle-javascript.md index b4921ffe14112..4bbc01a90d76b 100644 --- a/admin/tool/componentlibrary/content/library/moodle-javascript.md +++ b/admin/tool/componentlibrary/content/library/moodle-javascript.md @@ -30,15 +30,13 @@ In order for this to work you need to use the JavaScript syntax used in core Mus {{#js}} require( [ - 'jquery', 'theme_boost/toast', ], function( - $, Toast ) { - var root = $('#toasttest'); - root.toast('show'); + const toastTrigger = document.getElementById('toasttest'); + new Toast(toastTrigger).show(); }); {{/js}} {{< /example >}} \ No newline at end of file diff --git a/admin/tool/moodlenet/amd/build/instance_form.min.js b/admin/tool/moodlenet/amd/build/instance_form.min.js index 6b6ebb2ff2934..c9972ee0c0981 100644 --- a/admin/tool/moodlenet/amd/build/instance_form.min.js +++ b/admin/tool/moodlenet/amd/build/instance_form.min.js @@ -12,6 +12,6 @@ * @copyright 2020 Mathew May * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("tool_moodlenet/instance_form",["tool_moodlenet/validator","tool_moodlenet/selectors","core/loadingicon","core/templates","core/notification","jquery"],(function(Validator,Selectors,LoadingIcon,Templates,Notification,$){var chooserNavigateToMnet=function(showMoodleNet,footerData,carousel,modal){showMoodleNet.innerHTML="";var page,spinnerPromise=LoadingIcon.addIconToContainer(showMoodleNet),transitionPromiseResolver=null,transitionPromise=new Promise((resolve=>{transitionPromiseResolver=resolve}));$.when(spinnerPromise,transitionPromise).then((function(){Templates.replaceNodeContents(showMoodleNet,footerData.customcarouseltemplate,"")})).catch(Notification.exception),(page=showMoodleNet).addEventListener("click",(function(e){if(e.target.matches(Selectors.action.submit)){var input=page.querySelector('[data-var="mnet-link"]'),overlay=page.querySelector(Selectors.region.spinner),validationArea=document.querySelector(Selectors.region.validationArea);overlay.classList.remove("d-none");var spinner=LoadingIcon.addIconToContainerWithPromise(overlay);Validator.validation(input).then((function(result){spinner.resolve(),overlay.classList.add("d-none"),result.result?(input.classList.remove("is-invalid"),input.classList.add("is-valid"),validationArea.innerText=result.message,validationArea.classList.remove("text-danger"),validationArea.classList.add("text-success"),setTimeout((function(){window.location=result.domain}),1e3)):(input.classList.add("is-invalid"),validationArea.innerText=result.message,validationArea.classList.add("text-danger"))})).catch()}})),carousel.one("slid.bs.carousel",(function(){transitionPromiseResolver()})),carousel.carousel(2),modal.setFooter(Templates.render("tool_moodlenet/chooser_footer_close_mnet",{}))};return{footerClickListener:function(e,footerData,modal){if(e.target.matches(Selectors.action.showMoodleNet)||e.target.closest(Selectors.action.showMoodleNet)){e.preventDefault();const carousel=$(modal.getBody()[0].querySelector(Selectors.region.carousel)),showMoodleNet=carousel.find(Selectors.region.moodleNet)[0];chooserNavigateToMnet(showMoodleNet,footerData,carousel,modal)}if(e.target.matches(Selectors.action.closeOption)){!function(carousel,modal,footerData){carousel.carousel(0),modal.setFooter(footerData.customfootertemplate)}($(modal.getBody()[0].querySelector(Selectors.region.carousel)),modal,footerData)}}}})); +define("tool_moodlenet/instance_form",["tool_moodlenet/validator","tool_moodlenet/selectors","core/loadingicon","core/templates","core/notification","jquery","theme_boost/bootstrap/carousel"],(function(Validator,Selectors,LoadingIcon,Templates,Notification,$,Carousel){var chooserNavigateToMnet=function(showMoodleNet,footerData,carousel,modal){showMoodleNet.innerHTML="";var page,spinnerPromise=LoadingIcon.addIconToContainer(showMoodleNet),transitionPromiseResolver=null,transitionPromise=new Promise((resolve=>{transitionPromiseResolver=resolve}));$.when(spinnerPromise,transitionPromise).then((function(){Templates.replaceNodeContents(showMoodleNet,footerData.customcarouseltemplate,"")})).catch(Notification.exception),(page=showMoodleNet).addEventListener("click",(function(e){if(e.target.matches(Selectors.action.submit)){var input=page.querySelector('[data-var="mnet-link"]'),overlay=page.querySelector(Selectors.region.spinner),validationArea=document.querySelector(Selectors.region.validationArea);overlay.classList.remove("d-none");var spinner=LoadingIcon.addIconToContainerWithPromise(overlay);Validator.validation(input).then((function(result){spinner.resolve(),overlay.classList.add("d-none"),result.result?(input.classList.remove("is-invalid"),input.classList.add("is-valid"),validationArea.innerText=result.message,validationArea.classList.remove("text-danger"),validationArea.classList.add("text-success"),setTimeout((function(){window.location=result.domain}),1e3)):(input.classList.add("is-invalid"),validationArea.innerText=result.message,validationArea.classList.add("text-danger"))})).catch(Notification.exception)}})),carousel.addEventListener("slid.bs.carousel",(function(){transitionPromiseResolver()}),{once:!0}),Carousel.getInstance(carousel).to(2),modal.setFooter(Templates.render("tool_moodlenet/chooser_footer_close_mnet",{}))};return{footerClickListener:function(e,footerData,modal){if(e.target.matches(Selectors.action.showMoodleNet)||e.target.closest(Selectors.action.showMoodleNet)){e.preventDefault();const carousel=modal.getBody()[0].querySelector(Selectors.region.carousel),showMoodleNet=carousel.querySelector(Selectors.region.moodleNet);chooserNavigateToMnet(showMoodleNet,footerData,carousel,modal)}if(e.target.matches(Selectors.action.closeOption)){!function(carousel,modal,footerData){Carousel.getInstance(carousel).to(0),modal.setFooter(footerData.customfootertemplate)}(modal.getBody()[0].querySelector(Selectors.region.carousel),modal,footerData)}}}})); //# sourceMappingURL=instance_form.min.js.map \ No newline at end of file diff --git a/admin/tool/moodlenet/amd/build/instance_form.min.js.map b/admin/tool/moodlenet/amd/build/instance_form.min.js.map index 821603c3ba921..dcd232d83f9fb 100644 --- a/admin/tool/moodlenet/amd/build/instance_form.min.js.map +++ b/admin/tool/moodlenet/amd/build/instance_form.min.js.map @@ -1 +1 @@ -{"version":3,"file":"instance_form.min.js","sources":["../src/instance_form.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 * Our basic form manager for when a user either enters\n * their profile url or just wants to browse.\n *\n * This file is a mishmash of JS functions we need for both the standalone (M3.7, M3.8)\n * plugin & Moodle 3.9 functions. The 3.9 Functions have a base understanding that certain\n * things exist i.e. directory structures for templates. When this feature goes 3.9+ only\n * The goal is that we can quickly gut all AMD modules into bare JS files and use ES6 guidelines.\n * Till then this will have to do.\n *\n * @module tool_moodlenet/instance_form\n * @copyright 2020 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['tool_moodlenet/validator',\n 'tool_moodlenet/selectors',\n 'core/loadingicon',\n 'core/templates',\n 'core/notification',\n 'jquery'],\n function(Validator,\n Selectors,\n LoadingIcon,\n Templates,\n Notification,\n $) {\n\n /**\n * Add the event listeners to our form.\n *\n * @method registerListenerEvents\n * @param {HTMLElement} page The whole page element for our form area\n */\n var registerListenerEvents = function registerListenerEvents(page) {\n page.addEventListener('click', function(e) {\n\n // Our fake submit button / browse button.\n if (e.target.matches(Selectors.action.submit)) {\n var input = page.querySelector('[data-var=\"mnet-link\"]');\n var overlay = page.querySelector(Selectors.region.spinner);\n var validationArea = document.querySelector(Selectors.region.validationArea);\n\n overlay.classList.remove('d-none');\n var spinner = LoadingIcon.addIconToContainerWithPromise(overlay);\n Validator.validation(input)\n .then(function(result) {\n spinner.resolve();\n overlay.classList.add('d-none');\n if (result.result) {\n input.classList.remove('is-invalid'); // Just in case the class has been applied already.\n input.classList.add('is-valid');\n validationArea.innerText = result.message;\n validationArea.classList.remove('text-danger');\n validationArea.classList.add('text-success');\n // Give the user some time to see their input is valid.\n setTimeout(function() {\n window.location = result.domain;\n }, 1000);\n } else {\n input.classList.add('is-invalid');\n validationArea.innerText = result.message;\n validationArea.classList.add('text-danger');\n }\n return;\n }).catch();\n }\n });\n };\n\n /**\n * Given a user wishes to see the MoodleNet profile url form transition them there.\n *\n * @method chooserNavigateToMnet\n * @param {HTMLElement} showMoodleNet The chooser's area for ment\n * @param {Object} footerData Our footer object to render out\n * @param {jQuery} carousel Our carousel instance to manage\n * @param {jQuery} modal Our modal instance to manage\n */\n var chooserNavigateToMnet = function(showMoodleNet, footerData, carousel, modal) {\n showMoodleNet.innerHTML = '';\n\n // Add a spinner.\n var spinnerPromise = LoadingIcon.addIconToContainer(showMoodleNet);\n\n // Used later...\n var transitionPromiseResolver = null;\n var transitionPromise = new Promise(resolve => {\n transitionPromiseResolver = resolve;\n });\n\n $.when(\n spinnerPromise,\n transitionPromise\n ).then(function() {\n Templates.replaceNodeContents(showMoodleNet, footerData.customcarouseltemplate, '');\n return;\n }).catch(Notification.exception);\n\n // We apply our handlers in here to minimise plugin dependency in the Chooser.\n registerListenerEvents(showMoodleNet);\n\n // Move to the next slide, and resolve the transition promise when it's done.\n carousel.one('slid.bs.carousel', function() {\n transitionPromiseResolver();\n });\n // Trigger the transition between 'pages'.\n carousel.carousel(2);\n modal.setFooter(Templates.render('tool_moodlenet/chooser_footer_close_mnet', {}));\n };\n\n /**\n * Given a user no longer wishes to see the MoodleNet profile url form transition them from there.\n *\n * @method chooserNavigateFromMnet\n * @param {jQuery} carousel Our carousel instance to manage\n * @param {jQuery} modal Our modal instance to manage\n * @param {Object} footerData Our footer object to render out\n */\n var chooserNavigateFromMnet = function(carousel, modal, footerData) {\n // Trigger the transition between 'pages'.\n carousel.carousel(0);\n modal.setFooter(footerData.customfootertemplate);\n };\n\n /**\n * Create the custom listener that would handle anything in the footer.\n *\n * @param {Event} e The event being triggered.\n * @param {Object} footerData The data generated from the exporter.\n * @param {Object} modal The chooser modal.\n */\n var footerClickListener = function(e, footerData, modal) {\n if (e.target.matches(Selectors.action.showMoodleNet) || e.target.closest(Selectors.action.showMoodleNet)) {\n e.preventDefault();\n const carousel = $(modal.getBody()[0].querySelector(Selectors.region.carousel));\n const showMoodleNet = carousel.find(Selectors.region.moodleNet)[0];\n\n chooserNavigateToMnet(showMoodleNet, footerData, carousel, modal);\n }\n // From the help screen go back to the module overview.\n if (e.target.matches(Selectors.action.closeOption)) {\n const carousel = $(modal.getBody()[0].querySelector(Selectors.region.carousel));\n\n chooserNavigateFromMnet(carousel, modal, footerData);\n }\n };\n\n return {\n footerClickListener: footerClickListener\n };\n});\n"],"names":["define","Validator","Selectors","LoadingIcon","Templates","Notification","$","chooserNavigateToMnet","showMoodleNet","footerData","carousel","modal","innerHTML","page","spinnerPromise","addIconToContainer","transitionPromiseResolver","transitionPromise","Promise","resolve","when","then","replaceNodeContents","customcarouseltemplate","catch","exception","addEventListener","e","target","matches","action","submit","input","querySelector","overlay","region","spinner","validationArea","document","classList","remove","addIconToContainerWithPromise","validation","result","add","innerText","message","setTimeout","window","location","domain","one","setFooter","render","footerClickListener","closest","preventDefault","getBody","find","moodleNet","closeOption","customfootertemplate","chooserNavigateFromMnet"],"mappings":";;;;;;;;;;;;;;AA8BAA,sCAAO,CAAC,2BACA,2BACA,mBACA,iBACA,oBACA,WACJ,SAASC,UACAC,UACAC,YACAC,UACAC,aACAC,OAqDLC,sBAAwB,SAASC,cAAeC,WAAYC,SAAUC,OACtEH,cAAcI,UAAY,OA9C+BC,KAiDrDC,eAAiBX,YAAYY,mBAAmBP,eAGhDQ,0BAA4B,KAC5BC,kBAAoB,IAAIC,SAAQC,UAChCH,0BAA4BG,WAGhCb,EAAEc,KACEN,eACAG,mBACFI,MAAK,WACCjB,UAAUkB,oBAAoBd,cAAeC,WAAWc,uBAAwB,OAErFC,MAAMnB,aAAaoB,YA/DmCZ,KAkElCL,eAjElBkB,iBAAiB,SAAS,SAASC,MAGhCA,EAAEC,OAAOC,QAAQ3B,UAAU4B,OAAOC,QAAS,KACvCC,MAAQnB,KAAKoB,cAAc,0BAC3BC,QAAUrB,KAAKoB,cAAc/B,UAAUiC,OAAOC,SAC9CC,eAAiBC,SAASL,cAAc/B,UAAUiC,OAAOE,gBAE7DH,QAAQK,UAAUC,OAAO,cACrBJ,QAAUjC,YAAYsC,8BAA8BP,SACxDjC,UAAUyC,WAAWV,OAChBX,MAAK,SAASsB,QACXP,QAAQjB,UACRe,QAAQK,UAAUK,IAAI,UAClBD,OAAOA,QACPX,MAAMO,UAAUC,OAAO,cACvBR,MAAMO,UAAUK,IAAI,YACpBP,eAAeQ,UAAYF,OAAOG,QAClCT,eAAeE,UAAUC,OAAO,eAChCH,eAAeE,UAAUK,IAAI,gBAE7BG,YAAW,WACPC,OAAOC,SAAWN,OAAOO,SAC1B,OAEHlB,MAAMO,UAAUK,IAAI,cACpBP,eAAeQ,UAAYF,OAAOG,QAClCT,eAAeE,UAAUK,IAAI,mBAGtCpB,YAsCXd,SAASyC,IAAI,oBAAoB,WAC7BnC,+BAGJN,SAASA,SAAS,GAClBC,MAAMyC,UAAUhD,UAAUiD,OAAO,2CAA4C,YAwC1E,CACHC,oBAjBsB,SAAS3B,EAAGlB,WAAYE,UAC1CgB,EAAEC,OAAOC,QAAQ3B,UAAU4B,OAAOtB,gBAAkBmB,EAAEC,OAAO2B,QAAQrD,UAAU4B,OAAOtB,eAAgB,CACtGmB,EAAE6B,uBACI9C,SAAWJ,EAAEK,MAAM8C,UAAU,GAAGxB,cAAc/B,UAAUiC,OAAOzB,WAC/DF,cAAgBE,SAASgD,KAAKxD,UAAUiC,OAAOwB,WAAW,GAEhEpD,sBAAsBC,cAAeC,WAAYC,SAAUC,UAG3DgB,EAAEC,OAAOC,QAAQ3B,UAAU4B,OAAO8B,aAAc,EAtB1B,SAASlD,SAAUC,MAAOF,YAEpDC,SAASA,SAAS,GAClBC,MAAMyC,UAAU3C,WAAWoD,sBAsBvBC,CAFiBxD,EAAEK,MAAM8C,UAAU,GAAGxB,cAAc/B,UAAUiC,OAAOzB,WAEnCC,MAAOF"} \ No newline at end of file +{"version":3,"file":"instance_form.min.js","sources":["../src/instance_form.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 * Our basic form manager for when a user either enters\n * their profile url or just wants to browse.\n *\n * This file is a mishmash of JS functions we need for both the standalone (M3.7, M3.8)\n * plugin & Moodle 3.9 functions. The 3.9 Functions have a base understanding that certain\n * things exist i.e. directory structures for templates. When this feature goes 3.9+ only\n * The goal is that we can quickly gut all AMD modules into bare JS files and use ES6 guidelines.\n * Till then this will have to do.\n *\n * @module tool_moodlenet/instance_form\n * @copyright 2020 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['tool_moodlenet/validator',\n 'tool_moodlenet/selectors',\n 'core/loadingicon',\n 'core/templates',\n 'core/notification',\n 'jquery',\n 'theme_boost/bootstrap/carousel'],\n function(Validator,\n Selectors,\n LoadingIcon,\n Templates,\n Notification,\n $,\n Carousel) {\n\n /**\n * Add the event listeners to our form.\n *\n * @method registerListenerEvents\n * @param {HTMLElement} page The whole page element for our form area\n */\n var registerListenerEvents = function registerListenerEvents(page) {\n page.addEventListener('click', function(e) {\n\n // Our fake submit button / browse button.\n if (e.target.matches(Selectors.action.submit)) {\n var input = page.querySelector('[data-var=\"mnet-link\"]');\n var overlay = page.querySelector(Selectors.region.spinner);\n var validationArea = document.querySelector(Selectors.region.validationArea);\n\n overlay.classList.remove('d-none');\n var spinner = LoadingIcon.addIconToContainerWithPromise(overlay);\n Validator.validation(input)\n .then(function(result) {\n spinner.resolve();\n overlay.classList.add('d-none');\n if (result.result) {\n input.classList.remove('is-invalid'); // Just in case the class has been applied already.\n input.classList.add('is-valid');\n validationArea.innerText = result.message;\n validationArea.classList.remove('text-danger');\n validationArea.classList.add('text-success');\n // Give the user some time to see their input is valid.\n setTimeout(function() {\n window.location = result.domain;\n }, 1000);\n } else {\n input.classList.add('is-invalid');\n validationArea.innerText = result.message;\n validationArea.classList.add('text-danger');\n }\n return;\n }).catch(Notification.exception);\n }\n });\n };\n\n /**\n * Given a user wishes to see the MoodleNet profile url form transition them there.\n *\n * @method chooserNavigateToMnet\n * @param {HTMLElement} showMoodleNet The chooser's area for ment\n * @param {Object} footerData Our footer object to render out\n * @param {Element} carousel Our carousel instance to manage\n * @param {jQuery} modal Our modal instance to manage\n */\n var chooserNavigateToMnet = function(showMoodleNet, footerData, carousel, modal) {\n showMoodleNet.innerHTML = '';\n\n // Add a spinner.\n var spinnerPromise = LoadingIcon.addIconToContainer(showMoodleNet);\n\n // Used later...\n var transitionPromiseResolver = null;\n var transitionPromise = new Promise(resolve => {\n transitionPromiseResolver = resolve;\n });\n\n $.when(\n spinnerPromise,\n transitionPromise\n ).then(function() {\n Templates.replaceNodeContents(showMoodleNet, footerData.customcarouseltemplate, '');\n return;\n }).catch(Notification.exception);\n\n // We apply our handlers in here to minimise plugin dependency in the Chooser.\n registerListenerEvents(showMoodleNet);\n\n // Move to the next slide, and resolve the transition promise when it's done.\n carousel.addEventListener('slid.bs.carousel', function() {\n transitionPromiseResolver();\n }, {once: true});\n // Trigger the transition between 'pages'.\n Carousel.getInstance(carousel).to(2);\n modal.setFooter(Templates.render('tool_moodlenet/chooser_footer_close_mnet', {}));\n };\n\n /**\n * Given a user no longer wishes to see the MoodleNet profile url form transition them from there.\n *\n * @method chooserNavigateFromMnet\n * @param {Element} carousel Our carousel instance to manage\n * @param {jQuery} modal Our modal instance to manage\n * @param {Object} footerData Our footer object to render out\n */\n var chooserNavigateFromMnet = function(carousel, modal, footerData) {\n // Trigger the transition between 'pages'.\n Carousel.getInstance(carousel).to(0);\n modal.setFooter(footerData.customfootertemplate);\n };\n\n /**\n * Create the custom listener that would handle anything in the footer.\n *\n * @param {Event} e The event being triggered.\n * @param {Object} footerData The data generated from the exporter.\n * @param {Object} modal The chooser modal.\n */\n var footerClickListener = function(e, footerData, modal) {\n if (e.target.matches(Selectors.action.showMoodleNet) || e.target.closest(Selectors.action.showMoodleNet)) {\n e.preventDefault();\n const carousel = modal.getBody()[0].querySelector(Selectors.region.carousel);\n const showMoodleNet = carousel.querySelector(Selectors.region.moodleNet);\n\n chooserNavigateToMnet(showMoodleNet, footerData, carousel, modal);\n }\n // From the help screen go back to the module overview.\n if (e.target.matches(Selectors.action.closeOption)) {\n const carousel = modal.getBody()[0].querySelector(Selectors.region.carousel);\n\n chooserNavigateFromMnet(carousel, modal, footerData);\n }\n };\n\n return {\n footerClickListener: footerClickListener\n };\n});\n"],"names":["define","Validator","Selectors","LoadingIcon","Templates","Notification","$","Carousel","chooserNavigateToMnet","showMoodleNet","footerData","carousel","modal","innerHTML","page","spinnerPromise","addIconToContainer","transitionPromiseResolver","transitionPromise","Promise","resolve","when","then","replaceNodeContents","customcarouseltemplate","catch","exception","addEventListener","e","target","matches","action","submit","input","querySelector","overlay","region","spinner","validationArea","document","classList","remove","addIconToContainerWithPromise","validation","result","add","innerText","message","setTimeout","window","location","domain","once","getInstance","to","setFooter","render","footerClickListener","closest","preventDefault","getBody","moodleNet","closeOption","customfootertemplate","chooserNavigateFromMnet"],"mappings":";;;;;;;;;;;;;;AA8BAA,sCAAO,CAAC,2BACA,2BACA,mBACA,iBACA,oBACA,SACA,mCACJ,SAASC,UACAC,UACAC,YACAC,UACAC,aACAC,EACAC,cAqDLC,sBAAwB,SAASC,cAAeC,WAAYC,SAAUC,OACtEH,cAAcI,UAAY,OA9C+BC,KAiDrDC,eAAiBZ,YAAYa,mBAAmBP,eAGhDQ,0BAA4B,KAC5BC,kBAAoB,IAAIC,SAAQC,UAChCH,0BAA4BG,WAGhCd,EAAEe,KACEN,eACAG,mBACFI,MAAK,WACClB,UAAUmB,oBAAoBd,cAAeC,WAAWc,uBAAwB,OAErFC,MAAMpB,aAAaqB,YA/DmCZ,KAkElCL,eAjElBkB,iBAAiB,SAAS,SAASC,MAGhCA,EAAEC,OAAOC,QAAQ5B,UAAU6B,OAAOC,QAAS,KACvCC,MAAQnB,KAAKoB,cAAc,0BAC3BC,QAAUrB,KAAKoB,cAAchC,UAAUkC,OAAOC,SAC9CC,eAAiBC,SAASL,cAAchC,UAAUkC,OAAOE,gBAE7DH,QAAQK,UAAUC,OAAO,cACrBJ,QAAUlC,YAAYuC,8BAA8BP,SACxDlC,UAAU0C,WAAWV,OAChBX,MAAK,SAASsB,QACXP,QAAQjB,UACRe,QAAQK,UAAUK,IAAI,UAClBD,OAAOA,QACPX,MAAMO,UAAUC,OAAO,cACvBR,MAAMO,UAAUK,IAAI,YACpBP,eAAeQ,UAAYF,OAAOG,QAClCT,eAAeE,UAAUC,OAAO,eAChCH,eAAeE,UAAUK,IAAI,gBAE7BG,YAAW,WACPC,OAAOC,SAAWN,OAAOO,SAC1B,OAEHlB,MAAMO,UAAUK,IAAI,cACpBP,eAAeQ,UAAYF,OAAOG,QAClCT,eAAeE,UAAUK,IAAI,mBAGtCpB,MAAMpB,aAAaqB,eAsC9Bf,SAASgB,iBAAiB,oBAAoB,WAC1CV,8BACD,CAACmC,MAAM,IAEV7C,SAAS8C,YAAY1C,UAAU2C,GAAG,GAClC1C,MAAM2C,UAAUnD,UAAUoD,OAAO,2CAA4C,YAwC1E,CACHC,oBAjBsB,SAAS7B,EAAGlB,WAAYE,UAC1CgB,EAAEC,OAAOC,QAAQ5B,UAAU6B,OAAOtB,gBAAkBmB,EAAEC,OAAO6B,QAAQxD,UAAU6B,OAAOtB,eAAgB,CACtGmB,EAAE+B,uBACIhD,SAAWC,MAAMgD,UAAU,GAAG1B,cAAchC,UAAUkC,OAAOzB,UAC7DF,cAAgBE,SAASuB,cAAchC,UAAUkC,OAAOyB,WAE9DrD,sBAAsBC,cAAeC,WAAYC,SAAUC,UAG3DgB,EAAEC,OAAOC,QAAQ5B,UAAU6B,OAAO+B,aAAc,EAtB1B,SAASnD,SAAUC,MAAOF,YAEpDH,SAAS8C,YAAY1C,UAAU2C,GAAG,GAClC1C,MAAM2C,UAAU7C,WAAWqD,sBAsBvBC,CAFiBpD,MAAMgD,UAAU,GAAG1B,cAAchC,UAAUkC,OAAOzB,UAEjCC,MAAOF"} \ No newline at end of file diff --git a/admin/tool/moodlenet/amd/src/instance_form.js b/admin/tool/moodlenet/amd/src/instance_form.js index 57b4cd50159c7..fb5d40d64b716 100644 --- a/admin/tool/moodlenet/amd/src/instance_form.js +++ b/admin/tool/moodlenet/amd/src/instance_form.js @@ -33,13 +33,15 @@ define(['tool_moodlenet/validator', 'core/loadingicon', 'core/templates', 'core/notification', - 'jquery'], + 'jquery', + 'theme_boost/bootstrap/carousel'], function(Validator, Selectors, LoadingIcon, Templates, Notification, - $) { + $, + Carousel) { /** * Add the event listeners to our form. @@ -78,7 +80,7 @@ define(['tool_moodlenet/validator', validationArea.classList.add('text-danger'); } return; - }).catch(); + }).catch(Notification.exception); } }); }; @@ -89,7 +91,7 @@ define(['tool_moodlenet/validator', * @method chooserNavigateToMnet * @param {HTMLElement} showMoodleNet The chooser's area for ment * @param {Object} footerData Our footer object to render out - * @param {jQuery} carousel Our carousel instance to manage + * @param {Element} carousel Our carousel instance to manage * @param {jQuery} modal Our modal instance to manage */ var chooserNavigateToMnet = function(showMoodleNet, footerData, carousel, modal) { @@ -116,11 +118,11 @@ define(['tool_moodlenet/validator', registerListenerEvents(showMoodleNet); // Move to the next slide, and resolve the transition promise when it's done. - carousel.one('slid.bs.carousel', function() { + carousel.addEventListener('slid.bs.carousel', function() { transitionPromiseResolver(); - }); + }, {once: true}); // Trigger the transition between 'pages'. - carousel.carousel(2); + Carousel.getInstance(carousel).to(2); modal.setFooter(Templates.render('tool_moodlenet/chooser_footer_close_mnet', {})); }; @@ -128,13 +130,13 @@ define(['tool_moodlenet/validator', * Given a user no longer wishes to see the MoodleNet profile url form transition them from there. * * @method chooserNavigateFromMnet - * @param {jQuery} carousel Our carousel instance to manage + * @param {Element} carousel Our carousel instance to manage * @param {jQuery} modal Our modal instance to manage * @param {Object} footerData Our footer object to render out */ var chooserNavigateFromMnet = function(carousel, modal, footerData) { // Trigger the transition between 'pages'. - carousel.carousel(0); + Carousel.getInstance(carousel).to(0); modal.setFooter(footerData.customfootertemplate); }; @@ -148,14 +150,14 @@ define(['tool_moodlenet/validator', var footerClickListener = function(e, footerData, modal) { if (e.target.matches(Selectors.action.showMoodleNet) || e.target.closest(Selectors.action.showMoodleNet)) { e.preventDefault(); - const carousel = $(modal.getBody()[0].querySelector(Selectors.region.carousel)); - const showMoodleNet = carousel.find(Selectors.region.moodleNet)[0]; + const carousel = modal.getBody()[0].querySelector(Selectors.region.carousel); + const showMoodleNet = carousel.querySelector(Selectors.region.moodleNet); chooserNavigateToMnet(showMoodleNet, footerData, carousel, modal); } // From the help screen go back to the module overview. if (e.target.matches(Selectors.action.closeOption)) { - const carousel = $(modal.getBody()[0].querySelector(Selectors.region.carousel)); + const carousel = modal.getBody()[0].querySelector(Selectors.region.carousel); chooserNavigateFromMnet(carousel, modal, footerData); } diff --git a/blocks/timeline/amd/build/view_nav.min.js b/blocks/timeline/amd/build/view_nav.min.js index 7682827a6e4df..efce7600cf61a 100644 --- a/blocks/timeline/amd/build/view_nav.min.js +++ b/blocks/timeline/amd/build/view_nav.min.js @@ -4,6 +4,6 @@ define("block_timeline/view_nav",["exports","jquery","core/custom_interaction_ev * * @copyright 2018 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj},CustomEvents=_interopRequireWildcard(CustomEvents),View=_interopRequireWildcard(View),Notification=_interopRequireWildcard(Notification),Utils=_interopRequireWildcard(Utils),UserRepository=_interopRequireWildcard(UserRepository);const SELECTORS_TIMELINE_DAY_FILTER='[data-region="day-filter"]',SELECTORS_TIMELINE_DAY_FILTER_OPTION="[data-from]",SELECTORS_TIMELINE_VIEW_SELECTOR='[data-region="view-selector"]',SELECTORS_DATA_DAYS_OFFSET="[data-days-offset]",SELECTORS_TIMELINE_SEARCH_INPUT='[data-action="search"]',SELECTORS_TIMELINE_SEARCH_CLEAR_ICON='[data-action="clearsearch"]',SELECTORS_NO_COURSES_EMPTY_MESSAGE='[data-region="no-courses-empty-message"]',activeSearchState=(clearSearchIcon,timelineViewRoot)=>{clearSearchIcon.removeClass("d-none"),View.reset(timelineViewRoot)},clearSearchState=(clearSearchIcon,timelineViewRoot)=>{clearSearchIcon.addClass("d-none"),View.reset(timelineViewRoot)};_exports.init=function(root,timelineViewRoot){(function(root,timelineViewRoot){const viewSelector=root.find(SELECTORS_TIMELINE_VIEW_SELECTOR);viewSelector.on("shown shown.bs.tab",(function(e){View.shown(timelineViewRoot),(0,_jquery.default)(e.target).removeClass("active")})),CustomEvents.define(viewSelector,[CustomEvents.events.activate]),viewSelector.on(CustomEvents.events.activate,"[data-bs-toggle='tab']",(function(e){var filtername=(0,_jquery.default)(e.currentTarget).data("filtername");UserRepository.setUserPreference("block_timeline_user_sort_preference",filtername).catch(Notification.exception)}))})(root=(0,_jquery.default)(root),timelineViewRoot),root.find(SELECTORS_NO_COURSES_EMPTY_MESSAGE).length||(function(root,timelineViewRoot){const timelineDaySelectorContainer=root.find(SELECTORS_TIMELINE_DAY_FILTER);CustomEvents.define(timelineDaySelectorContainer,[CustomEvents.events.activate]),timelineDaySelectorContainer.on(CustomEvents.events.activate,SELECTORS_TIMELINE_DAY_FILTER_OPTION,(function(e,data){var filtername=(0,_jquery.default)(e.currentTarget).data("filtername");UserRepository.setUserPreference("block_timeline_user_filter_preference",filtername).catch(Notification.exception);var option=(0,_jquery.default)(e.target).closest(SELECTORS_TIMELINE_DAY_FILTER_OPTION);if("true"!=option.attr("aria-current")){var daysOffset=option.attr("data-from"),daysLimit=option.attr("data-to"),elementsWithDaysOffset=root.find(SELECTORS_DATA_DAYS_OFFSET);elementsWithDaysOffset.attr("data-days-offset",daysOffset),null!=daysLimit?elementsWithDaysOffset.attr("data-days-limit",daysLimit):elementsWithDaysOffset.removeAttr("data-days-limit"),"overdue"===option.attr("data-filtername")?elementsWithDaysOffset.attr("data-filter-overdue",!0):elementsWithDaysOffset.removeAttr("data-filter-overdue"),View.reset(timelineViewRoot),data.originalEvent.preventDefault()}}))}(root,timelineViewRoot),((root,timelineViewRoot)=>{const searchInput=root.find(SELECTORS_TIMELINE_SEARCH_INPUT),clearSearchIcon=root.find(SELECTORS_TIMELINE_SEARCH_CLEAR_ICON);searchInput.on("input",Utils.debounce((()=>{""!==searchInput.val()?activeSearchState(clearSearchIcon,timelineViewRoot):clearSearchState(clearSearchIcon,timelineViewRoot)}),1e3)),clearSearchIcon.on("click",(()=>{searchInput.val(""),clearSearchState(clearSearchIcon,timelineViewRoot),searchInput.focus()}))})(root,timelineViewRoot))}})); + */function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj},CustomEvents=_interopRequireWildcard(CustomEvents),View=_interopRequireWildcard(View),Notification=_interopRequireWildcard(Notification),Utils=_interopRequireWildcard(Utils),UserRepository=_interopRequireWildcard(UserRepository);const SELECTORS_TIMELINE_DAY_FILTER='[data-region="day-filter"]',SELECTORS_TIMELINE_DAY_FILTER_OPTION="[data-from]",SELECTORS_TIMELINE_VIEW_SELECTOR='[data-region="view-selector"]',SELECTORS_DATA_DAYS_OFFSET="[data-days-offset]",SELECTORS_TIMELINE_SEARCH_INPUT='[data-action="search"]',SELECTORS_TIMELINE_SEARCH_CLEAR_ICON='[data-action="clearsearch"]',SELECTORS_NO_COURSES_EMPTY_MESSAGE='[data-region="no-courses-empty-message"]',activeSearchState=(clearSearchIcon,timelineViewRoot)=>{clearSearchIcon.removeClass("d-none"),View.reset(timelineViewRoot)},clearSearchState=(clearSearchIcon,timelineViewRoot)=>{clearSearchIcon.addClass("d-none"),View.reset(timelineViewRoot)};_exports.init=function(root,timelineViewRoot){(function(root,timelineViewRoot){const viewSelector=root.find(SELECTORS_TIMELINE_VIEW_SELECTOR);viewSelector[0].addEventListener("shown shown.bs.tab",(function(e){View.shown(timelineViewRoot),(0,_jquery.default)(e.target).removeClass("active")})),CustomEvents.define(viewSelector,[CustomEvents.events.activate]),viewSelector.on(CustomEvents.events.activate,"[data-bs-toggle='tab']",(function(e){var filtername=(0,_jquery.default)(e.currentTarget).data("filtername");UserRepository.setUserPreference("block_timeline_user_sort_preference",filtername).catch(Notification.exception)}))})(root=(0,_jquery.default)(root),timelineViewRoot),root.find(SELECTORS_NO_COURSES_EMPTY_MESSAGE).length||(function(root,timelineViewRoot){const timelineDaySelectorContainer=root.find(SELECTORS_TIMELINE_DAY_FILTER);CustomEvents.define(timelineDaySelectorContainer,[CustomEvents.events.activate]),timelineDaySelectorContainer.on(CustomEvents.events.activate,SELECTORS_TIMELINE_DAY_FILTER_OPTION,(function(e,data){var filtername=(0,_jquery.default)(e.currentTarget).data("filtername");UserRepository.setUserPreference("block_timeline_user_filter_preference",filtername).catch(Notification.exception);var option=(0,_jquery.default)(e.target).closest(SELECTORS_TIMELINE_DAY_FILTER_OPTION);if("true"!=option.attr("aria-current")){var daysOffset=option.attr("data-from"),daysLimit=option.attr("data-to"),elementsWithDaysOffset=root.find(SELECTORS_DATA_DAYS_OFFSET);elementsWithDaysOffset.attr("data-days-offset",daysOffset),null!=daysLimit?elementsWithDaysOffset.attr("data-days-limit",daysLimit):elementsWithDaysOffset.removeAttr("data-days-limit"),"overdue"===option.attr("data-filtername")?elementsWithDaysOffset.attr("data-filter-overdue",!0):elementsWithDaysOffset.removeAttr("data-filter-overdue"),View.reset(timelineViewRoot),data.originalEvent.preventDefault()}}))}(root,timelineViewRoot),((root,timelineViewRoot)=>{const searchInput=root.find(SELECTORS_TIMELINE_SEARCH_INPUT),clearSearchIcon=root.find(SELECTORS_TIMELINE_SEARCH_CLEAR_ICON);searchInput.on("input",Utils.debounce((()=>{""!==searchInput.val()?activeSearchState(clearSearchIcon,timelineViewRoot):clearSearchState(clearSearchIcon,timelineViewRoot)}),1e3)),clearSearchIcon.on("click",(()=>{searchInput.val(""),clearSearchState(clearSearchIcon,timelineViewRoot),searchInput.focus()}))})(root,timelineViewRoot))}})); //# sourceMappingURL=view_nav.min.js.map \ No newline at end of file diff --git a/blocks/timeline/amd/build/view_nav.min.js.map b/blocks/timeline/amd/build/view_nav.min.js.map index 66a757c688101..74b1c9c2ce87e 100644 --- a/blocks/timeline/amd/build/view_nav.min.js.map +++ b/blocks/timeline/amd/build/view_nav.min.js.map @@ -1 +1 @@ -{"version":3,"file":"view_nav.min.js","sources":["../src/view_nav.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 * Manage the timeline view navigation for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as View from 'block_timeline/view';\nimport * as Notification from 'core/notification';\nimport * as Utils from 'core/utils';\nimport * as UserRepository from 'core_user/repository';\n\nconst SELECTORS = {\n TIMELINE_DAY_FILTER: '[data-region=\"day-filter\"]',\n TIMELINE_DAY_FILTER_OPTION: '[data-from]',\n TIMELINE_VIEW_SELECTOR: '[data-region=\"view-selector\"]',\n DATA_DAYS_OFFSET: '[data-days-offset]',\n DATA_DAYS_LIMIT: '[data-days-limit]',\n TIMELINE_SEARCH_INPUT: '[data-action=\"search\"]',\n TIMELINE_SEARCH_CLEAR_ICON: '[data-action=\"clearsearch\"]',\n NO_COURSES_EMPTY_MESSAGE: '[data-region=\"no-courses-empty-message\"]',\n};\n\n/**\n * Event listener for the day selector (\"Next 7 days\", \"Next 30 days\", etc).\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nconst registerTimelineDaySelector = function(root, timelineViewRoot) {\n const timelineDaySelectorContainer = root.find(SELECTORS.TIMELINE_DAY_FILTER);\n\n CustomEvents.define(timelineDaySelectorContainer, [CustomEvents.events.activate]);\n timelineDaySelectorContainer.on(\n CustomEvents.events.activate,\n SELECTORS.TIMELINE_DAY_FILTER_OPTION,\n function(e, data) {\n // Update the user preference\n var filtername = $(e.currentTarget).data('filtername');\n var type = 'block_timeline_user_filter_preference';\n UserRepository.setUserPreference(type, filtername)\n .catch(Notification.exception);\n\n var option = $(e.target).closest(SELECTORS.TIMELINE_DAY_FILTER_OPTION);\n\n if (option.attr('aria-current') == 'true') {\n // If it's already active then we don't need to do anything.\n return;\n }\n\n var daysOffset = option.attr('data-from');\n var daysLimit = option.attr('data-to');\n var elementsWithDaysOffset = root.find(SELECTORS.DATA_DAYS_OFFSET);\n\n elementsWithDaysOffset.attr('data-days-offset', daysOffset);\n\n if (daysLimit != undefined) {\n elementsWithDaysOffset.attr('data-days-limit', daysLimit);\n } else {\n elementsWithDaysOffset.removeAttr('data-days-limit');\n }\n\n if (option.attr('data-filtername') === 'overdue') {\n elementsWithDaysOffset.attr('data-filter-overdue', true);\n } else {\n elementsWithDaysOffset.removeAttr('data-filter-overdue');\n }\n\n // Reset the views to reinitialise the event lists now that we've\n // updated the day limits.\n View.reset(timelineViewRoot);\n\n data.originalEvent.preventDefault();\n }\n );\n};\n\n/**\n * Event listener for the \"sort\" button in the timeline navigation that allows for\n * changing between the timeline dates and courses views.\n *\n * On a view change we tell the timeline view module that the view has been shown\n * so that it can handle how to display the appropriate view.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nconst registerViewSelector = function(root, timelineViewRoot) {\n const viewSelector = root.find(SELECTORS.TIMELINE_VIEW_SELECTOR);\n\n // Listen for when the user changes tab so that we can show the first set of courses\n // and load their events when they request the sort by courses view for the first time.\n viewSelector.on('shown shown.bs.tab', function(e) {\n View.shown(timelineViewRoot);\n $(e.target).removeClass('active');\n });\n\n\n // Event selector for user_sort\n CustomEvents.define(viewSelector, [CustomEvents.events.activate]);\n viewSelector.on(CustomEvents.events.activate, \"[data-bs-toggle='tab']\", function(e) {\n var filtername = $(e.currentTarget).data('filtername');\n var type = 'block_timeline_user_sort_preference';\n UserRepository.setUserPreference(type, filtername)\n .catch(Notification.exception);\n });\n};\n\n/**\n * Event listener for the \"search\" input field in the timeline navigation that allows for\n * searching the activity name, course name and activity type.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nconst registerSearch = (root, timelineViewRoot) => {\n const searchInput = root.find(SELECTORS.TIMELINE_SEARCH_INPUT);\n const clearSearchIcon = root.find(SELECTORS.TIMELINE_SEARCH_CLEAR_ICON);\n searchInput.on('input', Utils.debounce(() => {\n if (searchInput.val() !== '') {\n activeSearchState(clearSearchIcon, timelineViewRoot);\n } else {\n clearSearchState(clearSearchIcon, timelineViewRoot);\n }\n }, 1000));\n clearSearchIcon.on('click', () => {\n searchInput.val('');\n clearSearchState(clearSearchIcon, timelineViewRoot);\n searchInput.focus();\n });\n};\n\n/**\n * Show the clear search icon.\n *\n * @param {object} clearSearchIcon Clear search icon element.\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nconst activeSearchState = (clearSearchIcon, timelineViewRoot) => {\n clearSearchIcon.removeClass('d-none');\n View.reset(timelineViewRoot);\n};\n\n/**\n * Hide the clear search icon.\n *\n * @param {object} clearSearchIcon Clear search icon element.\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nconst clearSearchState = (clearSearchIcon, timelineViewRoot) => {\n clearSearchIcon.addClass('d-none');\n View.reset(timelineViewRoot);\n};\n\n/**\n * Initialise the timeline view navigation by adding event listeners to\n * the navigation elements.\n *\n * @param {jQuery|HTMLElement} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nexport const init = function(root, timelineViewRoot) {\n root = $(root);\n\n registerViewSelector(root, timelineViewRoot);\n\n // Only need to handle filtering if the user is actively enrolled in a course.\n if (!root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) {\n registerTimelineDaySelector(root, timelineViewRoot);\n registerSearch(root, timelineViewRoot);\n }\n};\n"],"names":["SELECTORS","activeSearchState","clearSearchIcon","timelineViewRoot","removeClass","View","reset","clearSearchState","addClass","root","viewSelector","find","on","e","shown","target","CustomEvents","define","events","activate","filtername","currentTarget","data","UserRepository","setUserPreference","catch","Notification","exception","registerViewSelector","length","timelineDaySelectorContainer","option","closest","attr","daysOffset","daysLimit","elementsWithDaysOffset","undefined","removeAttr","originalEvent","preventDefault","registerTimelineDaySelector","searchInput","Utils","debounce","val","focus","registerSearch"],"mappings":";;;;;;4yCA6BMA,8BACmB,6BADnBA,qCAE0B,cAF1BA,iCAGsB,gCAHtBA,2BAIgB,qBAJhBA,gCAMqB,yBANrBA,qCAO0B,8BAP1BA,mCAQwB,2CAsHxBC,kBAAoB,CAACC,gBAAiBC,oBACxCD,gBAAgBE,YAAY,UAC5BC,KAAKC,MAAMH,mBASTI,iBAAmB,CAACL,gBAAiBC,oBACvCD,gBAAgBM,SAAS,UACzBH,KAAKC,MAAMH,iCAUK,SAASM,KAAMN,mBA1EN,SAASM,KAAMN,wBAClCO,aAAeD,KAAKE,KAAKX,kCAI/BU,aAAaE,GAAG,sBAAsB,SAASC,GAC3CR,KAAKS,MAAMX,sCACTU,EAAEE,QAAQX,YAAY,aAK5BY,aAAaC,OAAOP,aAAc,CAACM,aAAaE,OAAOC,WACvDT,aAAaE,GAAGI,aAAaE,OAAOC,SAAU,0BAA0B,SAASN,OACzEO,YAAa,mBAAEP,EAAEQ,eAAeC,KAAK,cAEzCC,eAAeC,kBADJ,sCAC4BJ,YAClCK,MAAMC,aAAaC,eA4D5BC,CAFAnB,MAAO,mBAAEA,MAEkBN,kBAGtBM,KAAKE,KAAKX,oCAAoC6B,SA1InB,SAASpB,KAAMN,wBACzC2B,6BAA+BrB,KAAKE,KAAKX,+BAE/CgB,aAAaC,OAAOa,6BAA8B,CAACd,aAAaE,OAAOC,WACvEW,6BAA6BlB,GACzBI,aAAaE,OAAOC,SACpBnB,sCACA,SAASa,EAAGS,UAEJF,YAAa,mBAAEP,EAAEQ,eAAeC,KAAK,cAEzCC,eAAeC,kBADJ,wCAC4BJ,YAClCK,MAAMC,aAAaC,eAEpBI,QAAS,mBAAElB,EAAEE,QAAQiB,QAAQhC,yCAEE,QAA/B+B,OAAOE,KAAK,qBAKZC,WAAaH,OAAOE,KAAK,aACzBE,UAAYJ,OAAOE,KAAK,WACxBG,uBAAyB3B,KAAKE,KAAKX,4BAEvCoC,uBAAuBH,KAAK,mBAAoBC,YAE/BG,MAAbF,UACAC,uBAAuBH,KAAK,kBAAmBE,WAE/CC,uBAAuBE,WAAW,mBAGC,YAAnCP,OAAOE,KAAK,mBACZG,uBAAuBH,KAAK,uBAAuB,GAEnDG,uBAAuBE,WAAW,uBAKtCjC,KAAKC,MAAMH,kBAEXmB,KAAKiB,cAAcC,qBAgGvBC,CAA4BhC,KAAMN,kBArDnB,EAACM,KAAMN,0BACpBuC,YAAcjC,KAAKE,KAAKX,iCACxBE,gBAAkBO,KAAKE,KAAKX,sCAClC0C,YAAY9B,GAAG,QAAS+B,MAAMC,UAAS,KACT,KAAtBF,YAAYG,MACZ5C,kBAAkBC,gBAAiBC,kBAEnCI,iBAAiBL,gBAAiBC,oBAEvC,MACHD,gBAAgBU,GAAG,SAAS,KACxB8B,YAAYG,IAAI,IAChBtC,iBAAiBL,gBAAiBC,kBAClCuC,YAAYI,YAyCZC,CAAetC,KAAMN"} \ No newline at end of file +{"version":3,"file":"view_nav.min.js","sources":["../src/view_nav.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 * Manage the timeline view navigation for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as View from 'block_timeline/view';\nimport * as Notification from 'core/notification';\nimport * as Utils from 'core/utils';\nimport * as UserRepository from 'core_user/repository';\n\nconst SELECTORS = {\n TIMELINE_DAY_FILTER: '[data-region=\"day-filter\"]',\n TIMELINE_DAY_FILTER_OPTION: '[data-from]',\n TIMELINE_VIEW_SELECTOR: '[data-region=\"view-selector\"]',\n DATA_DAYS_OFFSET: '[data-days-offset]',\n DATA_DAYS_LIMIT: '[data-days-limit]',\n TIMELINE_SEARCH_INPUT: '[data-action=\"search\"]',\n TIMELINE_SEARCH_CLEAR_ICON: '[data-action=\"clearsearch\"]',\n NO_COURSES_EMPTY_MESSAGE: '[data-region=\"no-courses-empty-message\"]',\n};\n\n/**\n * Event listener for the day selector (\"Next 7 days\", \"Next 30 days\", etc).\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nconst registerTimelineDaySelector = function(root, timelineViewRoot) {\n const timelineDaySelectorContainer = root.find(SELECTORS.TIMELINE_DAY_FILTER);\n\n CustomEvents.define(timelineDaySelectorContainer, [CustomEvents.events.activate]);\n timelineDaySelectorContainer.on(\n CustomEvents.events.activate,\n SELECTORS.TIMELINE_DAY_FILTER_OPTION,\n function(e, data) {\n // Update the user preference\n var filtername = $(e.currentTarget).data('filtername');\n var type = 'block_timeline_user_filter_preference';\n UserRepository.setUserPreference(type, filtername)\n .catch(Notification.exception);\n\n var option = $(e.target).closest(SELECTORS.TIMELINE_DAY_FILTER_OPTION);\n\n if (option.attr('aria-current') == 'true') {\n // If it's already active then we don't need to do anything.\n return;\n }\n\n var daysOffset = option.attr('data-from');\n var daysLimit = option.attr('data-to');\n var elementsWithDaysOffset = root.find(SELECTORS.DATA_DAYS_OFFSET);\n\n elementsWithDaysOffset.attr('data-days-offset', daysOffset);\n\n if (daysLimit != undefined) {\n elementsWithDaysOffset.attr('data-days-limit', daysLimit);\n } else {\n elementsWithDaysOffset.removeAttr('data-days-limit');\n }\n\n if (option.attr('data-filtername') === 'overdue') {\n elementsWithDaysOffset.attr('data-filter-overdue', true);\n } else {\n elementsWithDaysOffset.removeAttr('data-filter-overdue');\n }\n\n // Reset the views to reinitialise the event lists now that we've\n // updated the day limits.\n View.reset(timelineViewRoot);\n\n data.originalEvent.preventDefault();\n }\n );\n};\n\n/**\n * Event listener for the \"sort\" button in the timeline navigation that allows for\n * changing between the timeline dates and courses views.\n *\n * On a view change we tell the timeline view module that the view has been shown\n * so that it can handle how to display the appropriate view.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nconst registerViewSelector = function(root, timelineViewRoot) {\n const viewSelector = root.find(SELECTORS.TIMELINE_VIEW_SELECTOR);\n\n // Listen for when the user changes tab so that we can show the first set of courses\n // and load their events when they request the sort by courses view for the first time.\n viewSelector[0].addEventListener('shown shown.bs.tab', function(e) {\n View.shown(timelineViewRoot);\n $(e.target).removeClass('active');\n });\n\n\n // Event selector for user_sort\n CustomEvents.define(viewSelector, [CustomEvents.events.activate]);\n viewSelector.on(CustomEvents.events.activate, \"[data-bs-toggle='tab']\", function(e) {\n var filtername = $(e.currentTarget).data('filtername');\n var type = 'block_timeline_user_sort_preference';\n UserRepository.setUserPreference(type, filtername)\n .catch(Notification.exception);\n });\n};\n\n/**\n * Event listener for the \"search\" input field in the timeline navigation that allows for\n * searching the activity name, course name and activity type.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nconst registerSearch = (root, timelineViewRoot) => {\n const searchInput = root.find(SELECTORS.TIMELINE_SEARCH_INPUT);\n const clearSearchIcon = root.find(SELECTORS.TIMELINE_SEARCH_CLEAR_ICON);\n searchInput.on('input', Utils.debounce(() => {\n if (searchInput.val() !== '') {\n activeSearchState(clearSearchIcon, timelineViewRoot);\n } else {\n clearSearchState(clearSearchIcon, timelineViewRoot);\n }\n }, 1000));\n clearSearchIcon.on('click', () => {\n searchInput.val('');\n clearSearchState(clearSearchIcon, timelineViewRoot);\n searchInput.focus();\n });\n};\n\n/**\n * Show the clear search icon.\n *\n * @param {object} clearSearchIcon Clear search icon element.\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nconst activeSearchState = (clearSearchIcon, timelineViewRoot) => {\n clearSearchIcon.removeClass('d-none');\n View.reset(timelineViewRoot);\n};\n\n/**\n * Hide the clear search icon.\n *\n * @param {object} clearSearchIcon Clear search icon element.\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nconst clearSearchState = (clearSearchIcon, timelineViewRoot) => {\n clearSearchIcon.addClass('d-none');\n View.reset(timelineViewRoot);\n};\n\n/**\n * Initialise the timeline view navigation by adding event listeners to\n * the navigation elements.\n *\n * @param {jQuery|HTMLElement} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\nexport const init = function(root, timelineViewRoot) {\n root = $(root);\n\n registerViewSelector(root, timelineViewRoot);\n\n // Only need to handle filtering if the user is actively enrolled in a course.\n if (!root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) {\n registerTimelineDaySelector(root, timelineViewRoot);\n registerSearch(root, timelineViewRoot);\n }\n};\n"],"names":["SELECTORS","activeSearchState","clearSearchIcon","timelineViewRoot","removeClass","View","reset","clearSearchState","addClass","root","viewSelector","find","addEventListener","e","shown","target","CustomEvents","define","events","activate","on","filtername","currentTarget","data","UserRepository","setUserPreference","catch","Notification","exception","registerViewSelector","length","timelineDaySelectorContainer","option","closest","attr","daysOffset","daysLimit","elementsWithDaysOffset","undefined","removeAttr","originalEvent","preventDefault","registerTimelineDaySelector","searchInput","Utils","debounce","val","focus","registerSearch"],"mappings":";;;;;;4yCA6BMA,8BACmB,6BADnBA,qCAE0B,cAF1BA,iCAGsB,gCAHtBA,2BAIgB,qBAJhBA,gCAMqB,yBANrBA,qCAO0B,8BAP1BA,mCAQwB,2CAsHxBC,kBAAoB,CAACC,gBAAiBC,oBACxCD,gBAAgBE,YAAY,UAC5BC,KAAKC,MAAMH,mBASTI,iBAAmB,CAACL,gBAAiBC,oBACvCD,gBAAgBM,SAAS,UACzBH,KAAKC,MAAMH,iCAUK,SAASM,KAAMN,mBA1EN,SAASM,KAAMN,wBAClCO,aAAeD,KAAKE,KAAKX,kCAI/BU,aAAa,GAAGE,iBAAiB,sBAAsB,SAASC,GAC5DR,KAAKS,MAAMX,sCACTU,EAAEE,QAAQX,YAAY,aAK5BY,aAAaC,OAAOP,aAAc,CAACM,aAAaE,OAAOC,WACvDT,aAAaU,GAAGJ,aAAaE,OAAOC,SAAU,0BAA0B,SAASN,OACzEQ,YAAa,mBAAER,EAAES,eAAeC,KAAK,cAEzCC,eAAeC,kBADJ,sCAC4BJ,YAClCK,MAAMC,aAAaC,eA4D5BC,CAFApB,MAAO,mBAAEA,MAEkBN,kBAGtBM,KAAKE,KAAKX,oCAAoC8B,SA1InB,SAASrB,KAAMN,wBACzC4B,6BAA+BtB,KAAKE,KAAKX,+BAE/CgB,aAAaC,OAAOc,6BAA8B,CAACf,aAAaE,OAAOC,WACvEY,6BAA6BX,GACzBJ,aAAaE,OAAOC,SACpBnB,sCACA,SAASa,EAAGU,UAEJF,YAAa,mBAAER,EAAES,eAAeC,KAAK,cAEzCC,eAAeC,kBADJ,wCAC4BJ,YAClCK,MAAMC,aAAaC,eAEpBI,QAAS,mBAAEnB,EAAEE,QAAQkB,QAAQjC,yCAEE,QAA/BgC,OAAOE,KAAK,qBAKZC,WAAaH,OAAOE,KAAK,aACzBE,UAAYJ,OAAOE,KAAK,WACxBG,uBAAyB5B,KAAKE,KAAKX,4BAEvCqC,uBAAuBH,KAAK,mBAAoBC,YAE/BG,MAAbF,UACAC,uBAAuBH,KAAK,kBAAmBE,WAE/CC,uBAAuBE,WAAW,mBAGC,YAAnCP,OAAOE,KAAK,mBACZG,uBAAuBH,KAAK,uBAAuB,GAEnDG,uBAAuBE,WAAW,uBAKtClC,KAAKC,MAAMH,kBAEXoB,KAAKiB,cAAcC,qBAgGvBC,CAA4BjC,KAAMN,kBArDnB,EAACM,KAAMN,0BACpBwC,YAAclC,KAAKE,KAAKX,iCACxBE,gBAAkBO,KAAKE,KAAKX,sCAClC2C,YAAYvB,GAAG,QAASwB,MAAMC,UAAS,KACT,KAAtBF,YAAYG,MACZ7C,kBAAkBC,gBAAiBC,kBAEnCI,iBAAiBL,gBAAiBC,oBAEvC,MACHD,gBAAgBkB,GAAG,SAAS,KACxBuB,YAAYG,IAAI,IAChBvC,iBAAiBL,gBAAiBC,kBAClCwC,YAAYI,YAyCZC,CAAevC,KAAMN"} \ No newline at end of file diff --git a/blocks/timeline/amd/src/view_nav.js b/blocks/timeline/amd/src/view_nav.js index 01531229f59a5..5452f09f7d504 100644 --- a/blocks/timeline/amd/src/view_nav.js +++ b/blocks/timeline/amd/src/view_nav.js @@ -107,7 +107,7 @@ const registerViewSelector = function(root, timelineViewRoot) { // Listen for when the user changes tab so that we can show the first set of courses // and load their events when they request the sort by courses view for the first time. - viewSelector.on('shown shown.bs.tab', function(e) { + viewSelector[0].addEventListener('shown shown.bs.tab', function(e) { View.shown(timelineViewRoot); $(e.target).removeClass('active'); }); diff --git a/calendar/amd/build/popover.min.js b/calendar/amd/build/popover.min.js index a7a5bf451fe47..f4ee50489a537 100644 --- a/calendar/amd/build/popover.min.js +++ b/calendar/amd/build/popover.min.js @@ -1,4 +1,4 @@ -define("core_calendar/popover",["theme_boost/popover","jquery","core_calendar/selectors"],(function(_popover,_jquery,CalendarSelectors){var obj; +define("core_calendar/popover",["theme_boost/bootstrap/popover","jquery","core_calendar/selectors"],(function(_popover,_jquery,CalendarSelectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Javascript popover for the `core_calendar` subsystem. * @@ -6,6 +6,6 @@ define("core_calendar/popover",["theme_boost/popover","jquery","core_calendar/se * @copyright 2021 Huong Nguyen * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 4.0 - */function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj},CalendarSelectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CalendarSelectors);const isPopoverConfigured=new Map,showPopover=target=>{const dateContainer=target.closest(CalendarSelectors.elements.dateContainer);if(!isPopoverConfigured.has(dateContainer)){(0,_jquery.default)(target).popover({trigger:"manual",placement:"top",html:!0,title:dateContainer.dataset.title,content:()=>{const source=(0,_jquery.default)(dateContainer).find(CalendarSelectors.elements.dateContent),content=(0,_jquery.default)("
");if(source.length){const temptContent=source.find(".hidden").clone(!1);content.html(temptContent.html())}return content.html()},animation:!1}),isPopoverConfigured.set(dateContainer,!0)}(dateContainer=>"none"===window.getComputedStyle(dateContainer.querySelector(CalendarSelectors.elements.dateContent)).display)(dateContainer)&&((0,_jquery.default)(target).popover("show"),target.addEventListener("mouseleave",hidePopover),target.addEventListener("focusout",hidePopover),target.addEventListener("click",hidePopover))},hidePopover=e=>{const target=e.target,dateContainer=e.target.closest(CalendarSelectors.elements.dateContainer);if(dateContainer&&isPopoverConfigured.has(dateContainer)){const isTargetActive=target.contains(document.activeElement),isTargetHover=target.matches(":hover"),isTargetClicked=document.activeElement.contains(target);let removeListener=!0;isTargetActive||isTargetHover?isTargetClicked?(0,_jquery.default)(document.activeElement).popover("hide"):removeListener=!1:(0,_jquery.default)(target).popover("hide"),removeListener&&(target.removeEventListener("mouseleave",hidePopover),target.removeEventListener("focusout",hidePopover),target.removeEventListener("click",hidePopover))}};let listenersRegistered=!1;listenersRegistered||((()=>{const showPopoverHandler=e=>{const dayLink=e.target.closest(CalendarSelectors.links.dayLink);dayLink&&(e.preventDefault(),showPopover(dayLink))};document.addEventListener("mouseover",showPopoverHandler),document.addEventListener("focusin",showPopoverHandler)})(),listenersRegistered=!0)})); + */_popover=_interopRequireDefault(_popover),_jquery=_interopRequireDefault(_jquery),CalendarSelectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CalendarSelectors);const isPopoverConfigured=new Map,showPopover=target=>{const dateContainer=target.closest(CalendarSelectors.elements.dateContainer);if(!isPopoverConfigured.has(dateContainer)){const config={trigger:"manual",placement:"top",html:!0,title:dateContainer.dataset.title,content:()=>{const source=(0,_jquery.default)(dateContainer).find(CalendarSelectors.elements.dateContent),content=(0,_jquery.default)("
");if(source.length){const temptContent=source.find(".hidden").clone(!1);content.html(temptContent.html())}return content.html()},animation:!1};new _popover.default(target,config),isPopoverConfigured.set(dateContainer,!0)}(dateContainer=>"none"===window.getComputedStyle(dateContainer.querySelector(CalendarSelectors.elements.dateContent)).display)(dateContainer)&&(_popover.default.getInstance(target).show(),target.addEventListener("mouseleave",hidePopover),target.addEventListener("focusout",hidePopover),target.addEventListener("click",hidePopover))},hidePopover=e=>{const target=e.target,dateContainer=e.target.closest(CalendarSelectors.elements.dateContainer);if(dateContainer&&isPopoverConfigured.has(dateContainer)){const isTargetActive=target.contains(document.activeElement),isTargetHover=target.matches(":hover"),isTargetClicked=document.activeElement.contains(target);let removeListener=!0;isTargetActive||isTargetHover?isTargetClicked?_popover.default.getOrCreateInstance(document.activeElement).hide():removeListener=!1:_popover.default.getOrCreateInstance(target).hide(),removeListener&&(target.removeEventListener("mouseleave",hidePopover),target.removeEventListener("focusout",hidePopover),target.removeEventListener("click",hidePopover))}};let listenersRegistered=!1;listenersRegistered||((()=>{const showPopoverHandler=e=>{const dayLink=e.target.closest(CalendarSelectors.links.dayLink);dayLink&&(e.preventDefault(),showPopover(dayLink))};document.addEventListener("mouseover",showPopoverHandler),document.addEventListener("focusin",showPopoverHandler)})(),listenersRegistered=!0)})); //# sourceMappingURL=popover.min.js.map \ No newline at end of file diff --git a/calendar/amd/build/popover.min.js.map b/calendar/amd/build/popover.min.js.map index 1d406bf8da5df..939459edaa17d 100644 --- a/calendar/amd/build/popover.min.js.map +++ b/calendar/amd/build/popover.min.js.map @@ -1 +1 @@ -{"version":3,"file":"popover.min.js","sources":["../src/popover.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 * Javascript popover for the `core_calendar` subsystem.\n *\n * @module core_calendar/popover\n * @copyright 2021 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n */\n\nimport 'theme_boost/popover';\nimport jQuery from 'jquery';\nimport * as CalendarSelectors from 'core_calendar/selectors';\n\n/**\n * Check if we are allowing to enable the popover or not.\n * @param {Element} dateContainer\n * @returns {boolean}\n */\nconst isPopoverAvailable = (dateContainer) => {\n return window.getComputedStyle(dateContainer.querySelector(CalendarSelectors.elements.dateContent)).display === 'none';\n};\n\nconst isPopoverConfigured = new Map();\n\nconst showPopover = target => {\n const dateContainer = target.closest(CalendarSelectors.elements.dateContainer);\n if (!isPopoverConfigured.has(dateContainer)) {\n const dateEle = jQuery(target);\n dateEle.popover({\n trigger: 'manual',\n placement: 'top',\n html: true,\n title: dateContainer.dataset.title,\n content: () => {\n const source = jQuery(dateContainer).find(CalendarSelectors.elements.dateContent);\n const content = jQuery('
');\n if (source.length) {\n const temptContent = source.find('.hidden').clone(false);\n content.html(temptContent.html());\n }\n return content.html();\n },\n 'animation': false,\n });\n\n isPopoverConfigured.set(dateContainer, true);\n }\n\n if (isPopoverAvailable(dateContainer)) {\n jQuery(target).popover('show');\n target.addEventListener('mouseleave', hidePopover);\n target.addEventListener('focusout', hidePopover);\n // Set up the hide function to the click event type.\n target.addEventListener('click', hidePopover);\n }\n};\n\nconst hidePopover = e => {\n const target = e.target;\n const dateContainer = e.target.closest(CalendarSelectors.elements.dateContainer);\n if (!dateContainer) {\n return;\n }\n if (isPopoverConfigured.has(dateContainer)) {\n const isTargetActive = target.contains(document.activeElement);\n const isTargetHover = target.matches(':hover');\n\n // Checks if a target element is clicked or pressed.\n const isTargetClicked = document.activeElement.contains(target);\n\n let removeListener = true;\n if (!isTargetActive && !isTargetHover) {\n jQuery(target).popover('hide');\n } else if (isTargetClicked) {\n jQuery(document.activeElement).popover('hide');\n } else {\n removeListener = false;\n }\n\n if (removeListener) {\n target.removeEventListener('mouseleave', hidePopover);\n target.removeEventListener('focusout', hidePopover);\n target.removeEventListener('click', hidePopover);\n }\n }\n};\n\n/**\n * Register events for date container.\n */\nconst registerEventListeners = () => {\n const showPopoverHandler = (e) => {\n const dayLink = e.target.closest(CalendarSelectors.links.dayLink);\n if (!dayLink) {\n return;\n }\n\n e.preventDefault();\n showPopover(dayLink);\n };\n\n document.addEventListener('mouseover', showPopoverHandler);\n document.addEventListener('focusin', showPopoverHandler);\n};\n\nlet listenersRegistered = false;\nif (!listenersRegistered) {\n registerEventListeners();\n listenersRegistered = true;\n}\n"],"names":["isPopoverConfigured","Map","showPopover","target","dateContainer","closest","CalendarSelectors","elements","has","popover","trigger","placement","html","title","dataset","content","source","find","dateContent","length","temptContent","clone","set","window","getComputedStyle","querySelector","display","isPopoverAvailable","addEventListener","hidePopover","e","isTargetActive","contains","document","activeElement","isTargetHover","matches","isTargetClicked","removeListener","removeEventListener","listenersRegistered","showPopoverHandler","dayLink","links","preventDefault","registerEventListeners"],"mappings":";;;;;;;;wgCAqCMA,oBAAsB,IAAIC,IAE1BC,YAAcC,eACVC,cAAgBD,OAAOE,QAAQC,kBAAkBC,SAASH,mBAC3DJ,oBAAoBQ,IAAIJ,eAAgB,EACzB,mBAAOD,QACfM,QAAQ,CACZC,QAAS,SACTC,UAAW,MACXC,MAAM,EACNC,MAAOT,cAAcU,QAAQD,MAC7BE,QAAS,WACCC,QAAS,mBAAOZ,eAAea,KAAKX,kBAAkBC,SAASW,aAC/DH,SAAU,mBAAO,YACnBC,OAAOG,OAAQ,OACTC,aAAeJ,OAAOC,KAAK,WAAWI,OAAM,GAClDN,QAAQH,KAAKQ,aAAaR,eAEvBG,QAAQH,mBAEN,IAGjBZ,oBAAoBsB,IAAIlB,eAAe,GA3BnBA,CAAAA,eACwF,SAAzGmB,OAAOC,iBAAiBpB,cAAcqB,cAAcnB,kBAAkBC,SAASW,cAAcQ,QA6BhGC,CAAmBvB,qCACZD,QAAQM,QAAQ,QACvBN,OAAOyB,iBAAiB,aAAcC,aACtC1B,OAAOyB,iBAAiB,WAAYC,aAEpC1B,OAAOyB,iBAAiB,QAASC,eAInCA,YAAcC,UACV3B,OAAS2B,EAAE3B,OACXC,cAAgB0B,EAAE3B,OAAOE,QAAQC,kBAAkBC,SAASH,kBAC7DA,eAGDJ,oBAAoBQ,IAAIJ,eAAgB,OAClC2B,eAAiB5B,OAAO6B,SAASC,SAASC,eAC1CC,cAAgBhC,OAAOiC,QAAQ,UAG/BC,gBAAkBJ,SAASC,cAAcF,SAAS7B,YAEpDmC,gBAAiB,EAChBP,gBAAmBI,cAEbE,oCACAJ,SAASC,eAAezB,QAAQ,QAEvC6B,gBAAiB,sBAJVnC,QAAQM,QAAQ,QAOvB6B,iBACAnC,OAAOoC,oBAAoB,aAAcV,aACzC1B,OAAOoC,oBAAoB,WAAYV,aACvC1B,OAAOoC,oBAAoB,QAASV,oBAuB5CW,qBAAsB,EACrBA,sBAhB0B,YACrBC,mBAAsBX,UAClBY,QAAUZ,EAAE3B,OAAOE,QAAQC,kBAAkBqC,MAAMD,SACpDA,UAILZ,EAAEc,iBACF1C,YAAYwC,WAGhBT,SAASL,iBAAiB,YAAaa,oBACvCR,SAASL,iBAAiB,UAAWa,qBAKrCI,GACAL,qBAAsB"} \ No newline at end of file +{"version":3,"file":"popover.min.js","sources":["../src/popover.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 * Javascript popover for the `core_calendar` subsystem.\n *\n * @module core_calendar/popover\n * @copyright 2021 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n */\n\nimport Popover from 'theme_boost/bootstrap/popover';\nimport jQuery from 'jquery';\nimport * as CalendarSelectors from 'core_calendar/selectors';\n\n/**\n * Check if we are allowing to enable the popover or not.\n * @param {Element} dateContainer\n * @returns {boolean}\n */\nconst isPopoverAvailable = (dateContainer) => {\n return window.getComputedStyle(dateContainer.querySelector(CalendarSelectors.elements.dateContent)).display === 'none';\n};\n\nconst isPopoverConfigured = new Map();\nconst showPopover = target => {\n const dateContainer = target.closest(CalendarSelectors.elements.dateContainer);\n if (!isPopoverConfigured.has(dateContainer)) {\n const config = {\n trigger: 'manual',\n placement: 'top',\n html: true,\n title: dateContainer.dataset.title,\n content: () => {\n const source = jQuery(dateContainer).find(CalendarSelectors.elements.dateContent);\n const content = jQuery('
');\n if (source.length) {\n const temptContent = source.find('.hidden').clone(false);\n content.html(temptContent.html());\n }\n return content.html();\n },\n 'animation': false,\n };\n new Popover(target, config);\n\n isPopoverConfigured.set(dateContainer, true);\n }\n\n if (isPopoverAvailable(dateContainer)) {\n Popover.getInstance(target).show();\n target.addEventListener('mouseleave', hidePopover);\n target.addEventListener('focusout', hidePopover);\n // Set up the hide function to the click event type.\n target.addEventListener('click', hidePopover);\n }\n};\n\nconst hidePopover = e => {\n const target = e.target;\n const dateContainer = e.target.closest(CalendarSelectors.elements.dateContainer);\n if (!dateContainer) {\n return;\n }\n if (isPopoverConfigured.has(dateContainer)) {\n const isTargetActive = target.contains(document.activeElement);\n const isTargetHover = target.matches(':hover');\n\n // Checks if a target element is clicked or pressed.\n const isTargetClicked = document.activeElement.contains(target);\n\n let removeListener = true;\n if (!isTargetActive && !isTargetHover) {\n Popover.getOrCreateInstance(target).hide();\n } else if (isTargetClicked) {\n Popover.getOrCreateInstance(document.activeElement).hide();\n } else {\n removeListener = false;\n }\n\n if (removeListener) {\n target.removeEventListener('mouseleave', hidePopover);\n target.removeEventListener('focusout', hidePopover);\n target.removeEventListener('click', hidePopover);\n }\n }\n};\n\n/**\n * Register events for date container.\n */\nconst registerEventListeners = () => {\n const showPopoverHandler = (e) => {\n const dayLink = e.target.closest(CalendarSelectors.links.dayLink);\n if (!dayLink) {\n return;\n }\n\n e.preventDefault();\n showPopover(dayLink);\n };\n\n document.addEventListener('mouseover', showPopoverHandler);\n document.addEventListener('focusin', showPopoverHandler);\n};\n\nlet listenersRegistered = false;\nif (!listenersRegistered) {\n registerEventListeners();\n listenersRegistered = true;\n}\n"],"names":["isPopoverConfigured","Map","showPopover","target","dateContainer","closest","CalendarSelectors","elements","has","config","trigger","placement","html","title","dataset","content","source","find","dateContent","length","temptContent","clone","Popover","set","window","getComputedStyle","querySelector","display","isPopoverAvailable","getInstance","show","addEventListener","hidePopover","e","isTargetActive","contains","document","activeElement","isTargetHover","matches","isTargetClicked","removeListener","getOrCreateInstance","hide","removeEventListener","listenersRegistered","showPopoverHandler","dayLink","links","preventDefault","registerEventListeners"],"mappings":";;;;;;;;+wBAqCMA,oBAAsB,IAAIC,IAC1BC,YAAcC,eACVC,cAAgBD,OAAOE,QAAQC,kBAAkBC,SAASH,mBAC3DJ,oBAAoBQ,IAAIJ,eAAgB,OACnCK,OAAS,CACXC,QAAS,SACTC,UAAW,MACXC,MAAM,EACNC,MAAOT,cAAcU,QAAQD,MAC7BE,QAAS,WACCC,QAAS,mBAAOZ,eAAea,KAAKX,kBAAkBC,SAASW,aAC/DH,SAAU,mBAAO,YACnBC,OAAOG,OAAQ,OACTC,aAAeJ,OAAOC,KAAK,WAAWI,OAAM,GAClDN,QAAQH,KAAKQ,aAAaR,eAEvBG,QAAQH,mBAEN,OAEbU,iBAAQnB,OAAQM,QAEpBT,oBAAoBuB,IAAInB,eAAe,GA1BnBA,CAAAA,eACwF,SAAzGoB,OAAOC,iBAAiBrB,cAAcsB,cAAcpB,kBAAkBC,SAASW,cAAcS,QA4BhGC,CAAmBxB,kCACXyB,YAAY1B,QAAQ2B,OAC5B3B,OAAO4B,iBAAiB,aAAcC,aACtC7B,OAAO4B,iBAAiB,WAAYC,aAEpC7B,OAAO4B,iBAAiB,QAASC,eAInCA,YAAcC,UACV9B,OAAS8B,EAAE9B,OACXC,cAAgB6B,EAAE9B,OAAOE,QAAQC,kBAAkBC,SAASH,kBAC7DA,eAGDJ,oBAAoBQ,IAAIJ,eAAgB,OAClC8B,eAAiB/B,OAAOgC,SAASC,SAASC,eAC1CC,cAAgBnC,OAAOoC,QAAQ,UAG/BC,gBAAkBJ,SAASC,cAAcF,SAAShC,YAEpDsC,gBAAiB,EAChBP,gBAAmBI,cAEbE,iCACCE,oBAAoBN,SAASC,eAAeM,OAEpDF,gBAAiB,mBAJTC,oBAAoBvC,QAAQwC,OAOpCF,iBACAtC,OAAOyC,oBAAoB,aAAcZ,aACzC7B,OAAOyC,oBAAoB,WAAYZ,aACvC7B,OAAOyC,oBAAoB,QAASZ,oBAuB5Ca,qBAAsB,EACrBA,sBAhB0B,YACrBC,mBAAsBb,UAClBc,QAAUd,EAAE9B,OAAOE,QAAQC,kBAAkB0C,MAAMD,SACpDA,UAILd,EAAEgB,iBACF/C,YAAY6C,WAGhBX,SAASL,iBAAiB,YAAae,oBACvCV,SAASL,iBAAiB,UAAWe,qBAKrCI,GACAL,qBAAsB"} \ No newline at end of file diff --git a/calendar/amd/src/popover.js b/calendar/amd/src/popover.js index 5a8cba46573f1..1ffc56794370f 100644 --- a/calendar/amd/src/popover.js +++ b/calendar/amd/src/popover.js @@ -22,7 +22,7 @@ * @since 4.0 */ -import 'theme_boost/popover'; +import Popover from 'theme_boost/bootstrap/popover'; import jQuery from 'jquery'; import * as CalendarSelectors from 'core_calendar/selectors'; @@ -36,12 +36,10 @@ const isPopoverAvailable = (dateContainer) => { }; const isPopoverConfigured = new Map(); - const showPopover = target => { const dateContainer = target.closest(CalendarSelectors.elements.dateContainer); if (!isPopoverConfigured.has(dateContainer)) { - const dateEle = jQuery(target); - dateEle.popover({ + const config = { trigger: 'manual', placement: 'top', html: true, @@ -56,13 +54,14 @@ const showPopover = target => { return content.html(); }, 'animation': false, - }); + }; + new Popover(target, config); isPopoverConfigured.set(dateContainer, true); } if (isPopoverAvailable(dateContainer)) { - jQuery(target).popover('show'); + Popover.getInstance(target).show(); target.addEventListener('mouseleave', hidePopover); target.addEventListener('focusout', hidePopover); // Set up the hide function to the click event type. @@ -85,9 +84,9 @@ const hidePopover = e => { let removeListener = true; if (!isTargetActive && !isTargetHover) { - jQuery(target).popover('hide'); + Popover.getOrCreateInstance(target).hide(); } else if (isTargetClicked) { - jQuery(document.activeElement).popover('hide'); + Popover.getOrCreateInstance(document.activeElement).hide(); } else { removeListener = false; } diff --git a/calendar/templates/minicalendar_day_link.mustache b/calendar/templates/minicalendar_day_link.mustache index 2dd095bd964a6..283e8e2cdb756 100644 --- a/calendar/templates/minicalendar_day_link.mustache +++ b/calendar/templates/minicalendar_day_link.mustache @@ -50,8 +50,8 @@ {{#js}} require(['jquery'], function($) { require(['theme_boost/bootstrap/popover'], function() { - var target = $("#calendar-day-popover-link-{{courseid}}-{{year}}-{{yday}}-{{uniqid}}"); - target.popover({ + const target = document.getElementById$("calendar-day-popover-link-{{courseid}}-{{year}}-{{yday}}-{{uniqid}}"); + new Popover(target, { content: function() { var source = target.next().find("> *:not('.hidden')"); var content = $('
'); diff --git a/course/amd/build/local/activitychooser/dialogue.min.js b/course/amd/build/local/activitychooser/dialogue.min.js index 4945e3b9d8b64..e5d3ed1cc410d 100644 --- a/course/amd/build/local/activitychooser/dialogue.min.js +++ b/course/amd/build/local/activitychooser/dialogue.min.js @@ -1,3 +1,3 @@ -define("core_course/local/activitychooser/dialogue",["exports","jquery","core/modal_events","core_course/local/activitychooser/selectors","core/templates","core/key_codes","core/loadingicon","core_course/local/activitychooser/repository","core/notification","core/utils"],(function(_exports,_jquery,ModalEvents,_selectors,Templates,_key_codes,_loadingicon,Repository,_notification,_utils){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.displayChooser=void 0,_jquery=_interopRequireDefault(_jquery),ModalEvents=_interopRequireWildcard(ModalEvents),_selectors=_interopRequireDefault(_selectors),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),_notification=_interopRequireDefault(_notification);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}const showModuleHelp=function(carousel,moduleData){let modal=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;null!==modal&&!0===moduleData.showFooter&&modal.setFooter(Templates.render("core_course/local/activitychooser/footer_partial",moduleData));const help=carousel.find(_selectors.default.regions.help)[0];help.innerHTML="",help.classList.add("m-auto");const spinnerPromise=(0,_loadingicon.addIconToContainer)(help);let transitionPromiseResolver=null;const transitionPromise=new Promise((resolve=>{transitionPromiseResolver=resolve})),contentPromise=Templates.renderForPromise("core_course/local/activitychooser/help",moduleData);Promise.all([contentPromise,spinnerPromise,transitionPromise]).then((_ref=>{let[{html:html,js:js}]=_ref;return Templates.replaceNodeContents(help,html,js)})).then((()=>(help.querySelector(_selectors.default.regions.chooserSummary.header).focus(),help))).catch(_notification.default.exception),carousel.one("slid.bs.carousel",(()=>{transitionPromiseResolver()})),carousel.carousel("next")},registerListenerEvents=(modal,mappedModules,partialFavourite,footerData)=>{const bodyClickListener=async e=>{if(e.target.closest(_selectors.default.actions.optionActions.showSummary)){const carousel=(0,_jquery.default)(modal.getBody()[0].querySelector(_selectors.default.regions.carousel)),moduleName=e.target.closest(_selectors.default.regions.chooserOption.container).dataset.modname,moduleData=mappedModules.get(moduleName);moduleData.showFooter=modal.hasFooterContent(),showModuleHelp(carousel,moduleData,modal)}if(e.target.closest(_selectors.default.actions.optionActions.manageFavourite)){const caller=e.target.closest(_selectors.default.actions.optionActions.manageFavourite);await(async(modalBody,caller,partialFavourite)=>{const isFavourite=caller.dataset.favourited,id=caller.dataset.id,name=caller.dataset.name,internal=caller.dataset.internal;"true"===isFavourite?(await Repository.unfavouriteModule(name,id),partialFavourite(internal,!1,modalBody)):(await Repository.favouriteModule(name,id),partialFavourite(internal,!0,modalBody))})(modal.getBody()[0],caller,partialFavourite);const activeSectionId=modal.getBody()[0].querySelector(_selectors.default.elements.activetab).getAttribute("href"),sectionChooserOptions=modal.getBody()[0].querySelector(_selectors.default.regions.getSectionChooserOptions(activeSectionId)),firstChooserOption=sectionChooserOptions.querySelector(_selectors.default.regions.chooserOption.container);toggleFocusableChooserOption(firstChooserOption,!0),initChooserOptionsKeyboardNavigation(modal.getBody()[0],mappedModules,sectionChooserOptions,modal)}if(e.target.matches(_selectors.default.actions.closeOption)){const carousel=(0,_jquery.default)(modal.getBody()[0].querySelector(_selectors.default.regions.carousel));carousel.carousel("prev"),carousel.on("slid.bs.carousel",(()=>{modal.getBody()[0].querySelector(_selectors.default.regions.modules).querySelector(_selectors.default.regions.getModuleSelector(e.target.dataset.modname)).focus()}))}if(e.target.closest(_selectors.default.actions.clearSearch)){const searchInput=modal.getBody()[0].querySelector(_selectors.default.actions.search);searchInput.value="",searchInput.focus(),toggleSearchResultsView(modal,mappedModules,searchInput.value)}},footerClickListener=async e=>{if(!0===footerData.footer){const footerjs=await(pluginName=footerData.customfooterjs,"function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require([pluginName],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require(pluginName)):Promise.resolve(_systemImportTransformerGlobalIdentifier[pluginName]));await footerjs.footerClickListener(e,footerData,modal)}var pluginName};modal.getBodyPromise().then((body=>body[0])).then((body=>((0,_jquery.default)(body.querySelector(_selectors.default.regions.carousel)).carousel({interval:!1,pause:!0,keyboard:!1}),body))).then((body=>(body.addEventListener("click",bodyClickListener),body))).then((body=>{const searchInput=body.querySelector(_selectors.default.actions.search);return searchInput.addEventListener("input",(0,_utils.debounce)((()=>{toggleSearchResultsView(modal,mappedModules,searchInput.value)}),300)),body})).then((body=>{const activeSectionId=body.querySelector(_selectors.default.elements.activetab).getAttribute("href"),sectionChooserOptions=body.querySelector(_selectors.default.regions.getSectionChooserOptions(activeSectionId)),firstChooserOption=sectionChooserOptions.querySelector(_selectors.default.regions.chooserOption.container);return toggleFocusableChooserOption(firstChooserOption,!0),initChooserOptionsKeyboardNavigation(body,mappedModules,sectionChooserOptions,modal),body})).catch(),modal.getFooterPromise().then((footer=>footer[0])).then((footer=>(footer.addEventListener("click",footerClickListener),footer))).catch()},initChooserOptionsKeyboardNavigation=function(body,mappedModules,chooserOptionsContainer){let modal=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;const chooserOptions=chooserOptionsContainer.querySelectorAll(_selectors.default.regions.chooserOption.container);Array.from(chooserOptions).forEach((element=>element.addEventListener("keydown",(e=>{if((e.keyCode===_key_codes.enter||e.keyCode===_key_codes.space)&&e.target.matches(_selectors.default.actions.optionActions.showSummary)){e.preventDefault();const moduleName=e.target.closest(_selectors.default.regions.chooserOption.container).dataset.modname,moduleData=mappedModules.get(moduleName),carousel=(0,_jquery.default)(body.querySelector(_selectors.default.regions.carousel));carousel.carousel({interval:!1,pause:!0,keyboard:!1}),moduleData.showFooter=modal.hasFooterContent(),showModuleHelp(carousel,moduleData,modal)}if(e.keyCode===_key_codes.arrowRight){e.preventDefault();const currentOption=e.target.closest(_selectors.default.regions.chooserOption.container),nextOption=currentOption.nextElementSibling,firstOption=chooserOptionsContainer.firstElementChild,toFocusOption=clickErrorHandler(nextOption,firstOption);focusChooserOption(toFocusOption,currentOption)}if(e.keyCode===_key_codes.arrowLeft){e.preventDefault();const currentOption=e.target.closest(_selectors.default.regions.chooserOption.container),previousOption=currentOption.previousElementSibling,lastOption=chooserOptionsContainer.lastElementChild,toFocusOption=clickErrorHandler(previousOption,lastOption);focusChooserOption(toFocusOption,currentOption)}if(e.keyCode===_key_codes.home){e.preventDefault();const currentOption=e.target.closest(_selectors.default.regions.chooserOption.container),firstOption=chooserOptionsContainer.firstElementChild;focusChooserOption(firstOption,currentOption)}if(e.keyCode===_key_codes.end){e.preventDefault();const currentOption=e.target.closest(_selectors.default.regions.chooserOption.container),lastOption=chooserOptionsContainer.lastElementChild;focusChooserOption(lastOption,currentOption)}}))))},focusChooserOption=function(currentChooserOption){let previousChooserOption=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;null!==previousChooserOption&&toggleFocusableChooserOption(previousChooserOption,!1),toggleFocusableChooserOption(currentChooserOption,!0),currentChooserOption.focus()},toggleFocusableChooserOption=(chooserOption,isFocusable)=>{const chooserOptionLink=chooserOption.querySelector(_selectors.default.actions.addChooser),chooserOptionHelp=chooserOption.querySelector(_selectors.default.actions.optionActions.showSummary),chooserOptionFavourite=chooserOption.querySelector(_selectors.default.actions.optionActions.manageFavourite);isFocusable?(chooserOption.tabIndex=0,chooserOptionLink.tabIndex=0,chooserOptionHelp.tabIndex=0,chooserOptionFavourite.tabIndex=0):(chooserOption.tabIndex=-1,chooserOptionLink.tabIndex=-1,chooserOptionHelp.tabIndex=-1,chooserOptionFavourite.tabIndex=-1)},clickErrorHandler=(item,fallback)=>null!==item?item:fallback,toggleSearchResultsView=async(modal,mappedModules,searchQuery)=>{const modalBody=modal.getBody()[0],searchResultsContainer=modalBody.querySelector(_selectors.default.regions.searchResults),chooserContainer=modalBody.querySelector(_selectors.default.regions.chooser),clearSearchButton=modalBody.querySelector(_selectors.default.actions.clearSearch);if(searchQuery.length>0){const searchResultsData=searchModules(mappedModules,searchQuery);await(async(searchResultsContainer,searchResultsData)=>{const templateData={searchresultsnumber:searchResultsData.length,searchresults:searchResultsData},{html:html,js:js}=await Templates.renderForPromise("core_course/local/activitychooser/search_results",templateData);await Templates.replaceNodeContents(searchResultsContainer,html,js)})(searchResultsContainer,searchResultsData);const searchResultItemsContainer=searchResultsContainer.querySelector(_selectors.default.regions.searchResultItems),firstSearchResultItem=searchResultItemsContainer.querySelector(_selectors.default.regions.chooserOption.container);firstSearchResultItem&&(toggleFocusableChooserOption(firstSearchResultItem,!0),initChooserOptionsKeyboardNavigation(modalBody,mappedModules,searchResultItemsContainer,modal)),clearSearchButton.classList.remove("d-none"),chooserContainer.setAttribute("hidden","hidden"),searchResultsContainer.removeAttribute("hidden")}else clearSearchButton.classList.add("d-none"),searchResultsContainer.setAttribute("hidden","hidden"),chooserContainer.removeAttribute("hidden")},searchModules=(modules,searchTerm)=>{if(""===searchTerm)return modules;searchTerm=searchTerm.toLowerCase();const searchResults=[];return modules.forEach((activity=>{const activityName=activity.title.toLowerCase(),activityDesc=activity.help.toLowerCase();(activityName.includes(searchTerm)||activityDesc.includes(searchTerm))&&searchResults.push(activity)})),searchResults},disableFocusAllChooserOptions=sectionChooserOptions=>{sectionChooserOptions.querySelectorAll(_selectors.default.regions.chooserOption.container).forEach((chooserOption=>{toggleFocusableChooserOption(chooserOption,!1)}))};_exports.displayChooser=(modalPromise,sectionModules,partialFavourite,footerData)=>{const mappedModules=new Map;sectionModules.forEach((module=>{mappedModules.set(module.componentname+"_"+module.link,module)})),modalPromise.then((modal=>(registerListenerEvents(modal,mappedModules,partialFavourite,footerData),((modal,mappedModules)=>{modal.getModal()[0].tabIndex=-1,modal.getBodyPromise().then((body=>{(0,_jquery.default)(_selectors.default.elements.tab).on("shown.bs.tab",(e=>{const activeSectionId=e.target.getAttribute("href"),activeSectionChooserOptions=body[0].querySelector(_selectors.default.regions.getSectionChooserOptions(activeSectionId)),firstChooserOption=activeSectionChooserOptions.querySelector(_selectors.default.regions.chooserOption.container),prevActiveSectionId=e.relatedTarget.getAttribute("href"),prevActiveSectionChooserOptions=body[0].querySelector(_selectors.default.regions.getSectionChooserOptions(prevActiveSectionId));disableFocusAllChooserOptions(prevActiveSectionChooserOptions),toggleFocusableChooserOption(firstChooserOption,!0),initChooserOptionsKeyboardNavigation(body[0],mappedModules,activeSectionChooserOptions,modal)}))})).catch(_notification.default.exception)})(modal,mappedModules),modal.getRoot().on(ModalEvents.hidden,(()=>{modal.destroy()})),modal))).catch()}})); +define("core_course/local/activitychooser/dialogue",["exports","theme_boost/bootstrap/carousel","core/modal_events","core_course/local/activitychooser/selectors","core/templates","core/key_codes","core/loadingicon","core_course/local/activitychooser/repository","core/notification","core/utils"],(function(_exports,_carousel,ModalEvents,_selectors,Templates,_key_codes,_loadingicon,Repository,_notification,_utils){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.displayChooser=void 0,_carousel=_interopRequireDefault(_carousel),ModalEvents=_interopRequireWildcard(ModalEvents),_selectors=_interopRequireDefault(_selectors),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),_notification=_interopRequireDefault(_notification);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}const showModuleHelp=function(carousel,moduleData){let modal=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;null!==modal&&!0===moduleData.showFooter&&modal.setFooter(Templates.render("core_course/local/activitychooser/footer_partial",moduleData));const help=carousel.querySelector(_selectors.default.regions.help);help.innerHTML="",help.classList.add("m-auto");const spinnerPromise=(0,_loadingicon.addIconToContainer)(help);let transitionPromiseResolver=null;const transitionPromise=new Promise((resolve=>{transitionPromiseResolver=resolve})),contentPromise=Templates.renderForPromise("core_course/local/activitychooser/help",moduleData);Promise.all([contentPromise,spinnerPromise,transitionPromise]).then((_ref=>{let[{html:html,js:js}]=_ref;return Templates.replaceNodeContents(help,html,js)})).then((()=>(help.querySelector(_selectors.default.regions.chooserSummary.header).focus(),help))).catch(_notification.default.exception),carousel.addEventListener("slid.bs.carousel",(()=>{transitionPromiseResolver()}),{once:!0}),_carousel.default.getInstance(carousel).next()},registerListenerEvents=(modal,mappedModules,partialFavourite,footerData)=>{const bodyClickListener=async e=>{if(e.target.closest(_selectors.default.actions.optionActions.showSummary)){const carousel=modal.getBody()[0].querySelector(_selectors.default.regions.carousel),moduleName=e.target.closest(_selectors.default.regions.chooserOption.container).dataset.modname,moduleData=mappedModules.get(moduleName);moduleData.showFooter=modal.hasFooterContent(),showModuleHelp(carousel,moduleData,modal)}if(e.target.closest(_selectors.default.actions.optionActions.manageFavourite)){const caller=e.target.closest(_selectors.default.actions.optionActions.manageFavourite);await(async(modalBody,caller,partialFavourite)=>{const isFavourite=caller.dataset.favourited,id=caller.dataset.id,name=caller.dataset.name,internal=caller.dataset.internal;"true"===isFavourite?(await Repository.unfavouriteModule(name,id),partialFavourite(internal,!1,modalBody)):(await Repository.favouriteModule(name,id),partialFavourite(internal,!0,modalBody))})(modal.getBody()[0],caller,partialFavourite);const activeSectionId=modal.getBody()[0].querySelector(_selectors.default.elements.activetab).getAttribute("href"),sectionChooserOptions=modal.getBody()[0].querySelector(_selectors.default.regions.getSectionChooserOptions(activeSectionId)),firstChooserOption=sectionChooserOptions.querySelector(_selectors.default.regions.chooserOption.container);toggleFocusableChooserOption(firstChooserOption,!0),initChooserOptionsKeyboardNavigation(modal.getBody()[0],mappedModules,sectionChooserOptions,modal)}if(e.target.matches(_selectors.default.actions.closeOption)){const carousel=modal.getBody()[0].querySelector(_selectors.default.regions.carousel);_carousel.default.getInstance(carousel).prev(),carousel.addEventListener("slid.bs.carousel",(()=>{modal.getBody()[0].querySelector(_selectors.default.regions.modules).querySelector(_selectors.default.regions.getModuleSelector(e.target.dataset.modname)).focus()}))}if(e.target.closest(_selectors.default.actions.clearSearch)){const searchInput=modal.getBody()[0].querySelector(_selectors.default.actions.search);searchInput.value="",searchInput.focus(),toggleSearchResultsView(modal,mappedModules,searchInput.value)}},footerClickListener=async e=>{if(!0===footerData.footer){const footerjs=await(pluginName=footerData.customfooterjs,"function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require([pluginName],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require(pluginName)):Promise.resolve(_systemImportTransformerGlobalIdentifier[pluginName]));await footerjs.footerClickListener(e,footerData,modal)}var pluginName};modal.getBodyPromise().then((body=>body[0])).then((body=>{const carousel=document.querySelector(_selectors.default.regions.carousel);return new _carousel.default(carousel,{interval:!1,pause:!0,keyboard:!1}),body})).then((body=>(body.addEventListener("click",bodyClickListener),body))).then((body=>{const searchInput=body.querySelector(_selectors.default.actions.search);return searchInput.addEventListener("input",(0,_utils.debounce)((()=>{toggleSearchResultsView(modal,mappedModules,searchInput.value)}),300)),body})).then((body=>{const activeSectionId=body.querySelector(_selectors.default.elements.activetab).getAttribute("href"),sectionChooserOptions=body.querySelector(_selectors.default.regions.getSectionChooserOptions(activeSectionId)),firstChooserOption=sectionChooserOptions.querySelector(_selectors.default.regions.chooserOption.container);return toggleFocusableChooserOption(firstChooserOption,!0),initChooserOptionsKeyboardNavigation(body,mappedModules,sectionChooserOptions,modal),body})).catch(_notification.default.exception),modal.getFooterPromise().then((footer=>footer[0])).then((footer=>(footer.addEventListener("click",footerClickListener),footer))).catch(_notification.default.exception)},initChooserOptionsKeyboardNavigation=function(body,mappedModules,chooserOptionsContainer){let modal=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;const chooserOptions=chooserOptionsContainer.querySelectorAll(_selectors.default.regions.chooserOption.container);Array.from(chooserOptions).forEach((element=>element.addEventListener("keydown",(e=>{if((e.keyCode===_key_codes.enter||e.keyCode===_key_codes.space)&&e.target.matches(_selectors.default.actions.optionActions.showSummary)){e.preventDefault();const moduleName=e.target.closest(_selectors.default.regions.chooserOption.container).dataset.modname,moduleData=mappedModules.get(moduleName),carousel=document.querySelector(_selectors.default.regions.carousel);new _carousel.default({interval:!1,pause:!0,keyboard:!1}),moduleData.showFooter=modal.hasFooterContent(),showModuleHelp(carousel,moduleData,modal)}if(e.keyCode===_key_codes.arrowRight){e.preventDefault();const currentOption=e.target.closest(_selectors.default.regions.chooserOption.container),nextOption=currentOption.nextElementSibling,firstOption=chooserOptionsContainer.firstElementChild,toFocusOption=clickErrorHandler(nextOption,firstOption);focusChooserOption(toFocusOption,currentOption)}if(e.keyCode===_key_codes.arrowLeft){e.preventDefault();const currentOption=e.target.closest(_selectors.default.regions.chooserOption.container),previousOption=currentOption.previousElementSibling,lastOption=chooserOptionsContainer.lastElementChild,toFocusOption=clickErrorHandler(previousOption,lastOption);focusChooserOption(toFocusOption,currentOption)}if(e.keyCode===_key_codes.home){e.preventDefault();const currentOption=e.target.closest(_selectors.default.regions.chooserOption.container),firstOption=chooserOptionsContainer.firstElementChild;focusChooserOption(firstOption,currentOption)}if(e.keyCode===_key_codes.end){e.preventDefault();const currentOption=e.target.closest(_selectors.default.regions.chooserOption.container),lastOption=chooserOptionsContainer.lastElementChild;focusChooserOption(lastOption,currentOption)}}))))},focusChooserOption=function(currentChooserOption){let previousChooserOption=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;null!==previousChooserOption&&toggleFocusableChooserOption(previousChooserOption,!1),toggleFocusableChooserOption(currentChooserOption,!0),currentChooserOption.focus()},toggleFocusableChooserOption=(chooserOption,isFocusable)=>{const chooserOptionLink=chooserOption.querySelector(_selectors.default.actions.addChooser),chooserOptionHelp=chooserOption.querySelector(_selectors.default.actions.optionActions.showSummary),chooserOptionFavourite=chooserOption.querySelector(_selectors.default.actions.optionActions.manageFavourite);isFocusable?(chooserOption.tabIndex=0,chooserOptionLink.tabIndex=0,chooserOptionHelp.tabIndex=0,chooserOptionFavourite.tabIndex=0):(chooserOption.tabIndex=-1,chooserOptionLink.tabIndex=-1,chooserOptionHelp.tabIndex=-1,chooserOptionFavourite.tabIndex=-1)},clickErrorHandler=(item,fallback)=>null!==item?item:fallback,toggleSearchResultsView=async(modal,mappedModules,searchQuery)=>{const modalBody=modal.getBody()[0],searchResultsContainer=modalBody.querySelector(_selectors.default.regions.searchResults),chooserContainer=modalBody.querySelector(_selectors.default.regions.chooser),clearSearchButton=modalBody.querySelector(_selectors.default.actions.clearSearch);if(searchQuery.length>0){const searchResultsData=searchModules(mappedModules,searchQuery);await(async(searchResultsContainer,searchResultsData)=>{const templateData={searchresultsnumber:searchResultsData.length,searchresults:searchResultsData},{html:html,js:js}=await Templates.renderForPromise("core_course/local/activitychooser/search_results",templateData);await Templates.replaceNodeContents(searchResultsContainer,html,js)})(searchResultsContainer,searchResultsData);const searchResultItemsContainer=searchResultsContainer.querySelector(_selectors.default.regions.searchResultItems),firstSearchResultItem=searchResultItemsContainer.querySelector(_selectors.default.regions.chooserOption.container);firstSearchResultItem&&(toggleFocusableChooserOption(firstSearchResultItem,!0),initChooserOptionsKeyboardNavigation(modalBody,mappedModules,searchResultItemsContainer,modal)),clearSearchButton.classList.remove("d-none"),chooserContainer.setAttribute("hidden","hidden"),searchResultsContainer.removeAttribute("hidden")}else clearSearchButton.classList.add("d-none"),searchResultsContainer.setAttribute("hidden","hidden"),chooserContainer.removeAttribute("hidden")},searchModules=(modules,searchTerm)=>{if(""===searchTerm)return modules;searchTerm=searchTerm.toLowerCase();const searchResults=[];return modules.forEach((activity=>{const activityName=activity.title.toLowerCase(),activityDesc=activity.help.toLowerCase();(activityName.includes(searchTerm)||activityDesc.includes(searchTerm))&&searchResults.push(activity)})),searchResults},disableFocusAllChooserOptions=sectionChooserOptions=>{sectionChooserOptions.querySelectorAll(_selectors.default.regions.chooserOption.container).forEach((chooserOption=>{toggleFocusableChooserOption(chooserOption,!1)}))};_exports.displayChooser=(modalPromise,sectionModules,partialFavourite,footerData)=>{const mappedModules=new Map;sectionModules.forEach((module=>{mappedModules.set(module.componentname+"_"+module.link,module)})),modalPromise.then((modal=>(registerListenerEvents(modal,mappedModules,partialFavourite,footerData),((modal,mappedModules)=>{modal.getModal()[0].tabIndex=-1,modal.getBodyPromise().then((body=>{document.querySelectorAll(_selectors.default.elements.tab).forEach((tab=>{tab.addEventListener("shown.bs.tab",(e=>{const activeSectionId=e.target.getAttribute("href"),activeSectionChooserOptions=body[0].querySelector(_selectors.default.regions.getSectionChooserOptions(activeSectionId)),firstChooserOption=activeSectionChooserOptions.querySelector(_selectors.default.regions.chooserOption.container),prevActiveSectionId=e.relatedTarget.getAttribute("href"),prevActiveSectionChooserOptions=body[0].querySelector(_selectors.default.regions.getSectionChooserOptions(prevActiveSectionId));disableFocusAllChooserOptions(prevActiveSectionChooserOptions),toggleFocusableChooserOption(firstChooserOption,!0),initChooserOptionsKeyboardNavigation(body[0],mappedModules,activeSectionChooserOptions,modal)}))}))})).catch(_notification.default.exception)})(modal,mappedModules),modal.getRoot().on(ModalEvents.hidden,(()=>{modal.destroy()})),modal))).catch(_notification.default.exception)}})); //# sourceMappingURL=dialogue.min.js.map \ No newline at end of file diff --git a/course/amd/build/local/activitychooser/dialogue.min.js.map b/course/amd/build/local/activitychooser/dialogue.min.js.map index 311cbac79965e..c95458bf2bb0b 100644 --- a/course/amd/build/local/activitychooser/dialogue.min.js.map +++ b/course/amd/build/local/activitychooser/dialogue.min.js.map @@ -1 +1 @@ -{"version":3,"file":"dialogue.min.js","sources":["../../../src/local/activitychooser/dialogue.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 * A type of dialogue used as for choosing options.\n *\n * @module core_course/local/activitychooser/dialogue\n * @copyright 2019 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as ModalEvents from 'core/modal_events';\nimport selectors from 'core_course/local/activitychooser/selectors';\nimport * as Templates from 'core/templates';\nimport {end, arrowLeft, arrowRight, home, enter, space} from 'core/key_codes';\nimport {addIconToContainer} from 'core/loadingicon';\nimport * as Repository from 'core_course/local/activitychooser/repository';\nimport Notification from 'core/notification';\nimport {debounce} from 'core/utils';\nconst getPlugin = pluginName => import(pluginName);\n\n/**\n * Given an event from the main module 'page' navigate to it's help section via a carousel.\n *\n * @method showModuleHelp\n * @param {jQuery} carousel Our initialized carousel to manipulate\n * @param {Object} moduleData Data of the module to carousel to\n * @param {jQuery} modal We need to figure out if the current modal has a footer.\n */\nconst showModuleHelp = (carousel, moduleData, modal = null) => {\n // If we have a real footer then we need to change temporarily.\n if (modal !== null && moduleData.showFooter === true) {\n modal.setFooter(Templates.render('core_course/local/activitychooser/footer_partial', moduleData));\n }\n const help = carousel.find(selectors.regions.help)[0];\n help.innerHTML = '';\n help.classList.add('m-auto');\n\n // Add a spinner.\n const spinnerPromise = addIconToContainer(help);\n\n // Used later...\n let transitionPromiseResolver = null;\n const transitionPromise = new Promise(resolve => {\n transitionPromiseResolver = resolve;\n });\n\n // Build up the html & js ready to place into the help section.\n const contentPromise = Templates.renderForPromise('core_course/local/activitychooser/help', moduleData);\n\n // Wait for the content to be ready, and for the transition to be complet.\n Promise.all([contentPromise, spinnerPromise, transitionPromise])\n .then(([{html, js}]) => Templates.replaceNodeContents(help, html, js))\n .then(() => {\n help.querySelector(selectors.regions.chooserSummary.header).focus();\n return help;\n })\n .catch(Notification.exception);\n\n // Move to the next slide, and resolve the transition promise when it's done.\n carousel.one('slid.bs.carousel', () => {\n transitionPromiseResolver();\n });\n // Trigger the transition between 'pages'.\n carousel.carousel('next');\n};\n\n/**\n * Given a user wants to change the favourite state of a module we either add or remove the status.\n * We also propergate this change across our map of modals.\n *\n * @method manageFavouriteState\n * @param {HTMLElement} modalBody The DOM node of the modal to manipulate\n * @param {HTMLElement} caller\n * @param {Function} partialFavourite Partially applied function we need to manage favourite status\n */\nconst manageFavouriteState = async(modalBody, caller, partialFavourite) => {\n const isFavourite = caller.dataset.favourited;\n const id = caller.dataset.id;\n const name = caller.dataset.name;\n const internal = caller.dataset.internal;\n // Switch on fave or not.\n if (isFavourite === 'true') {\n await Repository.unfavouriteModule(name, id);\n\n partialFavourite(internal, false, modalBody);\n } else {\n await Repository.favouriteModule(name, id);\n\n partialFavourite(internal, true, modalBody);\n }\n\n};\n\n/**\n * Register chooser related event listeners.\n *\n * @method registerListenerEvents\n * @param {Promise} modal Our modal that we are working with\n * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}\n * @param {Function} partialFavourite Partially applied function we need to manage favourite status\n * @param {Object} footerData Our base footer object.\n */\nconst registerListenerEvents = (modal, mappedModules, partialFavourite, footerData) => {\n const bodyClickListener = async(e) => {\n if (e.target.closest(selectors.actions.optionActions.showSummary)) {\n const carousel = $(modal.getBody()[0].querySelector(selectors.regions.carousel));\n\n const module = e.target.closest(selectors.regions.chooserOption.container);\n const moduleName = module.dataset.modname;\n const moduleData = mappedModules.get(moduleName);\n // We need to know if the overall modal has a footer so we know when to show a real / vs fake footer.\n moduleData.showFooter = modal.hasFooterContent();\n showModuleHelp(carousel, moduleData, modal);\n }\n\n if (e.target.closest(selectors.actions.optionActions.manageFavourite)) {\n const caller = e.target.closest(selectors.actions.optionActions.manageFavourite);\n await manageFavouriteState(modal.getBody()[0], caller, partialFavourite);\n const activeSectionId = modal.getBody()[0].querySelector(selectors.elements.activetab).getAttribute(\"href\");\n const sectionChooserOptions = modal.getBody()[0]\n .querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = sectionChooserOptions\n .querySelector(selectors.regions.chooserOption.container);\n toggleFocusableChooserOption(firstChooserOption, true);\n initChooserOptionsKeyboardNavigation(modal.getBody()[0], mappedModules, sectionChooserOptions, modal);\n }\n\n // From the help screen go back to the module overview.\n if (e.target.matches(selectors.actions.closeOption)) {\n const carousel = $(modal.getBody()[0].querySelector(selectors.regions.carousel));\n\n // Trigger the transition between 'pages'.\n carousel.carousel('prev');\n carousel.on('slid.bs.carousel', () => {\n const allModules = modal.getBody()[0].querySelector(selectors.regions.modules);\n const caller = allModules.querySelector(selectors.regions.getModuleSelector(e.target.dataset.modname));\n caller.focus();\n });\n }\n\n // The \"clear search\" button is triggered.\n if (e.target.closest(selectors.actions.clearSearch)) {\n // Clear the entered search query in the search bar and hide the search results container.\n const searchInput = modal.getBody()[0].querySelector(selectors.actions.search);\n searchInput.value = \"\";\n searchInput.focus();\n toggleSearchResultsView(modal, mappedModules, searchInput.value);\n }\n };\n\n // We essentially have two types of footer.\n // A fake one that is handled within the template for chooser_help and then all of the stuff for\n // modal.footer. We need to ensure we know exactly what type of footer we are using so we know what we\n // need to manage. The below code handles a real footer going to a mnet carousel item.\n const footerClickListener = async(e) => {\n if (footerData.footer === true) {\n const footerjs = await getPlugin(footerData.customfooterjs);\n await footerjs.footerClickListener(e, footerData, modal);\n }\n };\n\n modal.getBodyPromise()\n\n // The return value of getBodyPromise is a jquery object containing the body NodeElement.\n .then(body => body[0])\n\n // Set up the carousel.\n .then(body => {\n $(body.querySelector(selectors.regions.carousel))\n .carousel({\n interval: false,\n pause: true,\n keyboard: false\n });\n\n return body;\n })\n\n // Add the listener for clicks on the body.\n .then(body => {\n body.addEventListener('click', bodyClickListener);\n return body;\n })\n\n // Add a listener for an input change in the activity chooser's search bar.\n .then(body => {\n const searchInput = body.querySelector(selectors.actions.search);\n // The search input is triggered.\n searchInput.addEventListener('input', debounce(() => {\n // Display the search results.\n toggleSearchResultsView(modal, mappedModules, searchInput.value);\n }, 300));\n return body;\n })\n\n // Register event listeners related to the keyboard navigation controls.\n .then(body => {\n // Get the active chooser options section.\n const activeSectionId = body.querySelector(selectors.elements.activetab).getAttribute(\"href\");\n const sectionChooserOptions = body.querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = sectionChooserOptions.querySelector(selectors.regions.chooserOption.container);\n\n toggleFocusableChooserOption(firstChooserOption, true);\n initChooserOptionsKeyboardNavigation(body, mappedModules, sectionChooserOptions, modal);\n\n return body;\n })\n .catch();\n\n modal.getFooterPromise()\n\n // The return value of getBodyPromise is a jquery object containing the body NodeElement.\n .then(footer => footer[0])\n // Add the listener for clicks on the footer.\n .then(footer => {\n footer.addEventListener('click', footerClickListener);\n return footer;\n })\n .catch();\n};\n\n/**\n * Initialise the keyboard navigation controls for the chooser options.\n *\n * @method initChooserOptionsKeyboardNavigation\n * @param {HTMLElement} body Our modal that we are working with\n * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}\n * @param {HTMLElement} chooserOptionsContainer The section that contains the chooser items\n * @param {Object} modal Our created modal for the section\n */\nconst initChooserOptionsKeyboardNavigation = (body, mappedModules, chooserOptionsContainer, modal = null) => {\n const chooserOptions = chooserOptionsContainer.querySelectorAll(selectors.regions.chooserOption.container);\n\n Array.from(chooserOptions).forEach((element) => {\n return element.addEventListener('keydown', (e) => {\n\n // Check for enter/ space triggers for showing the help.\n if (e.keyCode === enter || e.keyCode === space) {\n if (e.target.matches(selectors.actions.optionActions.showSummary)) {\n e.preventDefault();\n const module = e.target.closest(selectors.regions.chooserOption.container);\n const moduleName = module.dataset.modname;\n const moduleData = mappedModules.get(moduleName);\n const carousel = $(body.querySelector(selectors.regions.carousel));\n carousel.carousel({\n interval: false,\n pause: true,\n keyboard: false\n });\n\n // We need to know if the overall modal has a footer so we know when to show a real / vs fake footer.\n moduleData.showFooter = modal.hasFooterContent();\n showModuleHelp(carousel, moduleData, modal);\n }\n }\n\n // Next.\n if (e.keyCode === arrowRight) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const nextOption = currentOption.nextElementSibling;\n const firstOption = chooserOptionsContainer.firstElementChild;\n const toFocusOption = clickErrorHandler(nextOption, firstOption);\n focusChooserOption(toFocusOption, currentOption);\n }\n\n // Previous.\n if (e.keyCode === arrowLeft) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const previousOption = currentOption.previousElementSibling;\n const lastOption = chooserOptionsContainer.lastElementChild;\n const toFocusOption = clickErrorHandler(previousOption, lastOption);\n focusChooserOption(toFocusOption, currentOption);\n }\n\n if (e.keyCode === home) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const firstOption = chooserOptionsContainer.firstElementChild;\n focusChooserOption(firstOption, currentOption);\n }\n\n if (e.keyCode === end) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const lastOption = chooserOptionsContainer.lastElementChild;\n focusChooserOption(lastOption, currentOption);\n }\n });\n });\n};\n\n/**\n * Focus on a chooser option element and remove the previous chooser element from the focus order\n *\n * @method focusChooserOption\n * @param {HTMLElement} currentChooserOption The current chooser option element that we want to focus\n * @param {HTMLElement|null} previousChooserOption The previous focused option element\n */\nconst focusChooserOption = (currentChooserOption, previousChooserOption = null) => {\n if (previousChooserOption !== null) {\n toggleFocusableChooserOption(previousChooserOption, false);\n }\n\n toggleFocusableChooserOption(currentChooserOption, true);\n currentChooserOption.focus();\n};\n\n/**\n * Add or remove a chooser option from the focus order.\n *\n * @method toggleFocusableChooserOption\n * @param {HTMLElement} chooserOption The chooser option element which should be added or removed from the focus order\n * @param {Boolean} isFocusable Whether the chooser element is focusable or not\n */\nconst toggleFocusableChooserOption = (chooserOption, isFocusable) => {\n const chooserOptionLink = chooserOption.querySelector(selectors.actions.addChooser);\n const chooserOptionHelp = chooserOption.querySelector(selectors.actions.optionActions.showSummary);\n const chooserOptionFavourite = chooserOption.querySelector(selectors.actions.optionActions.manageFavourite);\n\n if (isFocusable) {\n // Set tabindex to 0 to add current chooser option element to the focus order.\n chooserOption.tabIndex = 0;\n chooserOptionLink.tabIndex = 0;\n chooserOptionHelp.tabIndex = 0;\n chooserOptionFavourite.tabIndex = 0;\n } else {\n // Set tabindex to -1 to remove the previous chooser option element from the focus order.\n chooserOption.tabIndex = -1;\n chooserOptionLink.tabIndex = -1;\n chooserOptionHelp.tabIndex = -1;\n chooserOptionFavourite.tabIndex = -1;\n }\n};\n\n/**\n * Small error handling function to make sure the navigated to object exists\n *\n * @method clickErrorHandler\n * @param {HTMLElement} item What we want to check exists\n * @param {HTMLElement} fallback If we dont match anything fallback the focus\n * @return {HTMLElement}\n */\nconst clickErrorHandler = (item, fallback) => {\n if (item !== null) {\n return item;\n } else {\n return fallback;\n }\n};\n\n/**\n * Render the search results in a defined container\n *\n * @method renderSearchResults\n * @param {HTMLElement} searchResultsContainer The container where the data should be rendered\n * @param {Object} searchResultsData Data containing the module items that satisfy the search criteria\n */\nconst renderSearchResults = async(searchResultsContainer, searchResultsData) => {\n const templateData = {\n 'searchresultsnumber': searchResultsData.length,\n 'searchresults': searchResultsData\n };\n // Build up the html & js ready to place into the help section.\n const {html, js} = await Templates.renderForPromise('core_course/local/activitychooser/search_results', templateData);\n await Templates.replaceNodeContents(searchResultsContainer, html, js);\n};\n\n/**\n * Toggle (display/hide) the search results depending on the value of the search query\n *\n * @method toggleSearchResultsView\n * @param {Object} modal Our created modal for the section\n * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}\n * @param {String} searchQuery The search query\n */\nconst toggleSearchResultsView = async(modal, mappedModules, searchQuery) => {\n const modalBody = modal.getBody()[0];\n const searchResultsContainer = modalBody.querySelector(selectors.regions.searchResults);\n const chooserContainer = modalBody.querySelector(selectors.regions.chooser);\n const clearSearchButton = modalBody.querySelector(selectors.actions.clearSearch);\n\n if (searchQuery.length > 0) { // Search query is present.\n const searchResultsData = searchModules(mappedModules, searchQuery);\n await renderSearchResults(searchResultsContainer, searchResultsData);\n const searchResultItemsContainer = searchResultsContainer.querySelector(selectors.regions.searchResultItems);\n const firstSearchResultItem = searchResultItemsContainer.querySelector(selectors.regions.chooserOption.container);\n if (firstSearchResultItem) {\n // Set the first result item to be focusable.\n toggleFocusableChooserOption(firstSearchResultItem, true);\n // Register keyboard events on the created search result items.\n initChooserOptionsKeyboardNavigation(modalBody, mappedModules, searchResultItemsContainer, modal);\n }\n // Display the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.remove('d-none');\n // Hide the default chooser options container.\n chooserContainer.setAttribute('hidden', 'hidden');\n // Display the search results container.\n searchResultsContainer.removeAttribute('hidden');\n } else { // Search query is not present.\n // Hide the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.add('d-none');\n // Hide the search results container.\n searchResultsContainer.setAttribute('hidden', 'hidden');\n // Display the default chooser options container.\n chooserContainer.removeAttribute('hidden');\n }\n};\n\n/**\n * Return the list of modules which have a name or description that matches the given search term.\n *\n * @method searchModules\n * @param {Array} modules List of available modules\n * @param {String} searchTerm The search term to match\n * @return {Array}\n */\nconst searchModules = (modules, searchTerm) => {\n if (searchTerm === '') {\n return modules;\n }\n searchTerm = searchTerm.toLowerCase();\n const searchResults = [];\n modules.forEach((activity) => {\n const activityName = activity.title.toLowerCase();\n const activityDesc = activity.help.toLowerCase();\n if (activityName.includes(searchTerm) || activityDesc.includes(searchTerm)) {\n searchResults.push(activity);\n }\n });\n\n return searchResults;\n};\n\n/**\n * Set up our tabindex information across the chooser.\n *\n * @method setupKeyboardAccessibility\n * @param {Promise} modal Our created modal for the section\n * @param {Map} mappedModules A map of all of the built module information\n */\nconst setupKeyboardAccessibility = (modal, mappedModules) => {\n modal.getModal()[0].tabIndex = -1;\n\n modal.getBodyPromise().then(body => {\n $(selectors.elements.tab).on('shown.bs.tab', (e) => {\n const activeSectionId = e.target.getAttribute(\"href\");\n const activeSectionChooserOptions = body[0]\n .querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = activeSectionChooserOptions\n .querySelector(selectors.regions.chooserOption.container);\n const prevActiveSectionId = e.relatedTarget.getAttribute(\"href\");\n const prevActiveSectionChooserOptions = body[0]\n .querySelector(selectors.regions.getSectionChooserOptions(prevActiveSectionId));\n\n // Disable the focus of every chooser option in the previous active section.\n disableFocusAllChooserOptions(prevActiveSectionChooserOptions);\n // Enable the focus of the first chooser option in the current active section.\n toggleFocusableChooserOption(firstChooserOption, true);\n initChooserOptionsKeyboardNavigation(body[0], mappedModules, activeSectionChooserOptions, modal);\n });\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Disable the focus of all chooser options in a specific container (section).\n *\n * @method disableFocusAllChooserOptions\n * @param {HTMLElement} sectionChooserOptions The section that contains the chooser items\n */\nconst disableFocusAllChooserOptions = (sectionChooserOptions) => {\n const allChooserOptions = sectionChooserOptions.querySelectorAll(selectors.regions.chooserOption.container);\n allChooserOptions.forEach((chooserOption) => {\n toggleFocusableChooserOption(chooserOption, false);\n });\n};\n\n/**\n * Display the module chooser.\n *\n * @method displayChooser\n * @param {Promise} modalPromise Our created modal for the section\n * @param {Array} sectionModules An array of all of the built module information\n * @param {Function} partialFavourite Partially applied function we need to manage favourite status\n * @param {Object} footerData Our base footer object.\n */\nexport const displayChooser = (modalPromise, sectionModules, partialFavourite, footerData) => {\n // Make a map so we can quickly fetch a specific module's object for either rendering or searching.\n const mappedModules = new Map();\n sectionModules.forEach((module) => {\n mappedModules.set(module.componentname + '_' + module.link, module);\n });\n\n // Register event listeners.\n modalPromise.then(modal => {\n registerListenerEvents(modal, mappedModules, partialFavourite, footerData);\n\n // We want to focus on the first chooser option element as soon as the modal is opened.\n setupKeyboardAccessibility(modal, mappedModules);\n\n // We want to focus on the action select when the dialog is closed.\n modal.getRoot().on(ModalEvents.hidden, () => {\n modal.destroy();\n });\n\n return modal;\n }).catch();\n};\n"],"names":["showModuleHelp","carousel","moduleData","modal","showFooter","setFooter","Templates","render","help","find","selectors","regions","innerHTML","classList","add","spinnerPromise","transitionPromiseResolver","transitionPromise","Promise","resolve","contentPromise","renderForPromise","all","then","_ref","html","js","replaceNodeContents","querySelector","chooserSummary","header","focus","catch","Notification","exception","one","registerListenerEvents","mappedModules","partialFavourite","footerData","bodyClickListener","async","e","target","closest","actions","optionActions","showSummary","getBody","moduleName","chooserOption","container","dataset","modname","get","hasFooterContent","manageFavourite","caller","modalBody","isFavourite","favourited","id","name","internal","Repository","unfavouriteModule","favouriteModule","manageFavouriteState","activeSectionId","elements","activetab","getAttribute","sectionChooserOptions","getSectionChooserOptions","firstChooserOption","toggleFocusableChooserOption","initChooserOptionsKeyboardNavigation","matches","closeOption","on","modules","getModuleSelector","clearSearch","searchInput","search","value","toggleSearchResultsView","footerClickListener","footer","footerjs","pluginName","customfooterjs","getBodyPromise","body","interval","pause","keyboard","addEventListener","getFooterPromise","chooserOptionsContainer","chooserOptions","querySelectorAll","Array","from","forEach","element","keyCode","enter","space","preventDefault","arrowRight","currentOption","nextOption","nextElementSibling","firstOption","firstElementChild","toFocusOption","clickErrorHandler","focusChooserOption","arrowLeft","previousOption","previousElementSibling","lastOption","lastElementChild","home","end","currentChooserOption","previousChooserOption","isFocusable","chooserOptionLink","addChooser","chooserOptionHelp","chooserOptionFavourite","tabIndex","item","fallback","searchQuery","searchResultsContainer","searchResults","chooserContainer","chooser","clearSearchButton","length","searchResultsData","searchModules","templateData","renderSearchResults","searchResultItemsContainer","searchResultItems","firstSearchResultItem","remove","setAttribute","removeAttribute","searchTerm","toLowerCase","activity","activityName","title","activityDesc","includes","push","disableFocusAllChooserOptions","modalPromise","sectionModules","Map","module","set","componentname","link","getModal","tab","activeSectionChooserOptions","prevActiveSectionId","relatedTarget","prevActiveSectionChooserOptions","setupKeyboardAccessibility","getRoot","ModalEvents","hidden","destroy"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"dialogue.min.js","sources":["../../../src/local/activitychooser/dialogue.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 * A type of dialogue used as for choosing options.\n *\n * @module core_course/local/activitychooser/dialogue\n * @copyright 2019 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Carousel from 'theme_boost/bootstrap/carousel';\nimport * as ModalEvents from 'core/modal_events';\nimport selectors from 'core_course/local/activitychooser/selectors';\nimport * as Templates from 'core/templates';\nimport {end, arrowLeft, arrowRight, home, enter, space} from 'core/key_codes';\nimport {addIconToContainer} from 'core/loadingicon';\nimport * as Repository from 'core_course/local/activitychooser/repository';\nimport Notification from 'core/notification';\nimport {debounce} from 'core/utils';\nconst getPlugin = pluginName => import(pluginName);\n\n/**\n * Given an event from the main module 'page' navigate to it's help section via a carousel.\n *\n * @method showModuleHelp\n * @param {Element} carousel Our initialized carousel to manipulate\n * @param {Object} moduleData Data of the module to carousel to\n * @param {jQuery} modal We need to figure out if the current modal has a footer.\n */\nconst showModuleHelp = (carousel, moduleData, modal = null) => {\n // If we have a real footer then we need to change temporarily.\n if (modal !== null && moduleData.showFooter === true) {\n modal.setFooter(Templates.render('core_course/local/activitychooser/footer_partial', moduleData));\n }\n const help = carousel.querySelector(selectors.regions.help);\n help.innerHTML = '';\n help.classList.add('m-auto');\n\n // Add a spinner.\n const spinnerPromise = addIconToContainer(help);\n\n // Used later...\n let transitionPromiseResolver = null;\n const transitionPromise = new Promise(resolve => {\n transitionPromiseResolver = resolve;\n });\n\n // Build up the html & js ready to place into the help section.\n const contentPromise = Templates.renderForPromise('core_course/local/activitychooser/help', moduleData);\n\n // Wait for the content to be ready, and for the transition to be complet.\n Promise.all([contentPromise, spinnerPromise, transitionPromise])\n .then(([{html, js}]) => Templates.replaceNodeContents(help, html, js))\n .then(() => {\n help.querySelector(selectors.regions.chooserSummary.header).focus();\n return help;\n })\n .catch(Notification.exception);\n\n // Move to the next slide, and resolve the transition promise when it's done.\n carousel.addEventListener('slid.bs.carousel', () => {\n transitionPromiseResolver();\n }, {once: true});\n // Trigger the transition between 'pages'.\n Carousel.getInstance(carousel).next();\n};\n\n/**\n * Given a user wants to change the favourite state of a module we either add or remove the status.\n * We also propergate this change across our map of modals.\n *\n * @method manageFavouriteState\n * @param {HTMLElement} modalBody The DOM node of the modal to manipulate\n * @param {HTMLElement} caller\n * @param {Function} partialFavourite Partially applied function we need to manage favourite status\n */\nconst manageFavouriteState = async(modalBody, caller, partialFavourite) => {\n const isFavourite = caller.dataset.favourited;\n const id = caller.dataset.id;\n const name = caller.dataset.name;\n const internal = caller.dataset.internal;\n // Switch on fave or not.\n if (isFavourite === 'true') {\n await Repository.unfavouriteModule(name, id);\n\n partialFavourite(internal, false, modalBody);\n } else {\n await Repository.favouriteModule(name, id);\n\n partialFavourite(internal, true, modalBody);\n }\n\n};\n\n/**\n * Register chooser related event listeners.\n *\n * @method registerListenerEvents\n * @param {Promise} modal Our modal that we are working with\n * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}\n * @param {Function} partialFavourite Partially applied function we need to manage favourite status\n * @param {Object} footerData Our base footer object.\n */\nconst registerListenerEvents = (modal, mappedModules, partialFavourite, footerData) => {\n const bodyClickListener = async(e) => {\n if (e.target.closest(selectors.actions.optionActions.showSummary)) {\n const carousel = modal.getBody()[0].querySelector(selectors.regions.carousel);\n\n const module = e.target.closest(selectors.regions.chooserOption.container);\n const moduleName = module.dataset.modname;\n const moduleData = mappedModules.get(moduleName);\n // We need to know if the overall modal has a footer so we know when to show a real / vs fake footer.\n moduleData.showFooter = modal.hasFooterContent();\n showModuleHelp(carousel, moduleData, modal);\n }\n\n if (e.target.closest(selectors.actions.optionActions.manageFavourite)) {\n const caller = e.target.closest(selectors.actions.optionActions.manageFavourite);\n await manageFavouriteState(modal.getBody()[0], caller, partialFavourite);\n const activeSectionId = modal.getBody()[0].querySelector(selectors.elements.activetab).getAttribute(\"href\");\n const sectionChooserOptions = modal.getBody()[0]\n .querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = sectionChooserOptions\n .querySelector(selectors.regions.chooserOption.container);\n toggleFocusableChooserOption(firstChooserOption, true);\n initChooserOptionsKeyboardNavigation(modal.getBody()[0], mappedModules, sectionChooserOptions, modal);\n }\n\n // From the help screen go back to the module overview.\n if (e.target.matches(selectors.actions.closeOption)) {\n const carousel = modal.getBody()[0].querySelector(selectors.regions.carousel);\n\n // Trigger the transition between 'pages'.\n Carousel.getInstance(carousel).prev();\n carousel.addEventListener('slid.bs.carousel', () => {\n const allModules = modal.getBody()[0].querySelector(selectors.regions.modules);\n const caller = allModules.querySelector(selectors.regions.getModuleSelector(e.target.dataset.modname));\n caller.focus();\n });\n }\n\n // The \"clear search\" button is triggered.\n if (e.target.closest(selectors.actions.clearSearch)) {\n // Clear the entered search query in the search bar and hide the search results container.\n const searchInput = modal.getBody()[0].querySelector(selectors.actions.search);\n searchInput.value = \"\";\n searchInput.focus();\n toggleSearchResultsView(modal, mappedModules, searchInput.value);\n }\n };\n\n // We essentially have two types of footer.\n // A fake one that is handled within the template for chooser_help and then all of the stuff for\n // modal.footer. We need to ensure we know exactly what type of footer we are using so we know what we\n // need to manage. The below code handles a real footer going to a mnet carousel item.\n const footerClickListener = async(e) => {\n if (footerData.footer === true) {\n const footerjs = await getPlugin(footerData.customfooterjs);\n await footerjs.footerClickListener(e, footerData, modal);\n }\n };\n\n modal.getBodyPromise()\n\n // The return value of getBodyPromise is a jquery object containing the body NodeElement.\n .then(body => body[0])\n\n // Set up the carousel.\n .then(body => {\n const carousel = document.querySelector(selectors.regions.carousel);\n new Carousel(carousel, {\n interval: false,\n pause: true,\n keyboard: false\n });\n\n return body;\n })\n\n // Add the listener for clicks on the body.\n .then(body => {\n body.addEventListener('click', bodyClickListener);\n return body;\n })\n\n // Add a listener for an input change in the activity chooser's search bar.\n .then(body => {\n const searchInput = body.querySelector(selectors.actions.search);\n // The search input is triggered.\n searchInput.addEventListener('input', debounce(() => {\n // Display the search results.\n toggleSearchResultsView(modal, mappedModules, searchInput.value);\n }, 300));\n return body;\n })\n\n // Register event listeners related to the keyboard navigation controls.\n .then(body => {\n // Get the active chooser options section.\n const activeSectionId = body.querySelector(selectors.elements.activetab).getAttribute(\"href\");\n const sectionChooserOptions = body.querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = sectionChooserOptions.querySelector(selectors.regions.chooserOption.container);\n\n toggleFocusableChooserOption(firstChooserOption, true);\n initChooserOptionsKeyboardNavigation(body, mappedModules, sectionChooserOptions, modal);\n\n return body;\n })\n .catch(Notification.exception);\n\n modal.getFooterPromise()\n\n // The return value of getBodyPromise is a jquery object containing the body NodeElement.\n .then(footer => footer[0])\n // Add the listener for clicks on the footer.\n .then(footer => {\n footer.addEventListener('click', footerClickListener);\n return footer;\n })\n .catch(Notification.exception);\n};\n\n/**\n * Initialise the keyboard navigation controls for the chooser options.\n *\n * @method initChooserOptionsKeyboardNavigation\n * @param {HTMLElement} body Our modal that we are working with\n * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}\n * @param {HTMLElement} chooserOptionsContainer The section that contains the chooser items\n * @param {Object} modal Our created modal for the section\n */\nconst initChooserOptionsKeyboardNavigation = (body, mappedModules, chooserOptionsContainer, modal = null) => {\n const chooserOptions = chooserOptionsContainer.querySelectorAll(selectors.regions.chooserOption.container);\n\n Array.from(chooserOptions).forEach((element) => {\n return element.addEventListener('keydown', (e) => {\n\n // Check for enter/ space triggers for showing the help.\n if (e.keyCode === enter || e.keyCode === space) {\n if (e.target.matches(selectors.actions.optionActions.showSummary)) {\n e.preventDefault();\n const module = e.target.closest(selectors.regions.chooserOption.container);\n const moduleName = module.dataset.modname;\n const moduleData = mappedModules.get(moduleName);\n const carousel = document.querySelector(selectors.regions.carousel);\n new Carousel({\n interval: false,\n pause: true,\n keyboard: false\n });\n\n // We need to know if the overall modal has a footer so we know when to show a real / vs fake footer.\n moduleData.showFooter = modal.hasFooterContent();\n showModuleHelp(carousel, moduleData, modal);\n }\n }\n\n // Next.\n if (e.keyCode === arrowRight) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const nextOption = currentOption.nextElementSibling;\n const firstOption = chooserOptionsContainer.firstElementChild;\n const toFocusOption = clickErrorHandler(nextOption, firstOption);\n focusChooserOption(toFocusOption, currentOption);\n }\n\n // Previous.\n if (e.keyCode === arrowLeft) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const previousOption = currentOption.previousElementSibling;\n const lastOption = chooserOptionsContainer.lastElementChild;\n const toFocusOption = clickErrorHandler(previousOption, lastOption);\n focusChooserOption(toFocusOption, currentOption);\n }\n\n if (e.keyCode === home) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const firstOption = chooserOptionsContainer.firstElementChild;\n focusChooserOption(firstOption, currentOption);\n }\n\n if (e.keyCode === end) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const lastOption = chooserOptionsContainer.lastElementChild;\n focusChooserOption(lastOption, currentOption);\n }\n });\n });\n};\n\n/**\n * Focus on a chooser option element and remove the previous chooser element from the focus order\n *\n * @method focusChooserOption\n * @param {HTMLElement} currentChooserOption The current chooser option element that we want to focus\n * @param {HTMLElement|null} previousChooserOption The previous focused option element\n */\nconst focusChooserOption = (currentChooserOption, previousChooserOption = null) => {\n if (previousChooserOption !== null) {\n toggleFocusableChooserOption(previousChooserOption, false);\n }\n\n toggleFocusableChooserOption(currentChooserOption, true);\n currentChooserOption.focus();\n};\n\n/**\n * Add or remove a chooser option from the focus order.\n *\n * @method toggleFocusableChooserOption\n * @param {HTMLElement} chooserOption The chooser option element which should be added or removed from the focus order\n * @param {Boolean} isFocusable Whether the chooser element is focusable or not\n */\nconst toggleFocusableChooserOption = (chooserOption, isFocusable) => {\n const chooserOptionLink = chooserOption.querySelector(selectors.actions.addChooser);\n const chooserOptionHelp = chooserOption.querySelector(selectors.actions.optionActions.showSummary);\n const chooserOptionFavourite = chooserOption.querySelector(selectors.actions.optionActions.manageFavourite);\n\n if (isFocusable) {\n // Set tabindex to 0 to add current chooser option element to the focus order.\n chooserOption.tabIndex = 0;\n chooserOptionLink.tabIndex = 0;\n chooserOptionHelp.tabIndex = 0;\n chooserOptionFavourite.tabIndex = 0;\n } else {\n // Set tabindex to -1 to remove the previous chooser option element from the focus order.\n chooserOption.tabIndex = -1;\n chooserOptionLink.tabIndex = -1;\n chooserOptionHelp.tabIndex = -1;\n chooserOptionFavourite.tabIndex = -1;\n }\n};\n\n/**\n * Small error handling function to make sure the navigated to object exists\n *\n * @method clickErrorHandler\n * @param {HTMLElement} item What we want to check exists\n * @param {HTMLElement} fallback If we dont match anything fallback the focus\n * @return {HTMLElement}\n */\nconst clickErrorHandler = (item, fallback) => {\n if (item !== null) {\n return item;\n } else {\n return fallback;\n }\n};\n\n/**\n * Render the search results in a defined container\n *\n * @method renderSearchResults\n * @param {HTMLElement} searchResultsContainer The container where the data should be rendered\n * @param {Object} searchResultsData Data containing the module items that satisfy the search criteria\n */\nconst renderSearchResults = async(searchResultsContainer, searchResultsData) => {\n const templateData = {\n 'searchresultsnumber': searchResultsData.length,\n 'searchresults': searchResultsData\n };\n // Build up the html & js ready to place into the help section.\n const {html, js} = await Templates.renderForPromise('core_course/local/activitychooser/search_results', templateData);\n await Templates.replaceNodeContents(searchResultsContainer, html, js);\n};\n\n/**\n * Toggle (display/hide) the search results depending on the value of the search query\n *\n * @method toggleSearchResultsView\n * @param {Object} modal Our created modal for the section\n * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}\n * @param {String} searchQuery The search query\n */\nconst toggleSearchResultsView = async(modal, mappedModules, searchQuery) => {\n const modalBody = modal.getBody()[0];\n const searchResultsContainer = modalBody.querySelector(selectors.regions.searchResults);\n const chooserContainer = modalBody.querySelector(selectors.regions.chooser);\n const clearSearchButton = modalBody.querySelector(selectors.actions.clearSearch);\n\n if (searchQuery.length > 0) { // Search query is present.\n const searchResultsData = searchModules(mappedModules, searchQuery);\n await renderSearchResults(searchResultsContainer, searchResultsData);\n const searchResultItemsContainer = searchResultsContainer.querySelector(selectors.regions.searchResultItems);\n const firstSearchResultItem = searchResultItemsContainer.querySelector(selectors.regions.chooserOption.container);\n if (firstSearchResultItem) {\n // Set the first result item to be focusable.\n toggleFocusableChooserOption(firstSearchResultItem, true);\n // Register keyboard events on the created search result items.\n initChooserOptionsKeyboardNavigation(modalBody, mappedModules, searchResultItemsContainer, modal);\n }\n // Display the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.remove('d-none');\n // Hide the default chooser options container.\n chooserContainer.setAttribute('hidden', 'hidden');\n // Display the search results container.\n searchResultsContainer.removeAttribute('hidden');\n } else { // Search query is not present.\n // Hide the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.add('d-none');\n // Hide the search results container.\n searchResultsContainer.setAttribute('hidden', 'hidden');\n // Display the default chooser options container.\n chooserContainer.removeAttribute('hidden');\n }\n};\n\n/**\n * Return the list of modules which have a name or description that matches the given search term.\n *\n * @method searchModules\n * @param {Array} modules List of available modules\n * @param {String} searchTerm The search term to match\n * @return {Array}\n */\nconst searchModules = (modules, searchTerm) => {\n if (searchTerm === '') {\n return modules;\n }\n searchTerm = searchTerm.toLowerCase();\n const searchResults = [];\n modules.forEach((activity) => {\n const activityName = activity.title.toLowerCase();\n const activityDesc = activity.help.toLowerCase();\n if (activityName.includes(searchTerm) || activityDesc.includes(searchTerm)) {\n searchResults.push(activity);\n }\n });\n\n return searchResults;\n};\n\n/**\n * Set up our tabindex information across the chooser.\n *\n * @method setupKeyboardAccessibility\n * @param {Promise} modal Our created modal for the section\n * @param {Map} mappedModules A map of all of the built module information\n */\nconst setupKeyboardAccessibility = (modal, mappedModules) => {\n modal.getModal()[0].tabIndex = -1;\n\n modal.getBodyPromise().then(body => {\n document.querySelectorAll(selectors.elements.tab).forEach((tab) => {\n tab.addEventListener('shown.bs.tab', (e) => {\n const activeSectionId = e.target.getAttribute(\"href\");\n const activeSectionChooserOptions = body[0]\n .querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = activeSectionChooserOptions\n .querySelector(selectors.regions.chooserOption.container);\n const prevActiveSectionId = e.relatedTarget.getAttribute(\"href\");\n const prevActiveSectionChooserOptions = body[0]\n .querySelector(selectors.regions.getSectionChooserOptions(prevActiveSectionId));\n\n // Disable the focus of every chooser option in the previous active section.\n disableFocusAllChooserOptions(prevActiveSectionChooserOptions);\n // Enable the focus of the first chooser option in the current active section.\n toggleFocusableChooserOption(firstChooserOption, true);\n initChooserOptionsKeyboardNavigation(body[0], mappedModules, activeSectionChooserOptions, modal);\n });\n });\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Disable the focus of all chooser options in a specific container (section).\n *\n * @method disableFocusAllChooserOptions\n * @param {HTMLElement} sectionChooserOptions The section that contains the chooser items\n */\nconst disableFocusAllChooserOptions = (sectionChooserOptions) => {\n const allChooserOptions = sectionChooserOptions.querySelectorAll(selectors.regions.chooserOption.container);\n allChooserOptions.forEach((chooserOption) => {\n toggleFocusableChooserOption(chooserOption, false);\n });\n};\n\n/**\n * Display the module chooser.\n *\n * @method displayChooser\n * @param {Promise} modalPromise Our created modal for the section\n * @param {Array} sectionModules An array of all of the built module information\n * @param {Function} partialFavourite Partially applied function we need to manage favourite status\n * @param {Object} footerData Our base footer object.\n */\nexport const displayChooser = (modalPromise, sectionModules, partialFavourite, footerData) => {\n // Make a map so we can quickly fetch a specific module's object for either rendering or searching.\n const mappedModules = new Map();\n sectionModules.forEach((module) => {\n mappedModules.set(module.componentname + '_' + module.link, module);\n });\n\n // Register event listeners.\n modalPromise.then(modal => {\n registerListenerEvents(modal, mappedModules, partialFavourite, footerData);\n\n // We want to focus on the first chooser option element as soon as the modal is opened.\n setupKeyboardAccessibility(modal, mappedModules);\n\n // We want to focus on the action select when the dialog is closed.\n modal.getRoot().on(ModalEvents.hidden, () => {\n modal.destroy();\n });\n\n return modal;\n }).catch(Notification.exception);\n};\n"],"names":["showModuleHelp","carousel","moduleData","modal","showFooter","setFooter","Templates","render","help","querySelector","selectors","regions","innerHTML","classList","add","spinnerPromise","transitionPromiseResolver","transitionPromise","Promise","resolve","contentPromise","renderForPromise","all","then","_ref","html","js","replaceNodeContents","chooserSummary","header","focus","catch","Notification","exception","addEventListener","once","getInstance","next","registerListenerEvents","mappedModules","partialFavourite","footerData","bodyClickListener","async","e","target","closest","actions","optionActions","showSummary","getBody","moduleName","chooserOption","container","dataset","modname","get","hasFooterContent","manageFavourite","caller","modalBody","isFavourite","favourited","id","name","internal","Repository","unfavouriteModule","favouriteModule","manageFavouriteState","activeSectionId","elements","activetab","getAttribute","sectionChooserOptions","getSectionChooserOptions","firstChooserOption","toggleFocusableChooserOption","initChooserOptionsKeyboardNavigation","matches","closeOption","prev","modules","getModuleSelector","clearSearch","searchInput","search","value","toggleSearchResultsView","footerClickListener","footer","footerjs","pluginName","customfooterjs","getBodyPromise","body","document","Carousel","interval","pause","keyboard","getFooterPromise","chooserOptionsContainer","chooserOptions","querySelectorAll","Array","from","forEach","element","keyCode","enter","space","preventDefault","arrowRight","currentOption","nextOption","nextElementSibling","firstOption","firstElementChild","toFocusOption","clickErrorHandler","focusChooserOption","arrowLeft","previousOption","previousElementSibling","lastOption","lastElementChild","home","end","currentChooserOption","previousChooserOption","isFocusable","chooserOptionLink","addChooser","chooserOptionHelp","chooserOptionFavourite","tabIndex","item","fallback","searchQuery","searchResultsContainer","searchResults","chooserContainer","chooser","clearSearchButton","length","searchResultsData","searchModules","templateData","renderSearchResults","searchResultItemsContainer","searchResultItems","firstSearchResultItem","remove","setAttribute","removeAttribute","searchTerm","toLowerCase","activity","activityName","title","activityDesc","includes","push","disableFocusAllChooserOptions","modalPromise","sectionModules","Map","module","set","componentname","link","getModal","tab","activeSectionChooserOptions","prevActiveSectionId","relatedTarget","prevActiveSectionChooserOptions","setupKeyboardAccessibility","getRoot","on","ModalEvents","hidden","destroy"],"mappings":"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"} \ No newline at end of file diff --git a/course/amd/src/local/activitychooser/dialogue.js b/course/amd/src/local/activitychooser/dialogue.js index 433469a1387a1..21f2fdfc01888 100644 --- a/course/amd/src/local/activitychooser/dialogue.js +++ b/course/amd/src/local/activitychooser/dialogue.js @@ -21,7 +21,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import $ from 'jquery'; +import Carousel from 'theme_boost/bootstrap/carousel'; import * as ModalEvents from 'core/modal_events'; import selectors from 'core_course/local/activitychooser/selectors'; import * as Templates from 'core/templates'; @@ -36,7 +36,7 @@ const getPlugin = pluginName => import(pluginName); * Given an event from the main module 'page' navigate to it's help section via a carousel. * * @method showModuleHelp - * @param {jQuery} carousel Our initialized carousel to manipulate + * @param {Element} carousel Our initialized carousel to manipulate * @param {Object} moduleData Data of the module to carousel to * @param {jQuery} modal We need to figure out if the current modal has a footer. */ @@ -45,7 +45,7 @@ const showModuleHelp = (carousel, moduleData, modal = null) => { if (modal !== null && moduleData.showFooter === true) { modal.setFooter(Templates.render('core_course/local/activitychooser/footer_partial', moduleData)); } - const help = carousel.find(selectors.regions.help)[0]; + const help = carousel.querySelector(selectors.regions.help); help.innerHTML = ''; help.classList.add('m-auto'); @@ -71,11 +71,11 @@ const showModuleHelp = (carousel, moduleData, modal = null) => { .catch(Notification.exception); // Move to the next slide, and resolve the transition promise when it's done. - carousel.one('slid.bs.carousel', () => { + carousel.addEventListener('slid.bs.carousel', () => { transitionPromiseResolver(); - }); + }, {once: true}); // Trigger the transition between 'pages'. - carousel.carousel('next'); + Carousel.getInstance(carousel).next(); }; /** @@ -117,7 +117,7 @@ const manageFavouriteState = async(modalBody, caller, partialFavourite) => { const registerListenerEvents = (modal, mappedModules, partialFavourite, footerData) => { const bodyClickListener = async(e) => { if (e.target.closest(selectors.actions.optionActions.showSummary)) { - const carousel = $(modal.getBody()[0].querySelector(selectors.regions.carousel)); + const carousel = modal.getBody()[0].querySelector(selectors.regions.carousel); const module = e.target.closest(selectors.regions.chooserOption.container); const moduleName = module.dataset.modname; @@ -141,11 +141,11 @@ const registerListenerEvents = (modal, mappedModules, partialFavourite, footerDa // From the help screen go back to the module overview. if (e.target.matches(selectors.actions.closeOption)) { - const carousel = $(modal.getBody()[0].querySelector(selectors.regions.carousel)); + const carousel = modal.getBody()[0].querySelector(selectors.regions.carousel); // Trigger the transition between 'pages'. - carousel.carousel('prev'); - carousel.on('slid.bs.carousel', () => { + Carousel.getInstance(carousel).prev(); + carousel.addEventListener('slid.bs.carousel', () => { const allModules = modal.getBody()[0].querySelector(selectors.regions.modules); const caller = allModules.querySelector(selectors.regions.getModuleSelector(e.target.dataset.modname)); caller.focus(); @@ -180,12 +180,12 @@ const registerListenerEvents = (modal, mappedModules, partialFavourite, footerDa // Set up the carousel. .then(body => { - $(body.querySelector(selectors.regions.carousel)) - .carousel({ + const carousel = document.querySelector(selectors.regions.carousel); + new Carousel(carousel, { interval: false, pause: true, keyboard: false - }); + }); return body; }) @@ -219,7 +219,7 @@ const registerListenerEvents = (modal, mappedModules, partialFavourite, footerDa return body; }) - .catch(); + .catch(Notification.exception); modal.getFooterPromise() @@ -230,7 +230,7 @@ const registerListenerEvents = (modal, mappedModules, partialFavourite, footerDa footer.addEventListener('click', footerClickListener); return footer; }) - .catch(); + .catch(Notification.exception); }; /** @@ -255,8 +255,8 @@ const initChooserOptionsKeyboardNavigation = (body, mappedModules, chooserOption const module = e.target.closest(selectors.regions.chooserOption.container); const moduleName = module.dataset.modname; const moduleData = mappedModules.get(moduleName); - const carousel = $(body.querySelector(selectors.regions.carousel)); - carousel.carousel({ + const carousel = document.querySelector(selectors.regions.carousel); + new Carousel({ interval: false, pause: true, keyboard: false @@ -458,21 +458,23 @@ const setupKeyboardAccessibility = (modal, mappedModules) => { modal.getModal()[0].tabIndex = -1; modal.getBodyPromise().then(body => { - $(selectors.elements.tab).on('shown.bs.tab', (e) => { - const activeSectionId = e.target.getAttribute("href"); - const activeSectionChooserOptions = body[0] - .querySelector(selectors.regions.getSectionChooserOptions(activeSectionId)); - const firstChooserOption = activeSectionChooserOptions - .querySelector(selectors.regions.chooserOption.container); - const prevActiveSectionId = e.relatedTarget.getAttribute("href"); - const prevActiveSectionChooserOptions = body[0] - .querySelector(selectors.regions.getSectionChooserOptions(prevActiveSectionId)); - - // Disable the focus of every chooser option in the previous active section. - disableFocusAllChooserOptions(prevActiveSectionChooserOptions); - // Enable the focus of the first chooser option in the current active section. - toggleFocusableChooserOption(firstChooserOption, true); - initChooserOptionsKeyboardNavigation(body[0], mappedModules, activeSectionChooserOptions, modal); + document.querySelectorAll(selectors.elements.tab).forEach((tab) => { + tab.addEventListener('shown.bs.tab', (e) => { + const activeSectionId = e.target.getAttribute("href"); + const activeSectionChooserOptions = body[0] + .querySelector(selectors.regions.getSectionChooserOptions(activeSectionId)); + const firstChooserOption = activeSectionChooserOptions + .querySelector(selectors.regions.chooserOption.container); + const prevActiveSectionId = e.relatedTarget.getAttribute("href"); + const prevActiveSectionChooserOptions = body[0] + .querySelector(selectors.regions.getSectionChooserOptions(prevActiveSectionId)); + + // Disable the focus of every chooser option in the previous active section. + disableFocusAllChooserOptions(prevActiveSectionChooserOptions); + // Enable the focus of the first chooser option in the current active section. + toggleFocusableChooserOption(firstChooserOption, true); + initChooserOptionsKeyboardNavigation(body[0], mappedModules, activeSectionChooserOptions, modal); + }); }); return; }).catch(Notification.exception); @@ -520,5 +522,5 @@ export const displayChooser = (modalPromise, sectionModules, partialFavourite, f }); return modal; - }).catch(); + }).catch(Notification.exception); }; diff --git a/course/format/amd/build/local/content.min.js b/course/format/amd/build/local/content.min.js index 5f15c5901db10..6857f47298e69 100644 --- a/course/format/amd/build/local/content.min.js +++ b/course/format/amd/build/local/content.min.js @@ -1,4 +1,4 @@ -define("core_courseformat/local/content",["exports","core/reactive","core/utils","core_courseformat/courseeditor","core/config","core/inplace_editable","core_courseformat/local/content/section","core_courseformat/local/content/section/cmitem","core/fragment","core/templates","core_courseformat/local/content/actions","core_course/events","jquery","core/pending"],(function(_exports,_reactive,_utils,_courseeditor,_config,_inplace_editable,_section,_cmitem,_fragment,_templates,_actions,CourseEvents,_jquery,_pending){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("core_courseformat/local/content",["exports","core/reactive","theme_boost/bootstrap/collapse","core/utils","core_courseformat/courseeditor","core/config","core/inplace_editable","core_courseformat/local/content/section","core_courseformat/local/content/section/cmitem","core/fragment","core/templates","core_courseformat/local/content/actions","core_course/events","core/pending"],(function(_exports,_reactive,_collapse,_utils,_courseeditor,_config,_inplace_editable,_section,_cmitem,_fragment,_templates,_actions,CourseEvents,_pending){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Course index main component. * @@ -6,6 +6,6 @@ define("core_courseformat/local/content",["exports","core/reactive","core/utils" * @class core_courseformat/local/content * @copyright 2020 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_config=_interopRequireDefault(_config),_inplace_editable=_interopRequireDefault(_inplace_editable),_section=_interopRequireDefault(_section),_cmitem=_interopRequireDefault(_cmitem),_fragment=_interopRequireDefault(_fragment),_templates=_interopRequireDefault(_templates),_actions=_interopRequireDefault(_actions),CourseEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CourseEvents),_jquery=_interopRequireDefault(_jquery),_pending=_interopRequireDefault(_pending);class Component extends _reactive.BaseComponent{create(descriptor){var _descriptor$sectionRe;this.name="course_format",this.selectors={SECTION:"[data-for='section']",SECTION_ITEM:"[data-for='section_title']",SECTION_CMLIST:"[data-for='cmlist']",COURSE_SECTIONLIST:"[data-for='course_sectionlist']",CM:"[data-for='cmitem']",TOGGLER:'[data-action="togglecoursecontentsection"]',COLLAPSE:'[data-bs-toggle="collapse"]',TOGGLEALL:'[data-toggle="toggleall"]',ACTIVITYTAG:"li",SECTIONTAG:"li"},this.selectorGenerators={cmNameFor:id=>"[data-cm-name-for='".concat(id,"']"),sectionNameFor:id=>"[data-section-name-for='".concat(id,"']")},this.classes={COLLAPSED:"collapsed",ACTIVITY:"activity",STATEDREADY:"stateready",SECTION:"section"},this.dettachedCms={},this.dettachedSections={},this.sections={},this.cms={},this.sectionReturn=null!==(_descriptor$sectionRe=descriptor.sectionReturn)&&void 0!==_descriptor$sectionRe?_descriptor$sectionRe:null,this.debouncedReloads=new Map}static init(target,selectors,sectionReturn){return new Component({element:document.getElementById(target),reactive:(0,_courseeditor.getCurrentCourseEditor)(),selectors:selectors,sectionReturn:sectionReturn})}stateReady(state){this._indexContents(),this.addEventListener(this.element,"click",this._sectionTogglers);const toogleAll=this.getElement(this.selectors.TOGGLEALL);if(toogleAll){const collapseElementIds=[...this.getElements(this.selectors.COLLAPSE)].map((element=>element.id));toogleAll.setAttribute("aria-controls",collapseElementIds.join(" ")),this.addEventListener(toogleAll,"click",this._allSectionToggler),this.addEventListener(toogleAll,"keydown",(e=>{" "===e.key&&this._allSectionToggler(e)})),this._refreshAllSectionsToggler(state)}this.reactive.supportComponents&&(this.reactive.isEditing&&new _actions.default(this),this.element.classList.add(this.classes.STATEDREADY)),this.addEventListener(this.element,CourseEvents.manualCompletionToggled,this._completionHandler),this.addEventListener(document,"scroll",this._scrollHandler)}_sectionTogglers(event){const sectionlink=event.target.closest(this.selectors.TOGGLER),closestCollapse=event.target.closest(this.selectors.COLLAPSE),isChevron=null==closestCollapse?void 0:closestCollapse.closest(this.selectors.SECTION_ITEM);if(sectionlink||isChevron){var _toggler$classList$co;const section=event.target.closest(this.selectors.SECTION),toggler=section.querySelector(this.selectors.COLLAPSE),isCollapsed=null!==(_toggler$classList$co=null==toggler?void 0:toggler.classList.contains(this.classes.COLLAPSED))&&void 0!==_toggler$classList$co&&_toggler$classList$co,sectionId=section.getAttribute("data-id");this.reactive.dispatch("sectionContentCollapsed",[sectionId],!isCollapsed)}}_allSectionToggler(event){var _course$sectionlist;event.preventDefault();const isAllCollapsed=event.target.closest(this.selectors.TOGGLEALL).classList.contains(this.classes.COLLAPSED),course=this.reactive.get("course");this.reactive.dispatch("sectionContentCollapsed",null!==(_course$sectionlist=course.sectionlist)&&void 0!==_course$sectionlist?_course$sectionlist:[],!isAllCollapsed)}getWatchers(){return this.reactive.sectionReturn=this.sectionReturn,this.reactive.supportComponents?[{watch:"cm.visible:updated",handler:this._reloadCm},{watch:"cm.stealth:updated",handler:this._reloadCm},{watch:"cm.sectionid:updated",handler:this._reloadCm},{watch:"cm.indent:updated",handler:this._reloadCm},{watch:"cm.groupmode:updated",handler:this._reloadCm},{watch:"cm.name:updated",handler:this._refreshCmName},{watch:"section.number:updated",handler:this._refreshSectionNumber},{watch:"section.title:updated",handler:this._refreshSectionTitle},{watch:"section.contentcollapsed:updated",handler:this._refreshSectionCollapsed},{watch:"transaction:start",handler:this._startProcessing},{watch:"course.sectionlist:updated",handler:this._refreshCourseSectionlist},{watch:"section.cmlist:updated",handler:this._refreshSectionCmlist},{watch:"section.visible:updated",handler:this._reloadSection},{watch:"state:updated",handler:this._indexContents}]:[]}_refreshCmName(_ref){let{element:element}=_ref;this.getElements(this.selectorGenerators.cmNameFor(element.id)).forEach((cmNameFor=>{cmNameFor.textContent=element.name}))}_refreshSectionCollapsed(_ref2){var _toggler$classList$co2;let{state:state,element:element}=_ref2;const target=this.getElement(this.selectors.SECTION,element.id);if(!target)throw new Error("Unknown section with ID ".concat(element.id));const toggler=target.querySelector(this.selectors.COLLAPSE),isCollapsed=null!==(_toggler$classList$co2=null==toggler?void 0:toggler.classList.contains(this.classes.COLLAPSED))&&void 0!==_toggler$classList$co2&&_toggler$classList$co2;if(element.contentcollapsed!==isCollapsed){var _toggler$dataset$targ;let collapsibleId=null!==(_toggler$dataset$targ=toggler.dataset.target)&&void 0!==_toggler$dataset$targ?_toggler$dataset$targ:toggler.getAttribute("href");if(!collapsibleId)return;collapsibleId=collapsibleId.replace("#","");const collapsible=document.getElementById(collapsibleId);if(!collapsible)return;(0,_jquery.default)(collapsible).collapse(element.contentcollapsed?"hide":"show")}this._refreshAllSectionsToggler(state)}_refreshAllSectionsToggler(state){const target=this.getElement(this.selectors.TOGGLEALL);if(!target)return;let allcollapsed=!0,allexpanded=!0;state.section.forEach((section=>{allcollapsed=allcollapsed&§ion.contentcollapsed,allexpanded=allexpanded&&!section.contentcollapsed})),allcollapsed&&(target.classList.add(this.classes.COLLAPSED),target.setAttribute("aria-expanded",!1)),allexpanded&&(target.classList.remove(this.classes.COLLAPSED),target.setAttribute("aria-expanded",!0))}_startProcessing(){this.dettachedCms={},this.dettachedSections={}}_completionHandler(_ref3){let{detail:detail}=_ref3;void 0!==detail&&this.reactive.dispatch("cmCompletion",[detail.cmid],detail.completed)}_scrollHandler(){const pageOffset=window.scrollY,items=this.reactive.getExporter().allItemsArray(this.reactive.state);let pageItem=null;items.every((item=>{const index="section"===item.type?this.sections:this.cms;if(void 0===index[item.id])return!0;const element=index[item.id].element;return pageItem=item,pageOffset>=element.offsetTop})),pageItem&&this.reactive.dispatch("setPageItem",pageItem.type,pageItem.id)}_refreshSectionNumber(_ref4){let{element:element}=_ref4;const target=this.getElement(this.selectors.SECTION,element.id);if(!target)return;target.id="section-".concat(element.number),target.dataset.sectionid=element.number,target.dataset.number=element.number;const inplace=_inplace_editable.default.getInplaceEditable(target.querySelector(this.selectors.SECTION_ITEM));if(inplace){const currentvalue=inplace.getValue(),currentitemid=inplace.getItemId();""===inplace.getValue()&&(currentitemid!=element.id||currentvalue==element.rawtitle&&""!=element.rawtitle||inplace.setValue(element.rawtitle))}}_refreshSectionTitle(_ref5){let{element:element}=_ref5;document.querySelectorAll(this.selectorGenerators.sectionNameFor(element.id)).forEach((sectionNameFor=>{sectionNameFor.textContent=element.title}))}_refreshSectionCmlist(_ref6){var _element$cmlist;let{element:element}=_ref6;const cmlist=null!==(_element$cmlist=element.cmlist)&&void 0!==_element$cmlist?_element$cmlist:[],section=this.getElement(this.selectors.SECTION,element.id),listparent=null==section?void 0:section.querySelector(this.selectors.SECTION_CMLIST),createCm=this._createCmItem.bind(this);listparent&&this._fixOrder(listparent,cmlist,this.selectors.CM,this.dettachedCms,createCm)}_refreshCourseSectionlist(_ref7){let{state:state}=_ref7;if(null!==this.reactive.sectionReturn)return;const sectionlist=this.reactive.getExporter().listedSectionIds(state),listparent=this.getElement(this.selectors.COURSE_SECTIONLIST),createSection=this._createSectionItem.bind(this);listparent&&this._fixOrder(listparent,sectionlist,this.selectors.SECTION,this.dettachedSections,createSection)}_indexContents(){this._scanIndex(this.selectors.SECTION,this.sections,(item=>new _section.default(item))),this._scanIndex(this.selectors.CM,this.cms,(item=>new _cmitem.default(item)))}_scanIndex(selector,index,creationhandler){this.getElements("".concat(selector,":not([data-indexed])")).forEach((item=>{var _item$dataset;null!=item&&null!==(_item$dataset=item.dataset)&&void 0!==_item$dataset&&_item$dataset.id&&(void 0!==index[item.dataset.id]&&index[item.dataset.id].unregister(),index[item.dataset.id]=creationhandler({...this,element:item}),item.dataset.indexed=!0)}))}_reloadCm(_ref8){let{element:element}=_ref8;if(!this.getElement(this.selectors.CM,element.id))return;this._getDebouncedReloadCm(element.id)()}_getDebouncedReloadCm(cmId){const pendingKey="courseformat/content:reloadCm_".concat(cmId);let debouncedReload=this.debouncedReloads.get(pendingKey);if(debouncedReload)return debouncedReload;return debouncedReload=(0,_utils.debounce)((()=>{var _this$reactive$sectio;const pendingReload=new _pending.default(pendingKey);this.debouncedReloads.delete(pendingKey);const cmitem=this.getElement(this.selectors.CM,cmId);if(!cmitem)return pendingReload.resolve();return _fragment.default.loadFragment("core_courseformat","cmitem",_config.default.courseContextId,{id:cmId,courseid:_config.default.courseId,sr:null!==(_this$reactive$sectio=this.reactive.sectionReturn)&&void 0!==_this$reactive$sectio?_this$reactive$sectio:null}).then(((html,js)=>document.contains(cmitem)?(_templates.default.replaceNode(cmitem,html,js),this._indexContents(),pendingReload.resolve(),!0):(pendingReload.resolve(),!1))).catch((()=>{pendingReload.resolve()})),pendingReload}),200,{cancel:!0,pending:!0}),this.debouncedReloads.set(pendingKey,debouncedReload),debouncedReload}_cancelDebouncedReloadCm(cmId){const pendingKey="courseformat/content:reloadCm_".concat(cmId),debouncedReload=this.debouncedReloads.get(pendingKey);debouncedReload&&(debouncedReload.cancel(),this.debouncedReloads.delete(pendingKey))}_reloadSection(_ref9){let{element:element}=_ref9;const pendingReload=new _pending.default("courseformat/content:reloadSection_".concat(element.id)),sectionitem=this.getElement(this.selectors.SECTION,element.id);if(sectionitem){var _this$reactive$sectio2;for(const cmId of element.cmlist)this._cancelDebouncedReloadCm(cmId);_fragment.default.loadFragment("core_courseformat","section",_config.default.courseContextId,{id:element.id,courseid:_config.default.courseId,sr:null!==(_this$reactive$sectio2=this.reactive.sectionReturn)&&void 0!==_this$reactive$sectio2?_this$reactive$sectio2:null}).then(((html,js)=>{_templates.default.replaceNode(sectionitem,html,js),this._indexContents(),pendingReload.resolve()})).catch((()=>{pendingReload.resolve()}))}}_createCmItem(container,cmid){const newItem=document.createElement(this.selectors.ACTIVITYTAG);return newItem.dataset.for="cmitem",newItem.dataset.id=cmid,newItem.id="module-".concat(cmid),newItem.classList.add(this.classes.ACTIVITY),container.append(newItem),this._reloadCm({element:this.reactive.get("cm",cmid)}),newItem}_createSectionItem(container,sectionid){const section=this.reactive.get("section",sectionid),newItem=document.createElement(this.selectors.SECTIONTAG);return newItem.dataset.for="section",newItem.dataset.id=sectionid,newItem.dataset.number=section.number,newItem.id="section-".concat(sectionid),newItem.classList.add(this.classes.SECTION),container.append(newItem),this._reloadSection({element:section}),newItem}async _fixOrder(container,neworder,selector,dettachedelements,createMethod){if(void 0===container)return;if(!neworder.length)return container.classList.add("hidden"),void(container.innerHTML="");container.classList.remove("hidden"),neworder.forEach(((itemid,index)=>{var _ref10,_this$getElement;let item=null!==(_ref10=null!==(_this$getElement=this.getElement(selector,itemid))&&void 0!==_this$getElement?_this$getElement:dettachedelements[itemid])&&void 0!==_ref10?_ref10:createMethod(container,itemid);if(void 0===item)return;const currentitem=container.children[index];void 0!==currentitem?currentitem!==item&&container.insertBefore(item,currentitem):container.append(item)}));const orphanElements=[];for(;container.children.length>neworder.length;){var _lastchild$classList,_lastchild$dataset;const lastchild=container.lastChild;var _lastchild$dataset$id,_lastchild$dataset2;if(null!=lastchild&&null!==(_lastchild$classList=lastchild.classList)&&void 0!==_lastchild$classList&&_lastchild$classList.contains("dndupload-preview")||null!==(_lastchild$dataset=lastchild.dataset)&&void 0!==_lastchild$dataset&&_lastchild$dataset.orphan)orphanElements.push(lastchild);else dettachedelements[null!==(_lastchild$dataset$id=null==lastchild||null===(_lastchild$dataset2=lastchild.dataset)||void 0===_lastchild$dataset2?void 0:_lastchild$dataset2.id)&&void 0!==_lastchild$dataset$id?_lastchild$dataset$id:0]=lastchild;container.removeChild(lastchild)}orphanElements.forEach((element=>{container.append(element)}))}}return _exports.default=Component,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_collapse=_interopRequireDefault(_collapse),_config=_interopRequireDefault(_config),_inplace_editable=_interopRequireDefault(_inplace_editable),_section=_interopRequireDefault(_section),_cmitem=_interopRequireDefault(_cmitem),_fragment=_interopRequireDefault(_fragment),_templates=_interopRequireDefault(_templates),_actions=_interopRequireDefault(_actions),CourseEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CourseEvents),_pending=_interopRequireDefault(_pending);class Component extends _reactive.BaseComponent{create(descriptor){var _descriptor$sectionRe;this.name="course_format",this.selectors={SECTION:"[data-for='section']",SECTION_ITEM:"[data-for='section_title']",SECTION_CMLIST:"[data-for='cmlist']",COURSE_SECTIONLIST:"[data-for='course_sectionlist']",CM:"[data-for='cmitem']",TOGGLER:'[data-action="togglecoursecontentsection"]',COLLAPSE:'[data-bs-toggle="collapse"]',TOGGLEALL:'[data-toggle="toggleall"]',ACTIVITYTAG:"li",SECTIONTAG:"li"},this.selectorGenerators={cmNameFor:id=>"[data-cm-name-for='".concat(id,"']"),sectionNameFor:id=>"[data-section-name-for='".concat(id,"']")},this.classes={COLLAPSED:"collapsed",ACTIVITY:"activity",STATEDREADY:"stateready",SECTION:"section"},this.dettachedCms={},this.dettachedSections={},this.sections={},this.cms={},this.sectionReturn=null!==(_descriptor$sectionRe=descriptor.sectionReturn)&&void 0!==_descriptor$sectionRe?_descriptor$sectionRe:null,this.debouncedReloads=new Map}static init(target,selectors,sectionReturn){return new Component({element:document.getElementById(target),reactive:(0,_courseeditor.getCurrentCourseEditor)(),selectors:selectors,sectionReturn:sectionReturn})}stateReady(state){this._indexContents(),this.addEventListener(this.element,"click",this._sectionTogglers);const toogleAll=this.getElement(this.selectors.TOGGLEALL);if(toogleAll){const collapseElementIds=[...this.getElements(this.selectors.COLLAPSE)].map((element=>element.id));toogleAll.setAttribute("aria-controls",collapseElementIds.join(" ")),this.addEventListener(toogleAll,"click",this._allSectionToggler),this.addEventListener(toogleAll,"keydown",(e=>{" "===e.key&&this._allSectionToggler(e)})),this._refreshAllSectionsToggler(state)}this.reactive.supportComponents&&(this.reactive.isEditing&&new _actions.default(this),this.element.classList.add(this.classes.STATEDREADY)),this.addEventListener(this.element,CourseEvents.manualCompletionToggled,this._completionHandler),this.addEventListener(document,"scroll",this._scrollHandler)}_sectionTogglers(event){const sectionlink=event.target.closest(this.selectors.TOGGLER),closestCollapse=event.target.closest(this.selectors.COLLAPSE),isChevron=null==closestCollapse?void 0:closestCollapse.closest(this.selectors.SECTION_ITEM);if(sectionlink||isChevron){var _toggler$classList$co;const section=event.target.closest(this.selectors.SECTION),toggler=section.querySelector(this.selectors.COLLAPSE),isCollapsed=null!==(_toggler$classList$co=null==toggler?void 0:toggler.classList.contains(this.classes.COLLAPSED))&&void 0!==_toggler$classList$co&&_toggler$classList$co,sectionId=section.getAttribute("data-id");this.reactive.dispatch("sectionContentCollapsed",[sectionId],!isCollapsed)}}_allSectionToggler(event){var _course$sectionlist;event.preventDefault();const isAllCollapsed=event.target.closest(this.selectors.TOGGLEALL).classList.contains(this.classes.COLLAPSED),course=this.reactive.get("course");this.reactive.dispatch("sectionContentCollapsed",null!==(_course$sectionlist=course.sectionlist)&&void 0!==_course$sectionlist?_course$sectionlist:[],!isAllCollapsed)}getWatchers(){return this.reactive.sectionReturn=this.sectionReturn,this.reactive.supportComponents?[{watch:"cm.visible:updated",handler:this._reloadCm},{watch:"cm.stealth:updated",handler:this._reloadCm},{watch:"cm.sectionid:updated",handler:this._reloadCm},{watch:"cm.indent:updated",handler:this._reloadCm},{watch:"cm.groupmode:updated",handler:this._reloadCm},{watch:"cm.name:updated",handler:this._refreshCmName},{watch:"section.number:updated",handler:this._refreshSectionNumber},{watch:"section.title:updated",handler:this._refreshSectionTitle},{watch:"section.contentcollapsed:updated",handler:this._refreshSectionCollapsed},{watch:"transaction:start",handler:this._startProcessing},{watch:"course.sectionlist:updated",handler:this._refreshCourseSectionlist},{watch:"section.cmlist:updated",handler:this._refreshSectionCmlist},{watch:"section.visible:updated",handler:this._reloadSection},{watch:"state:updated",handler:this._indexContents}]:[]}_refreshCmName(_ref){let{element:element}=_ref;this.getElements(this.selectorGenerators.cmNameFor(element.id)).forEach((cmNameFor=>{cmNameFor.textContent=element.name}))}_refreshSectionCollapsed(_ref2){var _toggler$classList$co2;let{state:state,element:element}=_ref2;const target=this.getElement(this.selectors.SECTION,element.id);if(!target)throw new Error("Unknown section with ID ".concat(element.id));const toggler=target.querySelector(this.selectors.COLLAPSE),isCollapsed=null!==(_toggler$classList$co2=null==toggler?void 0:toggler.classList.contains(this.classes.COLLAPSED))&&void 0!==_toggler$classList$co2&&_toggler$classList$co2;if(element.contentcollapsed!==isCollapsed){var _toggler$dataset$targ;let collapsibleId=null!==(_toggler$dataset$targ=toggler.dataset.target)&&void 0!==_toggler$dataset$targ?_toggler$dataset$targ:toggler.getAttribute("href");if(!collapsibleId)return;collapsibleId=collapsibleId.replace("#","");const collapsible=document.getElementById(collapsibleId);if(!collapsible)return;element.contentcollapsed?_collapse.default.getOrCreateInstance(collapsible,{toggle:!1}).hide():_collapse.default.getOrCreateInstance(collapsible,{toggle:!1}).show()}this._refreshAllSectionsToggler(state)}_refreshAllSectionsToggler(state){const target=this.getElement(this.selectors.TOGGLEALL);if(!target)return;let allcollapsed=!0,allexpanded=!0;state.section.forEach((section=>{allcollapsed=allcollapsed&§ion.contentcollapsed,allexpanded=allexpanded&&!section.contentcollapsed})),allcollapsed&&(target.classList.add(this.classes.COLLAPSED),target.setAttribute("aria-expanded",!1)),allexpanded&&(target.classList.remove(this.classes.COLLAPSED),target.setAttribute("aria-expanded",!0))}_startProcessing(){this.dettachedCms={},this.dettachedSections={}}_completionHandler(_ref3){let{detail:detail}=_ref3;void 0!==detail&&this.reactive.dispatch("cmCompletion",[detail.cmid],detail.completed)}_scrollHandler(){const pageOffset=window.scrollY,items=this.reactive.getExporter().allItemsArray(this.reactive.state);let pageItem=null;items.every((item=>{const index="section"===item.type?this.sections:this.cms;if(void 0===index[item.id])return!0;const element=index[item.id].element;return pageItem=item,pageOffset>=element.offsetTop})),pageItem&&this.reactive.dispatch("setPageItem",pageItem.type,pageItem.id)}_refreshSectionNumber(_ref4){let{element:element}=_ref4;const target=this.getElement(this.selectors.SECTION,element.id);if(!target)return;target.id="section-".concat(element.number),target.dataset.sectionid=element.number,target.dataset.number=element.number;const inplace=_inplace_editable.default.getInplaceEditable(target.querySelector(this.selectors.SECTION_ITEM));if(inplace){const currentvalue=inplace.getValue(),currentitemid=inplace.getItemId();""===inplace.getValue()&&(currentitemid!=element.id||currentvalue==element.rawtitle&&""!=element.rawtitle||inplace.setValue(element.rawtitle))}}_refreshSectionTitle(_ref5){let{element:element}=_ref5;document.querySelectorAll(this.selectorGenerators.sectionNameFor(element.id)).forEach((sectionNameFor=>{sectionNameFor.textContent=element.title}))}_refreshSectionCmlist(_ref6){var _element$cmlist;let{element:element}=_ref6;const cmlist=null!==(_element$cmlist=element.cmlist)&&void 0!==_element$cmlist?_element$cmlist:[],section=this.getElement(this.selectors.SECTION,element.id),listparent=null==section?void 0:section.querySelector(this.selectors.SECTION_CMLIST),createCm=this._createCmItem.bind(this);listparent&&this._fixOrder(listparent,cmlist,this.selectors.CM,this.dettachedCms,createCm)}_refreshCourseSectionlist(_ref7){let{state:state}=_ref7;if(null!==this.reactive.sectionReturn)return;const sectionlist=this.reactive.getExporter().listedSectionIds(state),listparent=this.getElement(this.selectors.COURSE_SECTIONLIST),createSection=this._createSectionItem.bind(this);listparent&&this._fixOrder(listparent,sectionlist,this.selectors.SECTION,this.dettachedSections,createSection)}_indexContents(){this._scanIndex(this.selectors.SECTION,this.sections,(item=>new _section.default(item))),this._scanIndex(this.selectors.CM,this.cms,(item=>new _cmitem.default(item)))}_scanIndex(selector,index,creationhandler){this.getElements("".concat(selector,":not([data-indexed])")).forEach((item=>{var _item$dataset;null!=item&&null!==(_item$dataset=item.dataset)&&void 0!==_item$dataset&&_item$dataset.id&&(void 0!==index[item.dataset.id]&&index[item.dataset.id].unregister(),index[item.dataset.id]=creationhandler({...this,element:item}),item.dataset.indexed=!0)}))}_reloadCm(_ref8){let{element:element}=_ref8;if(!this.getElement(this.selectors.CM,element.id))return;this._getDebouncedReloadCm(element.id)()}_getDebouncedReloadCm(cmId){const pendingKey="courseformat/content:reloadCm_".concat(cmId);let debouncedReload=this.debouncedReloads.get(pendingKey);if(debouncedReload)return debouncedReload;return debouncedReload=(0,_utils.debounce)((()=>{var _this$reactive$sectio;const pendingReload=new _pending.default(pendingKey);this.debouncedReloads.delete(pendingKey);const cmitem=this.getElement(this.selectors.CM,cmId);if(!cmitem)return pendingReload.resolve();return _fragment.default.loadFragment("core_courseformat","cmitem",_config.default.courseContextId,{id:cmId,courseid:_config.default.courseId,sr:null!==(_this$reactive$sectio=this.reactive.sectionReturn)&&void 0!==_this$reactive$sectio?_this$reactive$sectio:null}).then(((html,js)=>document.contains(cmitem)?(_templates.default.replaceNode(cmitem,html,js),this._indexContents(),pendingReload.resolve(),!0):(pendingReload.resolve(),!1))).catch((()=>{pendingReload.resolve()})),pendingReload}),200,{cancel:!0,pending:!0}),this.debouncedReloads.set(pendingKey,debouncedReload),debouncedReload}_cancelDebouncedReloadCm(cmId){const pendingKey="courseformat/content:reloadCm_".concat(cmId),debouncedReload=this.debouncedReloads.get(pendingKey);debouncedReload&&(debouncedReload.cancel(),this.debouncedReloads.delete(pendingKey))}_reloadSection(_ref9){let{element:element}=_ref9;const pendingReload=new _pending.default("courseformat/content:reloadSection_".concat(element.id)),sectionitem=this.getElement(this.selectors.SECTION,element.id);if(sectionitem){var _this$reactive$sectio2;for(const cmId of element.cmlist)this._cancelDebouncedReloadCm(cmId);_fragment.default.loadFragment("core_courseformat","section",_config.default.courseContextId,{id:element.id,courseid:_config.default.courseId,sr:null!==(_this$reactive$sectio2=this.reactive.sectionReturn)&&void 0!==_this$reactive$sectio2?_this$reactive$sectio2:null}).then(((html,js)=>{_templates.default.replaceNode(sectionitem,html,js),this._indexContents(),pendingReload.resolve()})).catch((()=>{pendingReload.resolve()}))}}_createCmItem(container,cmid){const newItem=document.createElement(this.selectors.ACTIVITYTAG);return newItem.dataset.for="cmitem",newItem.dataset.id=cmid,newItem.id="module-".concat(cmid),newItem.classList.add(this.classes.ACTIVITY),container.append(newItem),this._reloadCm({element:this.reactive.get("cm",cmid)}),newItem}_createSectionItem(container,sectionid){const section=this.reactive.get("section",sectionid),newItem=document.createElement(this.selectors.SECTIONTAG);return newItem.dataset.for="section",newItem.dataset.id=sectionid,newItem.dataset.number=section.number,newItem.id="section-".concat(sectionid),newItem.classList.add(this.classes.SECTION),container.append(newItem),this._reloadSection({element:section}),newItem}async _fixOrder(container,neworder,selector,dettachedelements,createMethod){if(void 0===container)return;if(!neworder.length)return container.classList.add("hidden"),void(container.innerHTML="");container.classList.remove("hidden"),neworder.forEach(((itemid,index)=>{var _ref10,_this$getElement;let item=null!==(_ref10=null!==(_this$getElement=this.getElement(selector,itemid))&&void 0!==_this$getElement?_this$getElement:dettachedelements[itemid])&&void 0!==_ref10?_ref10:createMethod(container,itemid);if(void 0===item)return;const currentitem=container.children[index];void 0!==currentitem?currentitem!==item&&container.insertBefore(item,currentitem):container.append(item)}));const orphanElements=[];for(;container.children.length>neworder.length;){var _lastchild$classList,_lastchild$dataset;const lastchild=container.lastChild;var _lastchild$dataset$id,_lastchild$dataset2;if(null!=lastchild&&null!==(_lastchild$classList=lastchild.classList)&&void 0!==_lastchild$classList&&_lastchild$classList.contains("dndupload-preview")||null!==(_lastchild$dataset=lastchild.dataset)&&void 0!==_lastchild$dataset&&_lastchild$dataset.orphan)orphanElements.push(lastchild);else dettachedelements[null!==(_lastchild$dataset$id=null==lastchild||null===(_lastchild$dataset2=lastchild.dataset)||void 0===_lastchild$dataset2?void 0:_lastchild$dataset2.id)&&void 0!==_lastchild$dataset$id?_lastchild$dataset$id:0]=lastchild;container.removeChild(lastchild)}orphanElements.forEach((element=>{container.append(element)}))}}return _exports.default=Component,_exports.default})); //# sourceMappingURL=content.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/content.min.js.map b/course/format/amd/build/local/content.min.js.map index 16df5c9fdd6db..d336453f8d8e5 100644 --- a/course/format/amd/build/local/content.min.js.map +++ b/course/format/amd/build/local/content.min.js.map @@ -1 +1 @@ -{"version":3,"file":"content.min.js","sources":["../../src/local/content.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 * Course index main component.\n *\n * @module core_courseformat/local/content\n * @class core_courseformat/local/content\n * @copyright 2020 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent} from 'core/reactive';\nimport {debounce} from 'core/utils';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport Config from 'core/config';\nimport inplaceeditable from 'core/inplace_editable';\nimport Section from 'core_courseformat/local/content/section';\nimport CmItem from 'core_courseformat/local/content/section/cmitem';\nimport Fragment from 'core/fragment';\nimport Templates from 'core/templates';\nimport DispatchActions from 'core_courseformat/local/content/actions';\nimport * as CourseEvents from 'core_course/events';\n// The jQuery module is only used for interacting with Boostrap 4. It can we removed when MDL-71979 is integrated.\nimport jQuery from 'jquery';\nimport Pending from 'core/pending';\n\nexport default class Component extends BaseComponent {\n\n /**\n * Constructor hook.\n *\n * @param {Object} descriptor the component descriptor\n */\n create(descriptor) {\n // Optional component name for debugging.\n this.name = 'course_format';\n // Default query selectors.\n this.selectors = {\n SECTION: `[data-for='section']`,\n SECTION_ITEM: `[data-for='section_title']`,\n SECTION_CMLIST: `[data-for='cmlist']`,\n COURSE_SECTIONLIST: `[data-for='course_sectionlist']`,\n CM: `[data-for='cmitem']`,\n TOGGLER: `[data-action=\"togglecoursecontentsection\"]`,\n COLLAPSE: `[data-bs-toggle=\"collapse\"]`,\n TOGGLEALL: `[data-toggle=\"toggleall\"]`,\n // Formats can override the activity tag but a default one is needed to create new elements.\n ACTIVITYTAG: 'li',\n SECTIONTAG: 'li',\n };\n this.selectorGenerators = {\n cmNameFor: (id) => `[data-cm-name-for='${id}']`,\n sectionNameFor: (id) => `[data-section-name-for='${id}']`,\n };\n // Default classes to toggle on refresh.\n this.classes = {\n COLLAPSED: `collapsed`,\n // Course content classes.\n ACTIVITY: `activity`,\n STATEDREADY: `stateready`,\n SECTION: `section`,\n };\n // Array to save dettached elements during element resorting.\n this.dettachedCms = {};\n this.dettachedSections = {};\n // Index of sections and cms components.\n this.sections = {};\n this.cms = {};\n // The page section return.\n this.sectionReturn = descriptor.sectionReturn ?? null;\n this.debouncedReloads = new Map();\n }\n\n /**\n * Static method to create a component instance form the mustahce template.\n *\n * @param {string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @param {number} sectionReturn the content section return\n * @return {Component}\n */\n static init(target, selectors, sectionReturn) {\n return new Component({\n element: document.getElementById(target),\n reactive: getCurrentCourseEditor(),\n selectors,\n sectionReturn,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the state data\n */\n stateReady(state) {\n this._indexContents();\n // Activate section togglers.\n this.addEventListener(this.element, 'click', this._sectionTogglers);\n\n // Collapse/Expand all sections button.\n const toogleAll = this.getElement(this.selectors.TOGGLEALL);\n if (toogleAll) {\n\n // Ensure collapse menu button adds aria-controls attribute referring to each collapsible element.\n const collapseElements = this.getElements(this.selectors.COLLAPSE);\n const collapseElementIds = [...collapseElements].map(element => element.id);\n toogleAll.setAttribute('aria-controls', collapseElementIds.join(' '));\n\n this.addEventListener(toogleAll, 'click', this._allSectionToggler);\n this.addEventListener(toogleAll, 'keydown', e => {\n // Collapse/expand all sections when Space key is pressed on the toggle button.\n if (e.key === ' ') {\n this._allSectionToggler(e);\n }\n });\n this._refreshAllSectionsToggler(state);\n }\n\n if (this.reactive.supportComponents) {\n // Actions are only available in edit mode.\n if (this.reactive.isEditing) {\n new DispatchActions(this);\n }\n\n // Mark content as state ready.\n this.element.classList.add(this.classes.STATEDREADY);\n }\n\n // Capture completion events.\n this.addEventListener(\n this.element,\n CourseEvents.manualCompletionToggled,\n this._completionHandler\n );\n\n // Capture page scroll to update page item.\n this.addEventListener(\n document,\n \"scroll\",\n this._scrollHandler\n );\n }\n\n /**\n * Setup sections toggler.\n *\n * Toggler click is delegated to the main course content element because new sections can\n * appear at any moment and this way we prevent accidental double bindings.\n *\n * @param {Event} event the triggered event\n */\n _sectionTogglers(event) {\n const sectionlink = event.target.closest(this.selectors.TOGGLER);\n const closestCollapse = event.target.closest(this.selectors.COLLAPSE);\n // Assume that chevron is the only collapse toggler in a section heading;\n // I think this is the most efficient way to verify at the moment.\n const isChevron = closestCollapse?.closest(this.selectors.SECTION_ITEM);\n\n if (sectionlink || isChevron) {\n\n const section = event.target.closest(this.selectors.SECTION);\n const toggler = section.querySelector(this.selectors.COLLAPSE);\n const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;\n\n const sectionId = section.getAttribute('data-id');\n this.reactive.dispatch(\n 'sectionContentCollapsed',\n [sectionId],\n !isCollapsed,\n );\n }\n }\n\n /**\n * Handle the collapse/expand all sections button.\n *\n * Toggler click is delegated to the main course content element because new sections can\n * appear at any moment and this way we prevent accidental double bindings.\n *\n * @param {Event} event the triggered event\n */\n _allSectionToggler(event) {\n event.preventDefault();\n\n const target = event.target.closest(this.selectors.TOGGLEALL);\n const isAllCollapsed = target.classList.contains(this.classes.COLLAPSED);\n\n const course = this.reactive.get('course');\n this.reactive.dispatch(\n 'sectionContentCollapsed',\n course.sectionlist ?? [],\n !isAllCollapsed\n );\n }\n\n /**\n * Return the component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n // Section return is a global page variable but most formats define it just before start printing\n // the course content. This is the reason why we define this page setting here.\n this.reactive.sectionReturn = this.sectionReturn;\n\n // Check if the course format is compatible with reactive components.\n if (!this.reactive.supportComponents) {\n return [];\n }\n return [\n // State changes that require to reload some course modules.\n {watch: `cm.visible:updated`, handler: this._reloadCm},\n {watch: `cm.stealth:updated`, handler: this._reloadCm},\n {watch: `cm.sectionid:updated`, handler: this._reloadCm},\n {watch: `cm.indent:updated`, handler: this._reloadCm},\n {watch: `cm.groupmode:updated`, handler: this._reloadCm},\n {watch: `cm.name:updated`, handler: this._refreshCmName},\n // Update section number and title.\n {watch: `section.number:updated`, handler: this._refreshSectionNumber},\n {watch: `section.title:updated`, handler: this._refreshSectionTitle},\n // Collapse and expand sections.\n {watch: `section.contentcollapsed:updated`, handler: this._refreshSectionCollapsed},\n // Sections and cm sorting.\n {watch: `transaction:start`, handler: this._startProcessing},\n {watch: `course.sectionlist:updated`, handler: this._refreshCourseSectionlist},\n {watch: `section.cmlist:updated`, handler: this._refreshSectionCmlist},\n // Section visibility.\n {watch: `section.visible:updated`, handler: this._reloadSection},\n // Reindex sections and cms.\n {watch: `state:updated`, handler: this._indexContents},\n ];\n }\n\n /**\n * Update a course module name on the whole page.\n *\n * @param {object} param\n * @param {Object} param.element details the update details.\n */\n _refreshCmName({element}) {\n // Update classes.\n // Replace the text content of the cm name.\n const allCmNamesFor = this.getElements(\n this.selectorGenerators.cmNameFor(element.id)\n );\n allCmNamesFor.forEach((cmNameFor) => {\n cmNameFor.textContent = element.name;\n });\n }\n\n /**\n * Update section collapsed state via bootstrap 4 if necessary.\n *\n * Formats that do not use bootstrap 4 must override this method in order to keep the section\n * toggling working.\n *\n * @param {object} args\n * @param {Object} args.state The state data\n * @param {Object} args.element The element to update\n */\n _refreshSectionCollapsed({state, element}) {\n const target = this.getElement(this.selectors.SECTION, element.id);\n if (!target) {\n throw new Error(`Unknown section with ID ${element.id}`);\n }\n // Check if it is already done.\n const toggler = target.querySelector(this.selectors.COLLAPSE);\n const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;\n\n if (element.contentcollapsed !== isCollapsed) {\n let collapsibleId = toggler.dataset.target ?? toggler.getAttribute(\"href\");\n if (!collapsibleId) {\n return;\n }\n collapsibleId = collapsibleId.replace('#', '');\n const collapsible = document.getElementById(collapsibleId);\n if (!collapsible) {\n return;\n }\n\n // Course index is based on Bootstrap 4 collapsibles. To collapse them we need jQuery to\n // interact with collapsibles methods. Hopefully, this will change in Bootstrap 5 because\n // it does not require jQuery anymore (when MDL-71979 is integrated).\n jQuery(collapsible).collapse(element.contentcollapsed ? 'hide' : 'show');\n }\n\n this._refreshAllSectionsToggler(state);\n }\n\n /**\n * Refresh the collapse/expand all sections element.\n *\n * @param {Object} state The state data\n */\n _refreshAllSectionsToggler(state) {\n const target = this.getElement(this.selectors.TOGGLEALL);\n if (!target) {\n return;\n }\n // Check if we have all sections collapsed/expanded.\n let allcollapsed = true;\n let allexpanded = true;\n state.section.forEach(\n section => {\n allcollapsed = allcollapsed && section.contentcollapsed;\n allexpanded = allexpanded && !section.contentcollapsed;\n }\n );\n if (allcollapsed) {\n target.classList.add(this.classes.COLLAPSED);\n target.setAttribute('aria-expanded', false);\n }\n if (allexpanded) {\n target.classList.remove(this.classes.COLLAPSED);\n target.setAttribute('aria-expanded', true);\n }\n }\n\n /**\n * Setup the component to start a transaction.\n *\n * Some of the course actions replaces the current DOM element with a new one before updating the\n * course state. This means the component cannot preload any index properly until the transaction starts.\n *\n */\n _startProcessing() {\n // During a section or cm sorting, some elements could be dettached from the DOM and we\n // need to store somewhare in case they are needed later.\n this.dettachedCms = {};\n this.dettachedSections = {};\n }\n\n /**\n * Activity manual completion listener.\n *\n * @param {Event} event the custom ecent\n */\n _completionHandler({detail}) {\n if (detail === undefined) {\n return;\n }\n this.reactive.dispatch('cmCompletion', [detail.cmid], detail.completed);\n }\n\n /**\n * Check the current page scroll and update the active element if necessary.\n */\n _scrollHandler() {\n const pageOffset = window.scrollY;\n const items = this.reactive.getExporter().allItemsArray(this.reactive.state);\n // Check what is the active element now.\n let pageItem = null;\n items.every(item => {\n const index = (item.type === 'section') ? this.sections : this.cms;\n if (index[item.id] === undefined) {\n return true;\n }\n\n const element = index[item.id].element;\n pageItem = item;\n return pageOffset >= element.offsetTop;\n });\n if (pageItem) {\n this.reactive.dispatch('setPageItem', pageItem.type, pageItem.id);\n }\n }\n\n /**\n * Update a course section when the section number changes.\n *\n * The courseActions module used for most course section tools still depends on css classes and\n * section numbers (not id). To prevent inconsistencies when a section is moved, we need to refresh\n * the\n *\n * Course formats can override the section title rendering so the frontend depends heavily on backend\n * rendering. Luckily in edit mode we can trigger a title update using the inplace_editable module.\n *\n * @param {Object} param\n * @param {Object} param.element details the update details.\n */\n _refreshSectionNumber({element}) {\n // Find the element.\n const target = this.getElement(this.selectors.SECTION, element.id);\n if (!target) {\n // Job done. Nothing to refresh.\n return;\n }\n // Update section numbers in all data, css and YUI attributes.\n target.id = `section-${element.number}`;\n // YUI uses section number as section id in data-sectionid, in principle if a format use components\n // don't need this sectionid attribute anymore, but we keep the compatibility in case some plugin\n // use it for legacy purposes.\n target.dataset.sectionid = element.number;\n // The data-number is the attribute used by components to store the section number.\n target.dataset.number = element.number;\n\n // Update title and title inplace editable, if any.\n const inplace = inplaceeditable.getInplaceEditable(target.querySelector(this.selectors.SECTION_ITEM));\n if (inplace) {\n // The course content HTML can be modified at any moment, so the function need to do some checkings\n // to make sure the inplace editable still represents the same itemid.\n const currentvalue = inplace.getValue();\n const currentitemid = inplace.getItemId();\n // Unnamed sections must be recalculated.\n if (inplace.getValue() === '') {\n // The value to send can be an empty value if it is a default name.\n if (currentitemid == element.id && (currentvalue != element.rawtitle || element.rawtitle == '')) {\n inplace.setValue(element.rawtitle);\n }\n }\n }\n }\n\n /**\n * Update a course section name on the whole page.\n *\n * @param {object} param\n * @param {Object} param.element details the update details.\n */\n _refreshSectionTitle({element}) {\n // Replace the text content of the section name in the whole page.\n const allSectionNamesFor = document.querySelectorAll(\n this.selectorGenerators.sectionNameFor(element.id)\n );\n allSectionNamesFor.forEach((sectionNameFor) => {\n sectionNameFor.textContent = element.title;\n });\n }\n\n /**\n * Refresh a section cm list.\n *\n * @param {Object} param\n * @param {Object} param.element details the update details.\n */\n _refreshSectionCmlist({element}) {\n const cmlist = element.cmlist ?? [];\n const section = this.getElement(this.selectors.SECTION, element.id);\n const listparent = section?.querySelector(this.selectors.SECTION_CMLIST);\n // A method to create a fake element to be replaced when the item is ready.\n const createCm = this._createCmItem.bind(this);\n if (listparent) {\n this._fixOrder(listparent, cmlist, this.selectors.CM, this.dettachedCms, createCm);\n }\n }\n\n /**\n * Refresh the section list.\n *\n * @param {Object} param\n * @param {Object} param.state the full state object.\n */\n _refreshCourseSectionlist({state}) {\n // If we have a section return means we only show a single section so no need to fix order.\n if (this.reactive.sectionReturn !== null) {\n return;\n }\n const sectionlist = this.reactive.getExporter().listedSectionIds(state);\n const listparent = this.getElement(this.selectors.COURSE_SECTIONLIST);\n // For now section cannot be created at a frontend level.\n const createSection = this._createSectionItem.bind(this);\n if (listparent) {\n this._fixOrder(listparent, sectionlist, this.selectors.SECTION, this.dettachedSections, createSection);\n }\n }\n\n /**\n * Regenerate content indexes.\n *\n * This method is used when a legacy action refresh some content element.\n */\n _indexContents() {\n // Find unindexed sections.\n this._scanIndex(\n this.selectors.SECTION,\n this.sections,\n (item) => {\n return new Section(item);\n }\n );\n\n // Find unindexed cms.\n this._scanIndex(\n this.selectors.CM,\n this.cms,\n (item) => {\n return new CmItem(item);\n }\n );\n }\n\n /**\n * Reindex a content (section or cm) of the course content.\n *\n * This method is used internally by _indexContents.\n *\n * @param {string} selector the DOM selector to scan\n * @param {*} index the index attribute to update\n * @param {*} creationhandler method to create a new indexed element\n */\n _scanIndex(selector, index, creationhandler) {\n const items = this.getElements(`${selector}:not([data-indexed])`);\n items.forEach((item) => {\n if (!item?.dataset?.id) {\n return;\n }\n // Delete previous item component.\n if (index[item.dataset.id] !== undefined) {\n index[item.dataset.id].unregister();\n }\n // Create the new component.\n index[item.dataset.id] = creationhandler({\n ...this,\n element: item,\n });\n // Mark as indexed.\n item.dataset.indexed = true;\n });\n }\n\n /**\n * Reload a course module contents.\n *\n * Most course module HTML is still strongly backend dependant.\n * Some changes require to get a new version of the module.\n *\n * @param {object} param0 the watcher details\n * @param {object} param0.element the state object\n */\n _reloadCm({element}) {\n if (!this.getElement(this.selectors.CM, element.id)) {\n return;\n }\n const debouncedReload = this._getDebouncedReloadCm(element.id);\n debouncedReload();\n }\n\n /**\n * Generate or get a reload CM debounced function.\n * @param {Number} cmId\n * @returns {Function} the debounced reload function\n */\n _getDebouncedReloadCm(cmId) {\n const pendingKey = `courseformat/content:reloadCm_${cmId}`;\n let debouncedReload = this.debouncedReloads.get(pendingKey);\n if (debouncedReload) {\n return debouncedReload;\n }\n const reload = () => {\n const pendingReload = new Pending(pendingKey);\n this.debouncedReloads.delete(pendingKey);\n const cmitem = this.getElement(this.selectors.CM, cmId);\n if (!cmitem) {\n return pendingReload.resolve();\n }\n const promise = Fragment.loadFragment(\n 'core_courseformat',\n 'cmitem',\n Config.courseContextId,\n {\n id: cmId,\n courseid: Config.courseId,\n sr: this.reactive.sectionReturn ?? null,\n }\n );\n promise.then((html, js) => {\n // Other state change can reload the CM or the section before this one.\n if (!document.contains(cmitem)) {\n pendingReload.resolve();\n return false;\n }\n Templates.replaceNode(cmitem, html, js);\n this._indexContents();\n pendingReload.resolve();\n return true;\n }).catch(() => {\n pendingReload.resolve();\n });\n return pendingReload;\n };\n debouncedReload = debounce(\n reload,\n 200,\n {\n cancel: true, pending: true\n }\n );\n this.debouncedReloads.set(pendingKey, debouncedReload);\n return debouncedReload;\n }\n\n /**\n * Cancel the active reload CM debounced function, if any.\n * @param {Number} cmId\n */\n _cancelDebouncedReloadCm(cmId) {\n const pendingKey = `courseformat/content:reloadCm_${cmId}`;\n const debouncedReload = this.debouncedReloads.get(pendingKey);\n if (!debouncedReload) {\n return;\n }\n debouncedReload.cancel();\n this.debouncedReloads.delete(pendingKey);\n }\n\n /**\n * Reload a course section contents.\n *\n * Section HTML is still strongly backend dependant.\n * Some changes require to get a new version of the section.\n *\n * @param {details} param0 the watcher details\n * @param {object} param0.element the state object\n */\n _reloadSection({element}) {\n const pendingReload = new Pending(`courseformat/content:reloadSection_${element.id}`);\n const sectionitem = this.getElement(this.selectors.SECTION, element.id);\n if (sectionitem) {\n // Cancel any pending reload because the section will reload cms too.\n for (const cmId of element.cmlist) {\n this._cancelDebouncedReloadCm(cmId);\n }\n const promise = Fragment.loadFragment(\n 'core_courseformat',\n 'section',\n Config.courseContextId,\n {\n id: element.id,\n courseid: Config.courseId,\n sr: this.reactive.sectionReturn ?? null,\n }\n );\n promise.then((html, js) => {\n Templates.replaceNode(sectionitem, html, js);\n this._indexContents();\n pendingReload.resolve();\n }).catch(() => {\n pendingReload.resolve();\n });\n }\n }\n\n /**\n * Create a new course module item in a section.\n *\n * Thos method will append a fake item in the container and trigger an ajax request to\n * replace the fake element by the real content.\n *\n * @param {Element} container the container element (section)\n * @param {Number} cmid the course-module ID\n * @returns {Element} the created element\n */\n _createCmItem(container, cmid) {\n const newItem = document.createElement(this.selectors.ACTIVITYTAG);\n newItem.dataset.for = 'cmitem';\n newItem.dataset.id = cmid;\n // The legacy actions.js requires a specific ID and class to refresh the CM.\n newItem.id = `module-${cmid}`;\n newItem.classList.add(this.classes.ACTIVITY);\n container.append(newItem);\n this._reloadCm({\n element: this.reactive.get('cm', cmid),\n });\n return newItem;\n }\n\n /**\n * Create a new section item.\n *\n * This method will append a fake item in the container and trigger an ajax request to\n * replace the fake element by the real content.\n *\n * @param {Element} container the container element (section)\n * @param {Number} sectionid the course-module ID\n * @returns {Element} the created element\n */\n _createSectionItem(container, sectionid) {\n const section = this.reactive.get('section', sectionid);\n const newItem = document.createElement(this.selectors.SECTIONTAG);\n newItem.dataset.for = 'section';\n newItem.dataset.id = sectionid;\n newItem.dataset.number = section.number;\n // The legacy actions.js requires a specific ID and class to refresh the section.\n newItem.id = `section-${sectionid}`;\n newItem.classList.add(this.classes.SECTION);\n container.append(newItem);\n this._reloadSection({\n element: section,\n });\n return newItem;\n }\n\n /**\n * Fix/reorder the section or cms order.\n *\n * @param {Element} container the HTML element to reorder.\n * @param {Array} neworder an array with the ids order\n * @param {string} selector the element selector\n * @param {Object} dettachedelements a list of dettached elements\n * @param {function} createMethod method to create missing elements\n */\n async _fixOrder(container, neworder, selector, dettachedelements, createMethod) {\n if (container === undefined) {\n return;\n }\n\n // Empty lists should not be visible.\n if (!neworder.length) {\n container.classList.add('hidden');\n container.innerHTML = '';\n return;\n }\n\n // Grant the list is visible (in case it was empty).\n container.classList.remove('hidden');\n\n // Move the elements in order at the beginning of the list.\n neworder.forEach((itemid, index) => {\n let item = this.getElement(selector, itemid) ?? dettachedelements[itemid] ?? createMethod(container, itemid);\n if (item === undefined) {\n // Missing elements cannot be sorted.\n return;\n }\n // Get the current elemnt at that position.\n const currentitem = container.children[index];\n if (currentitem === undefined) {\n container.append(item);\n return;\n }\n if (currentitem !== item) {\n container.insertBefore(item, currentitem);\n }\n });\n\n // Remove the remaining elements.\n const orphanElements = [];\n while (container.children.length > neworder.length) {\n const lastchild = container.lastChild;\n // Any orphan element is always displayed after the listed elements.\n // Also, some third-party plugins can use a fake dndupload-preview indicator.\n if (lastchild?.classList?.contains('dndupload-preview') || lastchild.dataset?.orphan) {\n orphanElements.push(lastchild);\n } else {\n dettachedelements[lastchild?.dataset?.id ?? 0] = lastchild;\n }\n container.removeChild(lastchild);\n }\n // Restore orphan elements.\n orphanElements.forEach((element) => {\n container.append(element);\n });\n }\n}\n"],"names":["Component","BaseComponent","create","descriptor","name","selectors","SECTION","SECTION_ITEM","SECTION_CMLIST","COURSE_SECTIONLIST","CM","TOGGLER","COLLAPSE","TOGGLEALL","ACTIVITYTAG","SECTIONTAG","selectorGenerators","cmNameFor","id","sectionNameFor","classes","COLLAPSED","ACTIVITY","STATEDREADY","dettachedCms","dettachedSections","sections","cms","sectionReturn","debouncedReloads","Map","target","element","document","getElementById","reactive","stateReady","state","_indexContents","addEventListener","this","_sectionTogglers","toogleAll","getElement","collapseElementIds","getElements","map","setAttribute","join","_allSectionToggler","e","key","_refreshAllSectionsToggler","supportComponents","isEditing","DispatchActions","classList","add","CourseEvents","manualCompletionToggled","_completionHandler","_scrollHandler","event","sectionlink","closest","closestCollapse","isChevron","section","toggler","querySelector","isCollapsed","contains","sectionId","getAttribute","dispatch","preventDefault","isAllCollapsed","course","get","sectionlist","getWatchers","watch","handler","_reloadCm","_refreshCmName","_refreshSectionNumber","_refreshSectionTitle","_refreshSectionCollapsed","_startProcessing","_refreshCourseSectionlist","_refreshSectionCmlist","_reloadSection","forEach","textContent","Error","contentcollapsed","collapsibleId","dataset","replace","collapsible","collapse","allcollapsed","allexpanded","remove","detail","undefined","cmid","completed","pageOffset","window","scrollY","items","getExporter","allItemsArray","pageItem","every","item","index","type","offsetTop","number","sectionid","inplace","inplaceeditable","getInplaceEditable","currentvalue","getValue","currentitemid","getItemId","rawtitle","setValue","querySelectorAll","title","cmlist","listparent","createCm","_createCmItem","bind","_fixOrder","listedSectionIds","createSection","_createSectionItem","_scanIndex","Section","CmItem","selector","creationhandler","_item$dataset","unregister","indexed","_getDebouncedReloadCm","debouncedReload","cmId","pendingKey","pendingReload","Pending","delete","cmitem","resolve","Fragment","loadFragment","Config","courseContextId","courseid","courseId","sr","then","html","js","replaceNode","catch","cancel","pending","set","_cancelDebouncedReloadCm","sectionitem","container","newItem","createElement","for","append","neworder","dettachedelements","createMethod","length","innerHTML","itemid","currentitem","children","insertBefore","orphanElements","lastchild","lastChild","_lastchild$dataset","orphan","push","_lastchild$dataset2","removeChild"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"content.min.js","sources":["../../src/local/content.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 * Course index main component.\n *\n * @module core_courseformat/local/content\n * @class core_courseformat/local/content\n * @copyright 2020 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent} from 'core/reactive';\nimport Collapse from 'theme_boost/bootstrap/collapse';\nimport {debounce} from 'core/utils';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport Config from 'core/config';\nimport inplaceeditable from 'core/inplace_editable';\nimport Section from 'core_courseformat/local/content/section';\nimport CmItem from 'core_courseformat/local/content/section/cmitem';\nimport Fragment from 'core/fragment';\nimport Templates from 'core/templates';\nimport DispatchActions from 'core_courseformat/local/content/actions';\nimport * as CourseEvents from 'core_course/events';\nimport Pending from 'core/pending';\n\nexport default class Component extends BaseComponent {\n\n /**\n * Constructor hook.\n *\n * @param {Object} descriptor the component descriptor\n */\n create(descriptor) {\n // Optional component name for debugging.\n this.name = 'course_format';\n // Default query selectors.\n this.selectors = {\n SECTION: `[data-for='section']`,\n SECTION_ITEM: `[data-for='section_title']`,\n SECTION_CMLIST: `[data-for='cmlist']`,\n COURSE_SECTIONLIST: `[data-for='course_sectionlist']`,\n CM: `[data-for='cmitem']`,\n TOGGLER: `[data-action=\"togglecoursecontentsection\"]`,\n COLLAPSE: `[data-bs-toggle=\"collapse\"]`,\n TOGGLEALL: `[data-toggle=\"toggleall\"]`,\n // Formats can override the activity tag but a default one is needed to create new elements.\n ACTIVITYTAG: 'li',\n SECTIONTAG: 'li',\n };\n this.selectorGenerators = {\n cmNameFor: (id) => `[data-cm-name-for='${id}']`,\n sectionNameFor: (id) => `[data-section-name-for='${id}']`,\n };\n // Default classes to toggle on refresh.\n this.classes = {\n COLLAPSED: `collapsed`,\n // Course content classes.\n ACTIVITY: `activity`,\n STATEDREADY: `stateready`,\n SECTION: `section`,\n };\n // Array to save dettached elements during element resorting.\n this.dettachedCms = {};\n this.dettachedSections = {};\n // Index of sections and cms components.\n this.sections = {};\n this.cms = {};\n // The page section return.\n this.sectionReturn = descriptor.sectionReturn ?? null;\n this.debouncedReloads = new Map();\n }\n\n /**\n * Static method to create a component instance form the mustahce template.\n *\n * @param {string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @param {number} sectionReturn the content section return\n * @return {Component}\n */\n static init(target, selectors, sectionReturn) {\n return new Component({\n element: document.getElementById(target),\n reactive: getCurrentCourseEditor(),\n selectors,\n sectionReturn,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the state data\n */\n stateReady(state) {\n this._indexContents();\n // Activate section togglers.\n this.addEventListener(this.element, 'click', this._sectionTogglers);\n\n // Collapse/Expand all sections button.\n const toogleAll = this.getElement(this.selectors.TOGGLEALL);\n if (toogleAll) {\n\n // Ensure collapse menu button adds aria-controls attribute referring to each collapsible element.\n const collapseElements = this.getElements(this.selectors.COLLAPSE);\n const collapseElementIds = [...collapseElements].map(element => element.id);\n toogleAll.setAttribute('aria-controls', collapseElementIds.join(' '));\n\n this.addEventListener(toogleAll, 'click', this._allSectionToggler);\n this.addEventListener(toogleAll, 'keydown', e => {\n // Collapse/expand all sections when Space key is pressed on the toggle button.\n if (e.key === ' ') {\n this._allSectionToggler(e);\n }\n });\n this._refreshAllSectionsToggler(state);\n }\n\n if (this.reactive.supportComponents) {\n // Actions are only available in edit mode.\n if (this.reactive.isEditing) {\n new DispatchActions(this);\n }\n\n // Mark content as state ready.\n this.element.classList.add(this.classes.STATEDREADY);\n }\n\n // Capture completion events.\n this.addEventListener(\n this.element,\n CourseEvents.manualCompletionToggled,\n this._completionHandler\n );\n\n // Capture page scroll to update page item.\n this.addEventListener(\n document,\n \"scroll\",\n this._scrollHandler\n );\n }\n\n /**\n * Setup sections toggler.\n *\n * Toggler click is delegated to the main course content element because new sections can\n * appear at any moment and this way we prevent accidental double bindings.\n *\n * @param {Event} event the triggered event\n */\n _sectionTogglers(event) {\n const sectionlink = event.target.closest(this.selectors.TOGGLER);\n const closestCollapse = event.target.closest(this.selectors.COLLAPSE);\n // Assume that chevron is the only collapse toggler in a section heading;\n // I think this is the most efficient way to verify at the moment.\n const isChevron = closestCollapse?.closest(this.selectors.SECTION_ITEM);\n\n if (sectionlink || isChevron) {\n\n const section = event.target.closest(this.selectors.SECTION);\n const toggler = section.querySelector(this.selectors.COLLAPSE);\n const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;\n\n const sectionId = section.getAttribute('data-id');\n this.reactive.dispatch(\n 'sectionContentCollapsed',\n [sectionId],\n !isCollapsed,\n );\n }\n }\n\n /**\n * Handle the collapse/expand all sections button.\n *\n * Toggler click is delegated to the main course content element because new sections can\n * appear at any moment and this way we prevent accidental double bindings.\n *\n * @param {Event} event the triggered event\n */\n _allSectionToggler(event) {\n event.preventDefault();\n\n const target = event.target.closest(this.selectors.TOGGLEALL);\n const isAllCollapsed = target.classList.contains(this.classes.COLLAPSED);\n\n const course = this.reactive.get('course');\n this.reactive.dispatch(\n 'sectionContentCollapsed',\n course.sectionlist ?? [],\n !isAllCollapsed\n );\n }\n\n /**\n * Return the component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n // Section return is a global page variable but most formats define it just before start printing\n // the course content. This is the reason why we define this page setting here.\n this.reactive.sectionReturn = this.sectionReturn;\n\n // Check if the course format is compatible with reactive components.\n if (!this.reactive.supportComponents) {\n return [];\n }\n return [\n // State changes that require to reload some course modules.\n {watch: `cm.visible:updated`, handler: this._reloadCm},\n {watch: `cm.stealth:updated`, handler: this._reloadCm},\n {watch: `cm.sectionid:updated`, handler: this._reloadCm},\n {watch: `cm.indent:updated`, handler: this._reloadCm},\n {watch: `cm.groupmode:updated`, handler: this._reloadCm},\n {watch: `cm.name:updated`, handler: this._refreshCmName},\n // Update section number and title.\n {watch: `section.number:updated`, handler: this._refreshSectionNumber},\n {watch: `section.title:updated`, handler: this._refreshSectionTitle},\n // Collapse and expand sections.\n {watch: `section.contentcollapsed:updated`, handler: this._refreshSectionCollapsed},\n // Sections and cm sorting.\n {watch: `transaction:start`, handler: this._startProcessing},\n {watch: `course.sectionlist:updated`, handler: this._refreshCourseSectionlist},\n {watch: `section.cmlist:updated`, handler: this._refreshSectionCmlist},\n // Section visibility.\n {watch: `section.visible:updated`, handler: this._reloadSection},\n // Reindex sections and cms.\n {watch: `state:updated`, handler: this._indexContents},\n ];\n }\n\n /**\n * Update a course module name on the whole page.\n *\n * @param {object} param\n * @param {Object} param.element details the update details.\n */\n _refreshCmName({element}) {\n // Update classes.\n // Replace the text content of the cm name.\n const allCmNamesFor = this.getElements(\n this.selectorGenerators.cmNameFor(element.id)\n );\n allCmNamesFor.forEach((cmNameFor) => {\n cmNameFor.textContent = element.name;\n });\n }\n\n /**\n * Update section collapsed state via bootstrap 4 if necessary.\n *\n * Formats that do not use bootstrap 4 must override this method in order to keep the section\n * toggling working.\n *\n * @param {object} args\n * @param {Object} args.state The state data\n * @param {Object} args.element The element to update\n */\n _refreshSectionCollapsed({state, element}) {\n const target = this.getElement(this.selectors.SECTION, element.id);\n if (!target) {\n throw new Error(`Unknown section with ID ${element.id}`);\n }\n // Check if it is already done.\n const toggler = target.querySelector(this.selectors.COLLAPSE);\n const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;\n\n if (element.contentcollapsed !== isCollapsed) {\n let collapsibleId = toggler.dataset.target ?? toggler.getAttribute(\"href\");\n if (!collapsibleId) {\n return;\n }\n collapsibleId = collapsibleId.replace('#', '');\n const collapsible = document.getElementById(collapsibleId);\n if (!collapsible) {\n return;\n }\n if (element.contentcollapsed) {\n Collapse.getOrCreateInstance(collapsible, {toggle: false}).hide();\n } else {\n Collapse.getOrCreateInstance(collapsible, {toggle: false}).show();\n }\n }\n\n this._refreshAllSectionsToggler(state);\n }\n\n /**\n * Refresh the collapse/expand all sections element.\n *\n * @param {Object} state The state data\n */\n _refreshAllSectionsToggler(state) {\n const target = this.getElement(this.selectors.TOGGLEALL);\n if (!target) {\n return;\n }\n // Check if we have all sections collapsed/expanded.\n let allcollapsed = true;\n let allexpanded = true;\n state.section.forEach(\n section => {\n allcollapsed = allcollapsed && section.contentcollapsed;\n allexpanded = allexpanded && !section.contentcollapsed;\n }\n );\n if (allcollapsed) {\n target.classList.add(this.classes.COLLAPSED);\n target.setAttribute('aria-expanded', false);\n }\n if (allexpanded) {\n target.classList.remove(this.classes.COLLAPSED);\n target.setAttribute('aria-expanded', true);\n }\n }\n\n /**\n * Setup the component to start a transaction.\n *\n * Some of the course actions replaces the current DOM element with a new one before updating the\n * course state. This means the component cannot preload any index properly until the transaction starts.\n *\n */\n _startProcessing() {\n // During a section or cm sorting, some elements could be dettached from the DOM and we\n // need to store somewhare in case they are needed later.\n this.dettachedCms = {};\n this.dettachedSections = {};\n }\n\n /**\n * Activity manual completion listener.\n *\n * @param {Event} event the custom ecent\n */\n _completionHandler({detail}) {\n if (detail === undefined) {\n return;\n }\n this.reactive.dispatch('cmCompletion', [detail.cmid], detail.completed);\n }\n\n /**\n * Check the current page scroll and update the active element if necessary.\n */\n _scrollHandler() {\n const pageOffset = window.scrollY;\n const items = this.reactive.getExporter().allItemsArray(this.reactive.state);\n // Check what is the active element now.\n let pageItem = null;\n items.every(item => {\n const index = (item.type === 'section') ? this.sections : this.cms;\n if (index[item.id] === undefined) {\n return true;\n }\n\n const element = index[item.id].element;\n pageItem = item;\n return pageOffset >= element.offsetTop;\n });\n if (pageItem) {\n this.reactive.dispatch('setPageItem', pageItem.type, pageItem.id);\n }\n }\n\n /**\n * Update a course section when the section number changes.\n *\n * The courseActions module used for most course section tools still depends on css classes and\n * section numbers (not id). To prevent inconsistencies when a section is moved, we need to refresh\n * the\n *\n * Course formats can override the section title rendering so the frontend depends heavily on backend\n * rendering. Luckily in edit mode we can trigger a title update using the inplace_editable module.\n *\n * @param {Object} param\n * @param {Object} param.element details the update details.\n */\n _refreshSectionNumber({element}) {\n // Find the element.\n const target = this.getElement(this.selectors.SECTION, element.id);\n if (!target) {\n // Job done. Nothing to refresh.\n return;\n }\n // Update section numbers in all data, css and YUI attributes.\n target.id = `section-${element.number}`;\n // YUI uses section number as section id in data-sectionid, in principle if a format use components\n // don't need this sectionid attribute anymore, but we keep the compatibility in case some plugin\n // use it for legacy purposes.\n target.dataset.sectionid = element.number;\n // The data-number is the attribute used by components to store the section number.\n target.dataset.number = element.number;\n\n // Update title and title inplace editable, if any.\n const inplace = inplaceeditable.getInplaceEditable(target.querySelector(this.selectors.SECTION_ITEM));\n if (inplace) {\n // The course content HTML can be modified at any moment, so the function need to do some checkings\n // to make sure the inplace editable still represents the same itemid.\n const currentvalue = inplace.getValue();\n const currentitemid = inplace.getItemId();\n // Unnamed sections must be recalculated.\n if (inplace.getValue() === '') {\n // The value to send can be an empty value if it is a default name.\n if (currentitemid == element.id && (currentvalue != element.rawtitle || element.rawtitle == '')) {\n inplace.setValue(element.rawtitle);\n }\n }\n }\n }\n\n /**\n * Update a course section name on the whole page.\n *\n * @param {object} param\n * @param {Object} param.element details the update details.\n */\n _refreshSectionTitle({element}) {\n // Replace the text content of the section name in the whole page.\n const allSectionNamesFor = document.querySelectorAll(\n this.selectorGenerators.sectionNameFor(element.id)\n );\n allSectionNamesFor.forEach((sectionNameFor) => {\n sectionNameFor.textContent = element.title;\n });\n }\n\n /**\n * Refresh a section cm list.\n *\n * @param {Object} param\n * @param {Object} param.element details the update details.\n */\n _refreshSectionCmlist({element}) {\n const cmlist = element.cmlist ?? [];\n const section = this.getElement(this.selectors.SECTION, element.id);\n const listparent = section?.querySelector(this.selectors.SECTION_CMLIST);\n // A method to create a fake element to be replaced when the item is ready.\n const createCm = this._createCmItem.bind(this);\n if (listparent) {\n this._fixOrder(listparent, cmlist, this.selectors.CM, this.dettachedCms, createCm);\n }\n }\n\n /**\n * Refresh the section list.\n *\n * @param {Object} param\n * @param {Object} param.state the full state object.\n */\n _refreshCourseSectionlist({state}) {\n // If we have a section return means we only show a single section so no need to fix order.\n if (this.reactive.sectionReturn !== null) {\n return;\n }\n const sectionlist = this.reactive.getExporter().listedSectionIds(state);\n const listparent = this.getElement(this.selectors.COURSE_SECTIONLIST);\n // For now section cannot be created at a frontend level.\n const createSection = this._createSectionItem.bind(this);\n if (listparent) {\n this._fixOrder(listparent, sectionlist, this.selectors.SECTION, this.dettachedSections, createSection);\n }\n }\n\n /**\n * Regenerate content indexes.\n *\n * This method is used when a legacy action refresh some content element.\n */\n _indexContents() {\n // Find unindexed sections.\n this._scanIndex(\n this.selectors.SECTION,\n this.sections,\n (item) => {\n return new Section(item);\n }\n );\n\n // Find unindexed cms.\n this._scanIndex(\n this.selectors.CM,\n this.cms,\n (item) => {\n return new CmItem(item);\n }\n );\n }\n\n /**\n * Reindex a content (section or cm) of the course content.\n *\n * This method is used internally by _indexContents.\n *\n * @param {string} selector the DOM selector to scan\n * @param {*} index the index attribute to update\n * @param {*} creationhandler method to create a new indexed element\n */\n _scanIndex(selector, index, creationhandler) {\n const items = this.getElements(`${selector}:not([data-indexed])`);\n items.forEach((item) => {\n if (!item?.dataset?.id) {\n return;\n }\n // Delete previous item component.\n if (index[item.dataset.id] !== undefined) {\n index[item.dataset.id].unregister();\n }\n // Create the new component.\n index[item.dataset.id] = creationhandler({\n ...this,\n element: item,\n });\n // Mark as indexed.\n item.dataset.indexed = true;\n });\n }\n\n /**\n * Reload a course module contents.\n *\n * Most course module HTML is still strongly backend dependant.\n * Some changes require to get a new version of the module.\n *\n * @param {object} param0 the watcher details\n * @param {object} param0.element the state object\n */\n _reloadCm({element}) {\n if (!this.getElement(this.selectors.CM, element.id)) {\n return;\n }\n const debouncedReload = this._getDebouncedReloadCm(element.id);\n debouncedReload();\n }\n\n /**\n * Generate or get a reload CM debounced function.\n * @param {Number} cmId\n * @returns {Function} the debounced reload function\n */\n _getDebouncedReloadCm(cmId) {\n const pendingKey = `courseformat/content:reloadCm_${cmId}`;\n let debouncedReload = this.debouncedReloads.get(pendingKey);\n if (debouncedReload) {\n return debouncedReload;\n }\n const reload = () => {\n const pendingReload = new Pending(pendingKey);\n this.debouncedReloads.delete(pendingKey);\n const cmitem = this.getElement(this.selectors.CM, cmId);\n if (!cmitem) {\n return pendingReload.resolve();\n }\n const promise = Fragment.loadFragment(\n 'core_courseformat',\n 'cmitem',\n Config.courseContextId,\n {\n id: cmId,\n courseid: Config.courseId,\n sr: this.reactive.sectionReturn ?? null,\n }\n );\n promise.then((html, js) => {\n // Other state change can reload the CM or the section before this one.\n if (!document.contains(cmitem)) {\n pendingReload.resolve();\n return false;\n }\n Templates.replaceNode(cmitem, html, js);\n this._indexContents();\n pendingReload.resolve();\n return true;\n }).catch(() => {\n pendingReload.resolve();\n });\n return pendingReload;\n };\n debouncedReload = debounce(\n reload,\n 200,\n {\n cancel: true, pending: true\n }\n );\n this.debouncedReloads.set(pendingKey, debouncedReload);\n return debouncedReload;\n }\n\n /**\n * Cancel the active reload CM debounced function, if any.\n * @param {Number} cmId\n */\n _cancelDebouncedReloadCm(cmId) {\n const pendingKey = `courseformat/content:reloadCm_${cmId}`;\n const debouncedReload = this.debouncedReloads.get(pendingKey);\n if (!debouncedReload) {\n return;\n }\n debouncedReload.cancel();\n this.debouncedReloads.delete(pendingKey);\n }\n\n /**\n * Reload a course section contents.\n *\n * Section HTML is still strongly backend dependant.\n * Some changes require to get a new version of the section.\n *\n * @param {details} param0 the watcher details\n * @param {object} param0.element the state object\n */\n _reloadSection({element}) {\n const pendingReload = new Pending(`courseformat/content:reloadSection_${element.id}`);\n const sectionitem = this.getElement(this.selectors.SECTION, element.id);\n if (sectionitem) {\n // Cancel any pending reload because the section will reload cms too.\n for (const cmId of element.cmlist) {\n this._cancelDebouncedReloadCm(cmId);\n }\n const promise = Fragment.loadFragment(\n 'core_courseformat',\n 'section',\n Config.courseContextId,\n {\n id: element.id,\n courseid: Config.courseId,\n sr: this.reactive.sectionReturn ?? null,\n }\n );\n promise.then((html, js) => {\n Templates.replaceNode(sectionitem, html, js);\n this._indexContents();\n pendingReload.resolve();\n }).catch(() => {\n pendingReload.resolve();\n });\n }\n }\n\n /**\n * Create a new course module item in a section.\n *\n * Thos method will append a fake item in the container and trigger an ajax request to\n * replace the fake element by the real content.\n *\n * @param {Element} container the container element (section)\n * @param {Number} cmid the course-module ID\n * @returns {Element} the created element\n */\n _createCmItem(container, cmid) {\n const newItem = document.createElement(this.selectors.ACTIVITYTAG);\n newItem.dataset.for = 'cmitem';\n newItem.dataset.id = cmid;\n // The legacy actions.js requires a specific ID and class to refresh the CM.\n newItem.id = `module-${cmid}`;\n newItem.classList.add(this.classes.ACTIVITY);\n container.append(newItem);\n this._reloadCm({\n element: this.reactive.get('cm', cmid),\n });\n return newItem;\n }\n\n /**\n * Create a new section item.\n *\n * This method will append a fake item in the container and trigger an ajax request to\n * replace the fake element by the real content.\n *\n * @param {Element} container the container element (section)\n * @param {Number} sectionid the course-module ID\n * @returns {Element} the created element\n */\n _createSectionItem(container, sectionid) {\n const section = this.reactive.get('section', sectionid);\n const newItem = document.createElement(this.selectors.SECTIONTAG);\n newItem.dataset.for = 'section';\n newItem.dataset.id = sectionid;\n newItem.dataset.number = section.number;\n // The legacy actions.js requires a specific ID and class to refresh the section.\n newItem.id = `section-${sectionid}`;\n newItem.classList.add(this.classes.SECTION);\n container.append(newItem);\n this._reloadSection({\n element: section,\n });\n return newItem;\n }\n\n /**\n * Fix/reorder the section or cms order.\n *\n * @param {Element} container the HTML element to reorder.\n * @param {Array} neworder an array with the ids order\n * @param {string} selector the element selector\n * @param {Object} dettachedelements a list of dettached elements\n * @param {function} createMethod method to create missing elements\n */\n async _fixOrder(container, neworder, selector, dettachedelements, createMethod) {\n if (container === undefined) {\n return;\n }\n\n // Empty lists should not be visible.\n if (!neworder.length) {\n container.classList.add('hidden');\n container.innerHTML = '';\n return;\n }\n\n // Grant the list is visible (in case it was empty).\n container.classList.remove('hidden');\n\n // Move the elements in order at the beginning of the list.\n neworder.forEach((itemid, index) => {\n let item = this.getElement(selector, itemid) ?? dettachedelements[itemid] ?? createMethod(container, itemid);\n if (item === undefined) {\n // Missing elements cannot be sorted.\n return;\n }\n // Get the current elemnt at that position.\n const currentitem = container.children[index];\n if (currentitem === undefined) {\n container.append(item);\n return;\n }\n if (currentitem !== item) {\n container.insertBefore(item, currentitem);\n }\n });\n\n // Remove the remaining elements.\n const orphanElements = [];\n while (container.children.length > neworder.length) {\n const lastchild = container.lastChild;\n // Any orphan element is always displayed after the listed elements.\n // Also, some third-party plugins can use a fake dndupload-preview indicator.\n if (lastchild?.classList?.contains('dndupload-preview') || lastchild.dataset?.orphan) {\n orphanElements.push(lastchild);\n } else {\n dettachedelements[lastchild?.dataset?.id ?? 0] = lastchild;\n }\n container.removeChild(lastchild);\n }\n // Restore orphan elements.\n orphanElements.forEach((element) => {\n container.append(element);\n });\n }\n}\n"],"names":["Component","BaseComponent","create","descriptor","name","selectors","SECTION","SECTION_ITEM","SECTION_CMLIST","COURSE_SECTIONLIST","CM","TOGGLER","COLLAPSE","TOGGLEALL","ACTIVITYTAG","SECTIONTAG","selectorGenerators","cmNameFor","id","sectionNameFor","classes","COLLAPSED","ACTIVITY","STATEDREADY","dettachedCms","dettachedSections","sections","cms","sectionReturn","debouncedReloads","Map","target","element","document","getElementById","reactive","stateReady","state","_indexContents","addEventListener","this","_sectionTogglers","toogleAll","getElement","collapseElementIds","getElements","map","setAttribute","join","_allSectionToggler","e","key","_refreshAllSectionsToggler","supportComponents","isEditing","DispatchActions","classList","add","CourseEvents","manualCompletionToggled","_completionHandler","_scrollHandler","event","sectionlink","closest","closestCollapse","isChevron","section","toggler","querySelector","isCollapsed","contains","sectionId","getAttribute","dispatch","preventDefault","isAllCollapsed","course","get","sectionlist","getWatchers","watch","handler","_reloadCm","_refreshCmName","_refreshSectionNumber","_refreshSectionTitle","_refreshSectionCollapsed","_startProcessing","_refreshCourseSectionlist","_refreshSectionCmlist","_reloadSection","forEach","textContent","Error","contentcollapsed","collapsibleId","dataset","replace","collapsible","getOrCreateInstance","toggle","hide","show","allcollapsed","allexpanded","remove","detail","undefined","cmid","completed","pageOffset","window","scrollY","items","getExporter","allItemsArray","pageItem","every","item","index","type","offsetTop","number","sectionid","inplace","inplaceeditable","getInplaceEditable","currentvalue","getValue","currentitemid","getItemId","rawtitle","setValue","querySelectorAll","title","cmlist","listparent","createCm","_createCmItem","bind","_fixOrder","listedSectionIds","createSection","_createSectionItem","_scanIndex","Section","CmItem","selector","creationhandler","_item$dataset","unregister","indexed","_getDebouncedReloadCm","debouncedReload","cmId","pendingKey","pendingReload","Pending","delete","cmitem","resolve","Fragment","loadFragment","Config","courseContextId","courseid","courseId","sr","then","html","js","replaceNode","catch","cancel","pending","set","_cancelDebouncedReloadCm","sectionitem","container","newItem","createElement","for","append","neworder","dettachedelements","createMethod","length","innerHTML","itemid","currentitem","children","insertBefore","orphanElements","lastchild","lastChild","_lastchild$dataset","orphan","push","_lastchild$dataset2","removeChild"],"mappings":"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"} \ No newline at end of file diff --git a/course/format/amd/build/local/content/actions.min.js b/course/format/amd/build/local/content/actions.min.js index e255af3fc345f..9720d18f6a867 100644 --- a/course/format/amd/build/local/content/actions.min.js +++ b/course/format/amd/build/local/content/actions.min.js @@ -1,4 +1,4 @@ -define("core_courseformat/local/content/actions",["exports","core/reactive","core/modal","core/modal_save_cancel","core/modal_delete_cancel","core/modal_events","core/templates","core/prefetch","core/str","core/normalise","core_courseformat/local/content/actions/bulkselection","core_course/events","core/pending","core_courseformat/local/courseeditor/contenttree","jquery","core/notification"],(function(_exports,_reactive,_modal,_modal_save_cancel,_modal_delete_cancel,_modal_events,_templates,_prefetch,_str,_normalise,_bulkselection,CourseEvents,_pending,_contenttree,_jquery,_notification){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("core_courseformat/local/content/actions",["exports","core/reactive","theme_boost/bootstrap/collapse","core/modal","core/modal_save_cancel","core/modal_delete_cancel","core/modal_events","core/templates","core/prefetch","core/str","core/normalise","core_courseformat/local/content/actions/bulkselection","core_course/events","core/pending","core_courseformat/local/courseeditor/contenttree","core/notification"],(function(_exports,_reactive,_collapse,_modal,_modal_save_cancel,_modal_delete_cancel,_modal_events,_templates,_prefetch,_str,_normalise,_bulkselection,CourseEvents,_pending,_contenttree,_notification){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Course state actions dispatcher. * @@ -9,6 +9,6 @@ define("core_courseformat/local/content/actions",["exports","core/reactive","cor * @class core_courseformat/local/content/actions * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=_interopRequireDefault(_modal),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_modal_delete_cancel=_interopRequireDefault(_modal_delete_cancel),_modal_events=_interopRequireDefault(_modal_events),_templates=_interopRequireDefault(_templates),CourseEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CourseEvents),_pending=_interopRequireDefault(_pending),_contenttree=_interopRequireDefault(_contenttree),_jquery=_interopRequireDefault(_jquery),_notification=_interopRequireDefault(_notification),(0,_prefetch.prefetchStrings)("core",["movecoursesection","movecoursemodule","confirm","delete"]);const directMutations={sectionHide:"sectionHide",sectionShow:"sectionShow",cmHide:"cmHide",cmShow:"cmShow",cmStealth:"cmStealth",cmMoveRight:"cmMoveRight",cmMoveLeft:"cmMoveLeft",cmNoGroups:"cmNoGroups",cmSeparateGroups:"cmSeparateGroups",cmVisibleGroups:"cmVisibleGroups"};class _default extends _reactive.BaseComponent{create(){this.name="content_actions",this.selectors={ACTIONLINK:"[data-action]",SECTIONLINK:"[data-for='section']",CMLINK:"[data-for='cm']",SECTIONNODE:"[data-for='sectionnode']",MODALTOGGLER:"[data-bs-toggle='collapse']",ADDSECTION:"[data-action='addSection']",CONTENTTREE:"#destination-selector",ACTIONMENU:".action-menu",ACTIONMENUTOGGLER:'[data-bs-toggle="dropdown"]',OPTIONSRADIO:"[type='radio']",COURSEADDSECTION:"#course-addsection",MAXSECTIONSWARNING:"[data-region='max-sections-warning']",ADDSECTIONREGION:"[data-region='section-addsection']"},this.classes={DISABLED:"disabled",ITALIC:"fst-italic",DISPLAYNONE:"d-none"}}static addActions(actions){for(const[action,mutationReference]of Object.entries(actions)){if("function"!=typeof mutationReference&&"string"!=typeof mutationReference)throw new Error("".concat(action," action must be a mutation name or a function"));directMutations[action]=mutationReference}}stateReady(state){this.addEventListener(this.element,"click",this._dispatchClick),this._checkSectionlist({state:state}),this.addEventListener(this.element,CourseEvents.sectionRefreshed,(()=>this._checkSectionlist({state:state})))}getWatchers(){return[{watch:"course.sectionlist:updated",handler:this._checkSectionlist}]}_dispatchClick(event){const target=event.target.closest(this.selectors.ACTIONLINK);if(!target)return;if(target.classList.contains(this.classes.DISABLED))return void event.preventDefault();const actionName=target.dataset.action,methodName=this._actionMethodName(actionName);if(void 0===this[methodName])return void 0!==directMutations[actionName]?"function"==typeof directMutations[actionName]?void directMutations[actionName](target,event):void this._requestMutationAction(target,event,directMutations[actionName]):void 0;this[methodName](target,event)}_actionMethodName(name){const requestName=name.charAt(0).toUpperCase()+name.slice(1);return"_request".concat(requestName)}_checkSectionlist(_ref){let{state:state}=_ref;this._setAddSectionLocked(state.course.sectionlist.length>state.course.maxsections)}_getTargetIds(target){var _target$dataset,_target$dataset2;let ids=[];null!=target&&null!==(_target$dataset=target.dataset)&&void 0!==_target$dataset&&_target$dataset.id&&ids.push(target.dataset.id);const bulkType=null==target||null===(_target$dataset2=target.dataset)||void 0===_target$dataset2?void 0:_target$dataset2.bulk;if(!bulkType)return ids;const bulk=this.reactive.get("bulk");return bulk.enabled&&bulk.selectedType===bulkType&&(ids=[...ids,...bulk.selection]),ids}async _requestMoveSection(target,event){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;event.preventDefault();const pendingModalReady=new _pending.default("courseformat/actions:prepareMoveSectionModal"),editTools=this._getClosestActionMenuToogler(target),data=this.reactive.getExporter().course(this.reactive.state);let titleText=null,sectionInfo=null;1==sectionIds.length?(sectionInfo=this.reactive.get("section",sectionIds[0]),data.sectionid=sectionInfo.id,data.sectiontitle=sectionInfo.title,data.information=await this.reactive.getFormatString("sectionmove_info",data.sectiontitle),titleText=this.reactive.getFormatString("sectionmove_title")):(data.information=await this.reactive.getFormatString("sectionsmove_info",sectionIds.length),titleText=this.reactive.getFormatString("sectionsmove_title"));const modal=await this._modalBodyRenderedPromise(_modal.default,{title:titleText,body:_templates.default.render("core_courseformat/local/content/movesection",data)}),modalBody=(0,_normalise.getFirst)(modal.getBody());sectionIds.forEach((sectionId=>{const currentElement=modalBody.querySelector("".concat(this.selectors.SECTIONLINK,"[data-id='").concat(sectionId,"']"));this._disableLink(currentElement)})),new _contenttree.default(modalBody.querySelector(this.selectors.CONTENTTREE),{SECTION:this.selectors.SECTIONNODE,TOGGLER:this.selectors.MODALTOGGLER,COLLAPSE:this.selectors.MODALTOGGLER},!0),modalBody.addEventListener("click",(event=>{const target=event.target;target.matches("a")&&"section"==target.dataset.for&&void 0!==target.dataset.id&&(target.getAttribute("aria-disabled")||(event.preventDefault(),this.reactive.dispatch("sectionMoveAfter",sectionIds,target.dataset.id),this._destroyModal(modal,editTools)))})),pendingModalReady.resolve()}async _requestMoveCm(target,event){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;event.preventDefault();const pendingModalReady=new _pending.default("courseformat/actions:prepareMoveCmModal"),editTools=this._getClosestActionMenuToogler(target),exporter=this.reactive.getExporter(),data=exporter.course(this.reactive.state);let titleText=null;if(1==cmIds.length){const cmInfo=this.reactive.get("cm",cmIds[0]);data.cmid=cmInfo.id,data.cmname=cmInfo.name,data.information=await this.reactive.getFormatString("cmmove_info",data.cmname),titleText=cmInfo.hasdelegatedsection?this.reactive.getFormatString("cmmove_subsectiontitle"):this.reactive.getFormatString("cmmove_title")}else data.information=await this.reactive.getFormatString("cmsmove_info",cmIds.length),titleText=this.reactive.getFormatString("cmsmove_title");const modal=await this._modalBodyRenderedPromise(_modal.default,{title:titleText,body:_templates.default.render("core_courseformat/local/content/movecm",data)}),modalBody=(0,_normalise.getFirst)(modal.getBody());cmIds.forEach((cmId=>{const currentElement=modalBody.querySelector("".concat(this.selectors.CMLINK,"[data-id='").concat(cmId,"']"));this._disableLink(currentElement)})),new _contenttree.default(modalBody.querySelector(this.selectors.CONTENTTREE),{SECTION:this.selectors.SECTIONNODE,TOGGLER:this.selectors.MODALTOGGLER,COLLAPSE:this.selectors.MODALTOGGLER,ENTER:this.selectors.SECTIONLINK}),cmIds.forEach((cmId=>{const cmInfo=this.reactive.get("cm",cmId);let selector;selector=cmInfo.hasdelegatedsection?"".concat(this.selectors.SECTIONLINK,"[data-id='").concat(cmInfo.sectionid,"']"):"".concat(this.selectors.CMLINK,"[data-id='").concat(cmId,"']");const currentElement=modalBody.querySelector(selector);this._expandCmMoveModalParentSections(modalBody,currentElement)})),modalBody.addEventListener("click",(event=>{const target=event.target;if(!target.matches("a")||void 0===target.dataset.for||void 0===target.dataset.id)return;if(target.getAttribute("aria-disabled"))return;let targetSectionId,targetCmId;event.preventDefault();let droppedCmIds=[...cmIds];if("cm"==target.dataset.for){const dropData=exporter.cmDraggableData(this.reactive.state,target.dataset.id);targetSectionId=dropData.sectionid,targetCmId=dropData.nextcmid}else{const section=this.reactive.get("section",target.dataset.id);targetSectionId=target.dataset.id,targetCmId=null==section?void 0:section.cmlist[0]}this.reactive.get("section",targetSectionId).component&&(droppedCmIds=droppedCmIds.filter((cmId=>!this.reactive.get("cm",cmId).hasdelegatedsection))),0!==droppedCmIds.length&&(this.reactive.dispatch("cmMove",droppedCmIds,targetSectionId,targetCmId),this._destroyModal(modal,editTools))})),pendingModalReady.resolve()}_expandCmMoveModalParentSections(modalBody,element){var _toggler$data;const sectionnode=element.closest(this.selectors.SECTIONNODE);if(!sectionnode)return;const toggler=(0,_jquery.default)(sectionnode).find(this.selectors.MODALTOGGLER);let collapsibleId=null!==(_toggler$data=toggler.data("target"))&&void 0!==_toggler$data?_toggler$data:toggler.attr("href");if(collapsibleId){collapsibleId=collapsibleId.replace("#","");const expandNode=modalBody.querySelector("#".concat(collapsibleId));(0,_jquery.default)(expandNode).collapse("show")}this._expandCmMoveModalParentSections(modalBody,sectionnode.parentElement)}async _requestAddSection(target,event){var _target$dataset$id;event.preventDefault(),this.reactive.dispatch("addSection",null!==(_target$dataset$id=target.dataset.id)&&void 0!==_target$dataset$id?_target$dataset$id:0)}async _requestAddModule(target,event){event.preventDefault(),this.reactive.dispatch("addModule",target.dataset.modname,target.dataset.sectionnum,target.dataset.beforemod)}async _requestDeleteSection(target,event){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;if(event.preventDefault(),!sectionIds.some((sectionId=>{var _sectionInfo$cmlist;const sectionInfo=this.reactive.get("section",sectionId);return(null!==(_sectionInfo$cmlist=sectionInfo.cmlist)&&void 0!==_sectionInfo$cmlist?_sectionInfo$cmlist:[]).length||sectionInfo.hassummary||sectionInfo.rawtitle})))return void this._dispatchSectionDelete(sectionIds,target);let bodyText=null,titleText=null;if(1==sectionIds.length){titleText=this.reactive.getFormatString("sectiondelete_title");const sectionInfo=this.reactive.get("section",sectionIds[0]);bodyText=this.reactive.getFormatString("sectiondelete_info",{name:sectionInfo.title})}else titleText=this.reactive.getFormatString("sectionsdelete_title"),bodyText=this.reactive.getFormatString("sectionsdelete_info",{count:sectionIds.length});const modal=await this._modalBodyRenderedPromise(_modal_delete_cancel.default,{title:titleText,body:bodyText});modal.getRoot().on(_modal_events.default.delete,(e=>{e.preventDefault(),modal.destroy(),this._dispatchSectionDelete(sectionIds,target)}))}async _dispatchSectionDelete(sectionIds,target){await this.reactive.dispatch("sectionDelete",sectionIds),target.baseURI.includes("section.php")&&(window.location.href=this.reactive.get("course").baseurl)}async _requestToggleSelectionCm(target,event){(0,_bulkselection.toggleBulkSelectionAction)(this.reactive,target,event,"cm")}async _requestToggleSelectionSection(target,event){(0,_bulkselection.toggleBulkSelectionAction)(this.reactive,target,event,"section")}async _requestMutationAction(target,event,mutationName){(target.dataset.id||"bulkaction"===target.dataset.for)&&(event.preventDefault(),"bulkaction"===target.dataset.for?this.reactive.dispatch(mutationName,this.reactive.get("bulk").selection):this.reactive.dispatch(mutationName,[target.dataset.id]))}async _requestCmDuplicate(target,event){var _target$dataset$secti;const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;const sectionId=null!==(_target$dataset$secti=target.dataset.sectionid)&&void 0!==_target$dataset$secti?_target$dataset$secti:null;event.preventDefault(),this.reactive.dispatch("cmDuplicate",cmIds,sectionId)}async _requestCmDelete(target,event){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;event.preventDefault();let bodyText=null,titleText=null,delegatedsection=null;if(1==cmIds.length){const cmInfo=this.reactive.get("cm",cmIds[0]);cmInfo.hasdelegatedsection?(delegatedsection=cmInfo.delegatesectionid,titleText=this.reactive.getFormatString("cmdelete_subsectiontitle"),bodyText=(0,_str.getString)("sectiondelete_info","core_courseformat",{type:cmInfo.modname,name:cmInfo.name})):(titleText=this.reactive.getFormatString("cmdelete_title"),bodyText=(0,_str.getString)("cmdelete_info","core_courseformat",{type:cmInfo.modname,name:cmInfo.name}))}else titleText=(0,_str.getString)("cmsdelete_title","core_courseformat"),bodyText=(0,_str.getString)("cmsdelete_info","core_courseformat",{count:cmIds.length});const modal=await this._modalBodyRenderedPromise(_modal_delete_cancel.default,{title:titleText,body:bodyText});modal.getRoot().on(_modal_events.default.delete,(e=>{if(e.preventDefault(),modal.destroy(),this.reactive.dispatch("cmDelete",cmIds),1==cmIds.length&&delegatedsection&&target.baseURI.includes("section.php")){let parameters=new URLSearchParams(window.location.search);parameters.has("id")&¶meters.get("id")==delegatedsection&&this._dispatchSectionDelete([delegatedsection],target)}}))}async _requestCmAvailability(target){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;const data={allowstealth:this.reactive.getExporter().canUseStealth(this.reactive.state,cmIds)},modal=await this._modalBodyRenderedPromise(_modal_save_cancel.default,{title:(0,_str.getString)("availability","core"),body:_templates.default.render("core_courseformat/local/content/cm/availabilitymodal",data),saveButtonText:(0,_str.getString)("apply","core")});this._setupMutationRadioButtonModal(modal,cmIds)}async _requestSectionAvailability(target){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;const title=1==sectionIds.length?"sectionavailability_title":"sectionsavailability_title",modal=await this._modalBodyRenderedPromise(_modal_save_cancel.default,{title:this.reactive.getFormatString(title),body:_templates.default.render("core_courseformat/local/content/section/availabilitymodal",[]),saveButtonText:(0,_str.getString)("apply","core")});this._setupMutationRadioButtonModal(modal,sectionIds)}_setupMutationRadioButtonModal(modal,ids){modal.setButtonDisabled("save",!0);const submitFunction=radio=>{const mutation=null==radio?void 0:radio.value;return!!mutation&&(this.reactive.dispatch(mutation,ids),!0)},modalBody=(0,_normalise.getFirst)(modal.getBody());modalBody.querySelectorAll(this.selectors.OPTIONSRADIO).forEach((radio=>{radio.addEventListener("change",(()=>{modal.setButtonDisabled("save",!1)})),radio.parentNode.addEventListener("click",(()=>{radio.checked=!0,modal.setButtonDisabled("save",!1)})),radio.parentNode.addEventListener("dblclick",(dbClickEvent=>{submitFunction(radio)&&(dbClickEvent.preventDefault(),modal.destroy())}))})),modal.getRoot().on(_modal_events.default.save,(()=>{const radio=modalBody.querySelector("".concat(this.selectors.OPTIONSRADIO,":checked"));submitFunction(radio)}))}_setAddSectionLocked(locked){this.getElements(this.selectors.ADDSECTIONREGION).forEach((element=>{element.classList.toggle(this.classes.DISABLED,locked);const addSectionElement=element.querySelector(this.selectors.ADDSECTION);addSectionElement.classList.toggle(this.classes.DISABLED,locked),this.setElementLocked(addSectionElement,locked),locked?((0,_str.getString)("sectionaddmax","core_courseformat").then((text=>addSectionElement.setAttribute("title",text))).catch(_notification.default.exception),addSectionElement.style.pointerEvents=null,addSectionElement.style.userSelect=null):addSectionElement.setAttribute("title",addSectionElement.dataset.addSections)}));const courseAddSection=this.getElement(this.selectors.COURSEADDSECTION);if(courseAddSection){courseAddSection.querySelector(this.selectors.ADDSECTION).classList.toggle(this.classes.DISPLAYNONE,locked);courseAddSection.querySelector(this.selectors.MAXSECTIONSWARNING).classList.toggle(this.classes.DISPLAYNONE,!locked)}}_disableLink(element){element&&(element.style.pointerEvents="none",element.style.userSelect="none",element.classList.add(this.classes.DISABLED),element.classList.add(this.classes.ITALIC),element.setAttribute("aria-disabled",!0),element.addEventListener("click",(event=>event.preventDefault())))}_modalBodyRenderedPromise(ModalClass,modalParams){return new Promise(((resolve,reject)=>{ModalClass.create(modalParams).then((modal=>{modal.setRemoveOnClose(!0),modal.getRoot().on(_modal_events.default.bodyRendered,(()=>{resolve(modal)})),void 0!==modalParams.saveButtonText&&modal.setSaveButtonText(modalParams.saveButtonText),void 0!==modalParams.deleteButtonText&&modal.setDeleteButtonText(modalParams.saveButtonText),modal.show()})).catch((()=>{reject("Cannot load modal content")}))}))}_destroyModal(modal,element){modal.hide();const pendingDestroy=new _pending.default("courseformat/actions:destroyModal");element&&element.focus(),setTimeout((()=>{modal.destroy(),pendingDestroy.resolve()}),500)}_getClosestActionMenuToogler(element){const actionMenu=element.closest(this.selectors.ACTIONMENU);if(actionMenu)return actionMenu.querySelector(this.selectors.ACTIONMENUTOGGLER)}}return _exports.default=_default,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_collapse=_interopRequireDefault(_collapse),_modal=_interopRequireDefault(_modal),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_modal_delete_cancel=_interopRequireDefault(_modal_delete_cancel),_modal_events=_interopRequireDefault(_modal_events),_templates=_interopRequireDefault(_templates),CourseEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CourseEvents),_pending=_interopRequireDefault(_pending),_contenttree=_interopRequireDefault(_contenttree),_notification=_interopRequireDefault(_notification),(0,_prefetch.prefetchStrings)("core",["movecoursesection","movecoursemodule","confirm","delete"]);const directMutations={sectionHide:"sectionHide",sectionShow:"sectionShow",cmHide:"cmHide",cmShow:"cmShow",cmStealth:"cmStealth",cmMoveRight:"cmMoveRight",cmMoveLeft:"cmMoveLeft",cmNoGroups:"cmNoGroups",cmSeparateGroups:"cmSeparateGroups",cmVisibleGroups:"cmVisibleGroups"};class _default extends _reactive.BaseComponent{create(){this.name="content_actions",this.selectors={ACTIONLINK:"[data-action]",SECTIONLINK:"[data-for='section']",CMLINK:"[data-for='cm']",SECTIONNODE:"[data-for='sectionnode']",MODALTOGGLER:"[data-bs-toggle='collapse']",ADDSECTION:"[data-action='addSection']",CONTENTTREE:"#destination-selector",ACTIONMENU:".action-menu",ACTIONMENUTOGGLER:'[data-bs-toggle="dropdown"]',OPTIONSRADIO:"[type='radio']",COURSEADDSECTION:"#course-addsection",MAXSECTIONSWARNING:"[data-region='max-sections-warning']",ADDSECTIONREGION:"[data-region='section-addsection']"},this.classes={DISABLED:"disabled",ITALIC:"fst-italic",DISPLAYNONE:"d-none"}}static addActions(actions){for(const[action,mutationReference]of Object.entries(actions)){if("function"!=typeof mutationReference&&"string"!=typeof mutationReference)throw new Error("".concat(action," action must be a mutation name or a function"));directMutations[action]=mutationReference}}stateReady(state){this.addEventListener(this.element,"click",this._dispatchClick),this._checkSectionlist({state:state}),this.addEventListener(this.element,CourseEvents.sectionRefreshed,(()=>this._checkSectionlist({state:state})))}getWatchers(){return[{watch:"course.sectionlist:updated",handler:this._checkSectionlist}]}_dispatchClick(event){const target=event.target.closest(this.selectors.ACTIONLINK);if(!target)return;if(target.classList.contains(this.classes.DISABLED))return void event.preventDefault();const actionName=target.dataset.action,methodName=this._actionMethodName(actionName);if(void 0===this[methodName])return void 0!==directMutations[actionName]?"function"==typeof directMutations[actionName]?void directMutations[actionName](target,event):void this._requestMutationAction(target,event,directMutations[actionName]):void 0;this[methodName](target,event)}_actionMethodName(name){const requestName=name.charAt(0).toUpperCase()+name.slice(1);return"_request".concat(requestName)}_checkSectionlist(_ref){let{state:state}=_ref;this._setAddSectionLocked(state.course.sectionlist.length>state.course.maxsections)}_getTargetIds(target){var _target$dataset,_target$dataset2;let ids=[];null!=target&&null!==(_target$dataset=target.dataset)&&void 0!==_target$dataset&&_target$dataset.id&&ids.push(target.dataset.id);const bulkType=null==target||null===(_target$dataset2=target.dataset)||void 0===_target$dataset2?void 0:_target$dataset2.bulk;if(!bulkType)return ids;const bulk=this.reactive.get("bulk");return bulk.enabled&&bulk.selectedType===bulkType&&(ids=[...ids,...bulk.selection]),ids}async _requestMoveSection(target,event){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;event.preventDefault();const pendingModalReady=new _pending.default("courseformat/actions:prepareMoveSectionModal"),editTools=this._getClosestActionMenuToogler(target),data=this.reactive.getExporter().course(this.reactive.state);let titleText=null,sectionInfo=null;1==sectionIds.length?(sectionInfo=this.reactive.get("section",sectionIds[0]),data.sectionid=sectionInfo.id,data.sectiontitle=sectionInfo.title,data.information=await this.reactive.getFormatString("sectionmove_info",data.sectiontitle),titleText=this.reactive.getFormatString("sectionmove_title")):(data.information=await this.reactive.getFormatString("sectionsmove_info",sectionIds.length),titleText=this.reactive.getFormatString("sectionsmove_title"));const modal=await this._modalBodyRenderedPromise(_modal.default,{title:titleText,body:_templates.default.render("core_courseformat/local/content/movesection",data)}),modalBody=(0,_normalise.getFirst)(modal.getBody());sectionIds.forEach((sectionId=>{const currentElement=modalBody.querySelector("".concat(this.selectors.SECTIONLINK,"[data-id='").concat(sectionId,"']"));this._disableLink(currentElement)})),new _contenttree.default(modalBody.querySelector(this.selectors.CONTENTTREE),{SECTION:this.selectors.SECTIONNODE,TOGGLER:this.selectors.MODALTOGGLER,COLLAPSE:this.selectors.MODALTOGGLER},!0),modalBody.addEventListener("click",(event=>{const target=event.target;target.matches("a")&&"section"==target.dataset.for&&void 0!==target.dataset.id&&(target.getAttribute("aria-disabled")||(event.preventDefault(),this.reactive.dispatch("sectionMoveAfter",sectionIds,target.dataset.id),this._destroyModal(modal,editTools)))})),pendingModalReady.resolve()}async _requestMoveCm(target,event){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;event.preventDefault();const pendingModalReady=new _pending.default("courseformat/actions:prepareMoveCmModal"),editTools=this._getClosestActionMenuToogler(target),exporter=this.reactive.getExporter(),data=exporter.course(this.reactive.state);let titleText=null;if(1==cmIds.length){const cmInfo=this.reactive.get("cm",cmIds[0]);data.cmid=cmInfo.id,data.cmname=cmInfo.name,data.information=await this.reactive.getFormatString("cmmove_info",data.cmname),titleText=cmInfo.hasdelegatedsection?this.reactive.getFormatString("cmmove_subsectiontitle"):this.reactive.getFormatString("cmmove_title")}else data.information=await this.reactive.getFormatString("cmsmove_info",cmIds.length),titleText=this.reactive.getFormatString("cmsmove_title");const modal=await this._modalBodyRenderedPromise(_modal.default,{title:titleText,body:_templates.default.render("core_courseformat/local/content/movecm",data)}),modalBody=(0,_normalise.getFirst)(modal.getBody());cmIds.forEach((cmId=>{const currentElement=modalBody.querySelector("".concat(this.selectors.CMLINK,"[data-id='").concat(cmId,"']"));this._disableLink(currentElement)})),new _contenttree.default(modalBody.querySelector(this.selectors.CONTENTTREE),{SECTION:this.selectors.SECTIONNODE,TOGGLER:this.selectors.MODALTOGGLER,COLLAPSE:this.selectors.MODALTOGGLER,ENTER:this.selectors.SECTIONLINK}),cmIds.forEach((cmId=>{const cmInfo=this.reactive.get("cm",cmId);let selector;selector=cmInfo.hasdelegatedsection?"".concat(this.selectors.SECTIONLINK,"[data-id='").concat(cmInfo.sectionid,"']"):"".concat(this.selectors.CMLINK,"[data-id='").concat(cmId,"']");const currentElement=modalBody.querySelector(selector);this._expandCmMoveModalParentSections(modalBody,currentElement)})),modalBody.addEventListener("click",(event=>{const target=event.target;if(!target.matches("a")||void 0===target.dataset.for||void 0===target.dataset.id)return;if(target.getAttribute("aria-disabled"))return;let targetSectionId,targetCmId;event.preventDefault();let droppedCmIds=[...cmIds];if("cm"==target.dataset.for){const dropData=exporter.cmDraggableData(this.reactive.state,target.dataset.id);targetSectionId=dropData.sectionid,targetCmId=dropData.nextcmid}else{const section=this.reactive.get("section",target.dataset.id);targetSectionId=target.dataset.id,targetCmId=null==section?void 0:section.cmlist[0]}this.reactive.get("section",targetSectionId).component&&(droppedCmIds=droppedCmIds.filter((cmId=>!this.reactive.get("cm",cmId).hasdelegatedsection))),0!==droppedCmIds.length&&(this.reactive.dispatch("cmMove",droppedCmIds,targetSectionId,targetCmId),this._destroyModal(modal,editTools))})),pendingModalReady.resolve()}_expandCmMoveModalParentSections(modalBody,element){var _toggler$dataset$targ;const sectionnode=element.closest(this.selectors.SECTIONNODE);if(!sectionnode)return;const toggler=sectionnode.querySelector(this.selectors.MODALTOGGLER);let collapsibleId=null!==(_toggler$dataset$targ=toggler.dataset.target)&&void 0!==_toggler$dataset$targ?_toggler$dataset$targ:toggler.getAttribute("href");if(collapsibleId){collapsibleId=collapsibleId.replace("#","");const expandNode=modalBody.querySelector("#".concat(collapsibleId));new _collapse.default(expandNode,{toggle:!1}).show()}this._expandCmMoveModalParentSections(modalBody,sectionnode.parentElement)}async _requestAddSection(target,event){var _target$dataset$id;event.preventDefault(),this.reactive.dispatch("addSection",null!==(_target$dataset$id=target.dataset.id)&&void 0!==_target$dataset$id?_target$dataset$id:0)}async _requestAddModule(target,event){event.preventDefault(),this.reactive.dispatch("addModule",target.dataset.modname,target.dataset.sectionnum,target.dataset.beforemod)}async _requestDeleteSection(target,event){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;if(event.preventDefault(),!sectionIds.some((sectionId=>{var _sectionInfo$cmlist;const sectionInfo=this.reactive.get("section",sectionId);return(null!==(_sectionInfo$cmlist=sectionInfo.cmlist)&&void 0!==_sectionInfo$cmlist?_sectionInfo$cmlist:[]).length||sectionInfo.hassummary||sectionInfo.rawtitle})))return void this._dispatchSectionDelete(sectionIds,target);let bodyText=null,titleText=null;if(1==sectionIds.length){titleText=this.reactive.getFormatString("sectiondelete_title");const sectionInfo=this.reactive.get("section",sectionIds[0]);bodyText=this.reactive.getFormatString("sectiondelete_info",{name:sectionInfo.title})}else titleText=this.reactive.getFormatString("sectionsdelete_title"),bodyText=this.reactive.getFormatString("sectionsdelete_info",{count:sectionIds.length});const modal=await this._modalBodyRenderedPromise(_modal_delete_cancel.default,{title:titleText,body:bodyText});modal.getRoot().on(_modal_events.default.delete,(e=>{e.preventDefault(),modal.destroy(),this._dispatchSectionDelete(sectionIds,target)}))}async _dispatchSectionDelete(sectionIds,target){await this.reactive.dispatch("sectionDelete",sectionIds),target.baseURI.includes("section.php")&&(window.location.href=this.reactive.get("course").baseurl)}async _requestToggleSelectionCm(target,event){(0,_bulkselection.toggleBulkSelectionAction)(this.reactive,target,event,"cm")}async _requestToggleSelectionSection(target,event){(0,_bulkselection.toggleBulkSelectionAction)(this.reactive,target,event,"section")}async _requestMutationAction(target,event,mutationName){(target.dataset.id||"bulkaction"===target.dataset.for)&&(event.preventDefault(),"bulkaction"===target.dataset.for?this.reactive.dispatch(mutationName,this.reactive.get("bulk").selection):this.reactive.dispatch(mutationName,[target.dataset.id]))}async _requestCmDuplicate(target,event){var _target$dataset$secti;const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;const sectionId=null!==(_target$dataset$secti=target.dataset.sectionid)&&void 0!==_target$dataset$secti?_target$dataset$secti:null;event.preventDefault(),this.reactive.dispatch("cmDuplicate",cmIds,sectionId)}async _requestCmDelete(target,event){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;event.preventDefault();let bodyText=null,titleText=null,delegatedsection=null;if(1==cmIds.length){const cmInfo=this.reactive.get("cm",cmIds[0]);cmInfo.hasdelegatedsection?(delegatedsection=cmInfo.delegatesectionid,titleText=this.reactive.getFormatString("cmdelete_subsectiontitle"),bodyText=(0,_str.getString)("sectiondelete_info","core_courseformat",{type:cmInfo.modname,name:cmInfo.name})):(titleText=this.reactive.getFormatString("cmdelete_title"),bodyText=(0,_str.getString)("cmdelete_info","core_courseformat",{type:cmInfo.modname,name:cmInfo.name}))}else titleText=(0,_str.getString)("cmsdelete_title","core_courseformat"),bodyText=(0,_str.getString)("cmsdelete_info","core_courseformat",{count:cmIds.length});const modal=await this._modalBodyRenderedPromise(_modal_delete_cancel.default,{title:titleText,body:bodyText});modal.getRoot().on(_modal_events.default.delete,(e=>{if(e.preventDefault(),modal.destroy(),this.reactive.dispatch("cmDelete",cmIds),1==cmIds.length&&delegatedsection&&target.baseURI.includes("section.php")){let parameters=new URLSearchParams(window.location.search);parameters.has("id")&¶meters.get("id")==delegatedsection&&this._dispatchSectionDelete([delegatedsection],target)}}))}async _requestCmAvailability(target){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;const data={allowstealth:this.reactive.getExporter().canUseStealth(this.reactive.state,cmIds)},modal=await this._modalBodyRenderedPromise(_modal_save_cancel.default,{title:(0,_str.getString)("availability","core"),body:_templates.default.render("core_courseformat/local/content/cm/availabilitymodal",data),saveButtonText:(0,_str.getString)("apply","core")});this._setupMutationRadioButtonModal(modal,cmIds)}async _requestSectionAvailability(target){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;const title=1==sectionIds.length?"sectionavailability_title":"sectionsavailability_title",modal=await this._modalBodyRenderedPromise(_modal_save_cancel.default,{title:this.reactive.getFormatString(title),body:_templates.default.render("core_courseformat/local/content/section/availabilitymodal",[]),saveButtonText:(0,_str.getString)("apply","core")});this._setupMutationRadioButtonModal(modal,sectionIds)}_setupMutationRadioButtonModal(modal,ids){modal.setButtonDisabled("save",!0);const submitFunction=radio=>{const mutation=null==radio?void 0:radio.value;return!!mutation&&(this.reactive.dispatch(mutation,ids),!0)},modalBody=(0,_normalise.getFirst)(modal.getBody());modalBody.querySelectorAll(this.selectors.OPTIONSRADIO).forEach((radio=>{radio.addEventListener("change",(()=>{modal.setButtonDisabled("save",!1)})),radio.parentNode.addEventListener("click",(()=>{radio.checked=!0,modal.setButtonDisabled("save",!1)})),radio.parentNode.addEventListener("dblclick",(dbClickEvent=>{submitFunction(radio)&&(dbClickEvent.preventDefault(),modal.destroy())}))})),modal.getRoot().on(_modal_events.default.save,(()=>{const radio=modalBody.querySelector("".concat(this.selectors.OPTIONSRADIO,":checked"));submitFunction(radio)}))}_setAddSectionLocked(locked){this.getElements(this.selectors.ADDSECTIONREGION).forEach((element=>{element.classList.toggle(this.classes.DISABLED,locked);const addSectionElement=element.querySelector(this.selectors.ADDSECTION);addSectionElement.classList.toggle(this.classes.DISABLED,locked),this.setElementLocked(addSectionElement,locked),locked?((0,_str.getString)("sectionaddmax","core_courseformat").then((text=>addSectionElement.setAttribute("title",text))).catch(_notification.default.exception),addSectionElement.style.pointerEvents=null,addSectionElement.style.userSelect=null):addSectionElement.setAttribute("title",addSectionElement.dataset.addSections)}));const courseAddSection=this.getElement(this.selectors.COURSEADDSECTION);if(courseAddSection){courseAddSection.querySelector(this.selectors.ADDSECTION).classList.toggle(this.classes.DISPLAYNONE,locked);courseAddSection.querySelector(this.selectors.MAXSECTIONSWARNING).classList.toggle(this.classes.DISPLAYNONE,!locked)}}_disableLink(element){element&&(element.style.pointerEvents="none",element.style.userSelect="none",element.classList.add(this.classes.DISABLED),element.classList.add(this.classes.ITALIC),element.setAttribute("aria-disabled",!0),element.addEventListener("click",(event=>event.preventDefault())))}_modalBodyRenderedPromise(ModalClass,modalParams){return new Promise(((resolve,reject)=>{ModalClass.create(modalParams).then((modal=>{modal.setRemoveOnClose(!0),modal.getRoot().on(_modal_events.default.bodyRendered,(()=>{resolve(modal)})),void 0!==modalParams.saveButtonText&&modal.setSaveButtonText(modalParams.saveButtonText),void 0!==modalParams.deleteButtonText&&modal.setDeleteButtonText(modalParams.saveButtonText),modal.show()})).catch((()=>{reject("Cannot load modal content")}))}))}_destroyModal(modal,element){modal.hide();const pendingDestroy=new _pending.default("courseformat/actions:destroyModal");element&&element.focus(),setTimeout((()=>{modal.destroy(),pendingDestroy.resolve()}),500)}_getClosestActionMenuToogler(element){const actionMenu=element.closest(this.selectors.ACTIONMENU);if(actionMenu)return actionMenu.querySelector(this.selectors.ACTIONMENUTOGGLER)}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=actions.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/content/actions.min.js.map b/course/format/amd/build/local/content/actions.min.js.map index eadf751328b4d..3428bd27c89bc 100644 --- a/course/format/amd/build/local/content/actions.min.js.map +++ b/course/format/amd/build/local/content/actions.min.js.map @@ -1 +1 @@ -{"version":3,"file":"actions.min.js","sources":["../../../src/local/content/actions.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 * Course state actions dispatcher.\n *\n * This module captures all data-dispatch links in the course content and dispatch the proper\n * state mutation, including any confirmation and modal required.\n *\n * @module core_courseformat/local/content/actions\n * @class core_courseformat/local/content/actions\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent} from 'core/reactive';\nimport Modal from 'core/modal';\nimport ModalSaveCancel from 'core/modal_save_cancel';\nimport ModalDeleteCancel from 'core/modal_delete_cancel';\nimport ModalEvents from 'core/modal_events';\nimport Templates from 'core/templates';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport {getFirst} from 'core/normalise';\nimport {toggleBulkSelectionAction} from 'core_courseformat/local/content/actions/bulkselection';\nimport * as CourseEvents from 'core_course/events';\nimport Pending from 'core/pending';\nimport ContentTree from 'core_courseformat/local/courseeditor/contenttree';\n// The jQuery module is only used for interacting with Boostrap 4. It can we removed when MDL-71979 is integrated.\nimport jQuery from 'jquery';\nimport Notification from \"core/notification\";\n\n// Load global strings.\nprefetchStrings('core', ['movecoursesection', 'movecoursemodule', 'confirm', 'delete']);\n\n// Mutations are dispatched by the course content actions.\n// Formats can use this module addActions static method to add custom actions.\n// Direct mutations can be simple strings (mutation) name or functions.\nconst directMutations = {\n sectionHide: 'sectionHide',\n sectionShow: 'sectionShow',\n cmHide: 'cmHide',\n cmShow: 'cmShow',\n cmStealth: 'cmStealth',\n cmMoveRight: 'cmMoveRight',\n cmMoveLeft: 'cmMoveLeft',\n cmNoGroups: 'cmNoGroups',\n cmSeparateGroups: 'cmSeparateGroups',\n cmVisibleGroups: 'cmVisibleGroups',\n};\n\nexport default class extends BaseComponent {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'content_actions';\n // Default query selectors.\n this.selectors = {\n ACTIONLINK: `[data-action]`,\n // Move modal selectors.\n SECTIONLINK: `[data-for='section']`,\n CMLINK: `[data-for='cm']`,\n SECTIONNODE: `[data-for='sectionnode']`,\n MODALTOGGLER: `[data-bs-toggle='collapse']`,\n ADDSECTION: `[data-action='addSection']`,\n CONTENTTREE: `#destination-selector`,\n ACTIONMENU: `.action-menu`,\n ACTIONMENUTOGGLER: `[data-bs-toggle=\"dropdown\"]`,\n // Availability modal selectors.\n OPTIONSRADIO: `[type='radio']`,\n COURSEADDSECTION: `#course-addsection`,\n MAXSECTIONSWARNING: `[data-region='max-sections-warning']`,\n ADDSECTIONREGION: `[data-region='section-addsection']`,\n };\n // Component css classes.\n this.classes = {\n DISABLED: `disabled`,\n ITALIC: `fst-italic`,\n DISPLAYNONE: `d-none`,\n };\n }\n\n /**\n * Add extra actions to the module.\n *\n * @param {array} actions array of methods to execute\n */\n static addActions(actions) {\n for (const [action, mutationReference] of Object.entries(actions)) {\n if (typeof mutationReference !== 'function' && typeof mutationReference !== 'string') {\n throw new Error(`${action} action must be a mutation name or a function`);\n }\n directMutations[action] = mutationReference;\n }\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the state data.\n *\n */\n stateReady(state) {\n // Delegate dispatch clicks.\n this.addEventListener(\n this.element,\n 'click',\n this._dispatchClick\n );\n // Check section limit.\n this._checkSectionlist({state});\n // Add an Event listener to recalculate limits it if a section HTML is altered.\n this.addEventListener(\n this.element,\n CourseEvents.sectionRefreshed,\n () => this._checkSectionlist({state})\n );\n }\n\n /**\n * Return the component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n return [\n // Check section limit.\n {watch: `course.sectionlist:updated`, handler: this._checkSectionlist},\n ];\n }\n\n _dispatchClick(event) {\n const target = event.target.closest(this.selectors.ACTIONLINK);\n if (!target) {\n return;\n }\n if (target.classList.contains(this.classes.DISABLED)) {\n event.preventDefault();\n return;\n }\n\n // Invoke proper method.\n const actionName = target.dataset.action;\n const methodName = this._actionMethodName(actionName);\n\n if (this[methodName] !== undefined) {\n this[methodName](target, event);\n return;\n }\n\n // Check direct mutations or mutations handlers.\n if (directMutations[actionName] !== undefined) {\n if (typeof directMutations[actionName] === 'function') {\n directMutations[actionName](target, event);\n return;\n }\n this._requestMutationAction(target, event, directMutations[actionName]);\n return;\n }\n }\n\n _actionMethodName(name) {\n const requestName = name.charAt(0).toUpperCase() + name.slice(1);\n return `_request${requestName}`;\n }\n\n /**\n * Check the section list and disable some options if needed.\n *\n * @param {Object} detail the update details.\n * @param {Object} detail.state the state object.\n */\n _checkSectionlist({state}) {\n // Disable \"add section\" actions if the course max sections has been exceeded.\n this._setAddSectionLocked(state.course.sectionlist.length > state.course.maxsections);\n }\n\n /**\n * Return the ids represented by this element.\n *\n * Depending on the dataset attributes the action could represent a single id\n * or a bulk actions with all the current selected ids.\n *\n * @param {HTMLElement} target\n * @returns {Number[]} array of Ids\n */\n _getTargetIds(target) {\n let ids = [];\n if (target?.dataset?.id) {\n ids.push(target.dataset.id);\n }\n const bulkType = target?.dataset?.bulk;\n if (!bulkType) {\n return ids;\n }\n const bulk = this.reactive.get('bulk');\n if (bulk.enabled && bulk.selectedType === bulkType) {\n ids = [...ids, ...bulk.selection];\n }\n return ids;\n }\n\n /**\n * Handle a move section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestMoveSection(target, event) {\n // Check we have an id.\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n const pendingModalReady = new Pending(`courseformat/actions:prepareMoveSectionModal`);\n\n // The section edit menu to refocus on end.\n const editTools = this._getClosestActionMenuToogler(target);\n\n // Collect section information from the state.\n const exporter = this.reactive.getExporter();\n const data = exporter.course(this.reactive.state);\n let titleText = null;\n\n // Add the target section id and title.\n let sectionInfo = null;\n if (sectionIds.length == 1) {\n sectionInfo = this.reactive.get('section', sectionIds[0]);\n data.sectionid = sectionInfo.id;\n data.sectiontitle = sectionInfo.title;\n data.information = await this.reactive.getFormatString('sectionmove_info', data.sectiontitle);\n titleText = this.reactive.getFormatString('sectionmove_title');\n } else {\n data.information = await this.reactive.getFormatString('sectionsmove_info', sectionIds.length);\n titleText = this.reactive.getFormatString('sectionsmove_title');\n }\n\n\n // Create the modal.\n // Build the modal parameters from the event data.\n const modal = await this._modalBodyRenderedPromise(Modal, {\n title: titleText,\n body: Templates.render('core_courseformat/local/content/movesection', data),\n });\n\n const modalBody = getFirst(modal.getBody());\n\n // Disable current selected section ids.\n sectionIds.forEach(sectionId => {\n const currentElement = modalBody.querySelector(`${this.selectors.SECTIONLINK}[data-id='${sectionId}']`);\n this._disableLink(currentElement);\n });\n\n // Setup keyboard navigation.\n new ContentTree(\n modalBody.querySelector(this.selectors.CONTENTTREE),\n {\n SECTION: this.selectors.SECTIONNODE,\n TOGGLER: this.selectors.MODALTOGGLER,\n COLLAPSE: this.selectors.MODALTOGGLER,\n },\n true\n );\n\n // Capture click.\n modalBody.addEventListener('click', (event) => {\n const target = event.target;\n if (!target.matches('a') || target.dataset.for != 'section' || target.dataset.id === undefined) {\n return;\n }\n if (target.getAttribute('aria-disabled')) {\n return;\n }\n event.preventDefault();\n this.reactive.dispatch('sectionMoveAfter', sectionIds, target.dataset.id);\n this._destroyModal(modal, editTools);\n });\n\n pendingModalReady.resolve();\n }\n\n /**\n * Handle a move cm request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestMoveCm(target, event) {\n // Check we have an id.\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n const pendingModalReady = new Pending(`courseformat/actions:prepareMoveCmModal`);\n\n // The section edit menu to refocus on end.\n const editTools = this._getClosestActionMenuToogler(target);\n\n // Collect information from the state.\n const exporter = this.reactive.getExporter();\n const data = exporter.course(this.reactive.state);\n\n let titleText = null;\n if (cmIds.length == 1) {\n const cmInfo = this.reactive.get('cm', cmIds[0]);\n data.cmid = cmInfo.id;\n data.cmname = cmInfo.name;\n data.information = await this.reactive.getFormatString('cmmove_info', data.cmname);\n if (cmInfo.hasdelegatedsection) {\n titleText = this.reactive.getFormatString('cmmove_subsectiontitle');\n } else {\n titleText = this.reactive.getFormatString('cmmove_title');\n }\n } else {\n data.information = await this.reactive.getFormatString('cmsmove_info', cmIds.length);\n titleText = this.reactive.getFormatString('cmsmove_title');\n }\n\n // Create the modal.\n // Build the modal parameters from the event data.\n const modal = await this._modalBodyRenderedPromise(Modal, {\n title: titleText,\n body: Templates.render('core_courseformat/local/content/movecm', data),\n });\n\n const modalBody = getFirst(modal.getBody());\n\n // Disable current selected section ids.\n cmIds.forEach(cmId => {\n const currentElement = modalBody.querySelector(`${this.selectors.CMLINK}[data-id='${cmId}']`);\n this._disableLink(currentElement);\n });\n\n // Setup keyboard navigation.\n new ContentTree(\n modalBody.querySelector(this.selectors.CONTENTTREE),\n {\n SECTION: this.selectors.SECTIONNODE,\n TOGGLER: this.selectors.MODALTOGGLER,\n COLLAPSE: this.selectors.MODALTOGGLER,\n ENTER: this.selectors.SECTIONLINK,\n }\n );\n\n // Open the cm section node if possible (Bootstrap 4 uses jQuery to interact with collapsibles).\n // All jQuery in this code can be replaced when MDL-71979 is integrated.\n cmIds.forEach(cmId => {\n const cmInfo = this.reactive.get('cm', cmId);\n let selector;\n if (!cmInfo.hasdelegatedsection) {\n selector = `${this.selectors.CMLINK}[data-id='${cmId}']`;\n } else {\n selector = `${this.selectors.SECTIONLINK}[data-id='${cmInfo.sectionid}']`;\n }\n const currentElement = modalBody.querySelector(selector);\n this._expandCmMoveModalParentSections(modalBody, currentElement);\n });\n\n modalBody.addEventListener('click', (event) => {\n const target = event.target;\n if (!target.matches('a') || target.dataset.for === undefined || target.dataset.id === undefined) {\n return;\n }\n if (target.getAttribute('aria-disabled')) {\n return;\n }\n event.preventDefault();\n\n let targetSectionId;\n let targetCmId;\n let droppedCmIds = [...cmIds];\n if (target.dataset.for == 'cm') {\n const dropData = exporter.cmDraggableData(this.reactive.state, target.dataset.id);\n targetSectionId = dropData.sectionid;\n targetCmId = dropData.nextcmid;\n } else {\n const section = this.reactive.get('section', target.dataset.id);\n targetSectionId = target.dataset.id;\n targetCmId = section?.cmlist[0];\n }\n const section = this.reactive.get('section', targetSectionId);\n if (section.component) {\n // Remove cmIds which are not allowed to be moved to this delegated section (mostly\n // all other delegated cm).\n droppedCmIds = droppedCmIds.filter(cmId => {\n const cmInfo = this.reactive.get('cm', cmId);\n return !cmInfo.hasdelegatedsection;\n });\n }\n if (droppedCmIds.length === 0) {\n return; // No cm to move.\n }\n this.reactive.dispatch('cmMove', droppedCmIds, targetSectionId, targetCmId);\n this._destroyModal(modal, editTools);\n });\n\n pendingModalReady.resolve();\n }\n\n /**\n * Expand all the modal tree branches that contains the element.\n *\n * Bootstrap 4 uses jQuery to interact with collapsibles.\n * All jQuery in this code can be replaced when MDL-71979 is integrated.\n *\n * @private\n * @param {HTMLElement} modalBody the modal body element\n * @param {HTMLElement} element the element to display\n */\n _expandCmMoveModalParentSections(modalBody, element) {\n const sectionnode = element.closest(this.selectors.SECTIONNODE);\n if (!sectionnode) {\n return;\n }\n\n const toggler = jQuery(sectionnode).find(this.selectors.MODALTOGGLER);\n let collapsibleId = toggler.data('target') ?? toggler.attr('href');\n if (collapsibleId) {\n // We cannot be sure we have # in the id element name.\n collapsibleId = collapsibleId.replace('#', '');\n const expandNode = modalBody.querySelector(`#${collapsibleId}`);\n jQuery(expandNode).collapse('show');\n }\n\n // Section are a tree structure, we need to expand all the parents.\n this._expandCmMoveModalParentSections(modalBody, sectionnode.parentElement);\n }\n\n /**\n * Handle a create section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestAddSection(target, event) {\n event.preventDefault();\n this.reactive.dispatch('addSection', target.dataset.id ?? 0);\n }\n\n /**\n * Handle a create subsection request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestAddModule(target, event) {\n event.preventDefault();\n this.reactive.dispatch('addModule', target.dataset.modname, target.dataset.sectionnum, target.dataset.beforemod);\n }\n\n /**\n * Handle a delete section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestDeleteSection(target, event) {\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n // We don't need confirmation to delete empty sections.\n let needsConfirmation = sectionIds.some(sectionId => {\n const sectionInfo = this.reactive.get('section', sectionId);\n const cmList = sectionInfo.cmlist ?? [];\n return (cmList.length || sectionInfo.hassummary || sectionInfo.rawtitle);\n });\n if (!needsConfirmation) {\n this._dispatchSectionDelete(sectionIds, target);\n return;\n }\n\n let bodyText = null;\n let titleText = null;\n if (sectionIds.length == 1) {\n titleText = this.reactive.getFormatString('sectiondelete_title');\n const sectionInfo = this.reactive.get('section', sectionIds[0]);\n bodyText = this.reactive.getFormatString('sectiondelete_info', {name: sectionInfo.title});\n } else {\n titleText = this.reactive.getFormatString('sectionsdelete_title');\n bodyText = this.reactive.getFormatString('sectionsdelete_info', {count: sectionIds.length});\n }\n\n const modal = await this._modalBodyRenderedPromise(ModalDeleteCancel, {\n title: titleText,\n body: bodyText,\n });\n\n modal.getRoot().on(\n ModalEvents.delete,\n e => {\n // Stop the default save button behaviour which is to close the modal.\n e.preventDefault();\n modal.destroy();\n this._dispatchSectionDelete(sectionIds, target);\n }\n );\n }\n\n /**\n * Dispatch the section delete action and handle the redirection if necessary.\n *\n * @param {Array} sectionIds the IDs of the sections to delete.\n * @param {Element} target the dispatch action element\n */\n async _dispatchSectionDelete(sectionIds, target) {\n await this.reactive.dispatch('sectionDelete', sectionIds);\n if (target.baseURI.includes('section.php')) {\n // Redirect to the course main page if the section is the current page.\n window.location.href = this.reactive.get('course').baseurl;\n }\n }\n\n /**\n * Handle a toggle cm selection.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestToggleSelectionCm(target, event) {\n toggleBulkSelectionAction(this.reactive, target, event, 'cm');\n }\n\n /**\n * Handle a toggle section selection.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestToggleSelectionSection(target, event) {\n toggleBulkSelectionAction(this.reactive, target, event, 'section');\n }\n\n /**\n * Basic mutation action helper.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n * @param {string} mutationName the mutation name\n */\n async _requestMutationAction(target, event, mutationName) {\n if (!target.dataset.id && target.dataset.for !== 'bulkaction') {\n return;\n }\n event.preventDefault();\n if (target.dataset.for === 'bulkaction') {\n // If the mutation is a bulk action we use the current selection.\n this.reactive.dispatch(mutationName, this.reactive.get('bulk').selection);\n } else {\n this.reactive.dispatch(mutationName, [target.dataset.id]);\n }\n }\n\n /**\n * Handle a course module duplicate request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestCmDuplicate(target, event) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n const sectionId = target.dataset.sectionid ?? null;\n event.preventDefault();\n this.reactive.dispatch('cmDuplicate', cmIds, sectionId);\n }\n\n /**\n * Handle a delete cm request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestCmDelete(target, event) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n let bodyText = null;\n let titleText = null;\n let delegatedsection = null;\n if (cmIds.length == 1) {\n const cmInfo = this.reactive.get('cm', cmIds[0]);\n if (cmInfo.hasdelegatedsection) {\n delegatedsection = cmInfo.delegatesectionid;\n titleText = this.reactive.getFormatString('cmdelete_subsectiontitle');\n bodyText = getString(\n 'sectiondelete_info',\n 'core_courseformat',\n {\n type: cmInfo.modname,\n name: cmInfo.name,\n }\n );\n } else {\n titleText = this.reactive.getFormatString('cmdelete_title');\n bodyText = getString(\n 'cmdelete_info',\n 'core_courseformat',\n {\n type: cmInfo.modname,\n name: cmInfo.name,\n }\n );\n }\n } else {\n titleText = getString('cmsdelete_title', 'core_courseformat');\n bodyText = getString(\n 'cmsdelete_info',\n 'core_courseformat',\n {count: cmIds.length}\n );\n }\n\n const modal = await this._modalBodyRenderedPromise(ModalDeleteCancel, {\n title: titleText,\n body: bodyText,\n });\n\n modal.getRoot().on(\n ModalEvents.delete,\n e => {\n // Stop the default save button behaviour which is to close the modal.\n e.preventDefault();\n modal.destroy();\n this.reactive.dispatch('cmDelete', cmIds);\n if (cmIds.length == 1 && delegatedsection && target.baseURI.includes('section.php')) {\n // Redirect to the course main page if the subsection is the current page.\n let parameters = new URLSearchParams(window.location.search);\n if (parameters.has('id') && parameters.get('id') == delegatedsection) {\n this._dispatchSectionDelete([delegatedsection], target);\n }\n }\n }\n );\n }\n\n /**\n * Handle a cm availability change request.\n *\n * @param {Element} target the dispatch action element\n */\n async _requestCmAvailability(target) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n // Show the availability modal to decide which action to trigger.\n const exporter = this.reactive.getExporter();\n const data = {\n allowstealth: exporter.canUseStealth(this.reactive.state, cmIds),\n };\n const modal = await this._modalBodyRenderedPromise(ModalSaveCancel, {\n title: getString('availability', 'core'),\n body: Templates.render('core_courseformat/local/content/cm/availabilitymodal', data),\n saveButtonText: getString('apply', 'core'),\n });\n\n this._setupMutationRadioButtonModal(modal, cmIds);\n }\n\n /**\n * Handle a section availability change request.\n *\n * @param {Element} target the dispatch action element\n */\n async _requestSectionAvailability(target) {\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n const title = (sectionIds.length == 1) ? 'sectionavailability_title' : 'sectionsavailability_title';\n // Show the availability modal to decide which action to trigger.\n const modal = await this._modalBodyRenderedPromise(ModalSaveCancel, {\n title: this.reactive.getFormatString(title),\n body: Templates.render('core_courseformat/local/content/section/availabilitymodal', []),\n saveButtonText: getString('apply', 'core'),\n });\n\n this._setupMutationRadioButtonModal(modal, sectionIds);\n }\n\n /**\n * Add events to a mutation selector radio buttons modal.\n * @param {Modal} modal\n * @param {Number[]} ids the section or cm ids to apply the mutation\n */\n _setupMutationRadioButtonModal(modal, ids) {\n // The save button is not enabled until the user selects an option.\n modal.setButtonDisabled('save', true);\n\n const submitFunction = (radio) => {\n const mutation = radio?.value;\n if (!mutation) {\n return false;\n }\n this.reactive.dispatch(mutation, ids);\n return true;\n };\n\n const modalBody = getFirst(modal.getBody());\n const radioOptions = modalBody.querySelectorAll(this.selectors.OPTIONSRADIO);\n radioOptions.forEach(radio => {\n radio.addEventListener('change', () => {\n modal.setButtonDisabled('save', false);\n });\n radio.parentNode.addEventListener('click', () => {\n radio.checked = true;\n modal.setButtonDisabled('save', false);\n });\n radio.parentNode.addEventListener('dblclick', dbClickEvent => {\n if (submitFunction(radio)) {\n dbClickEvent.preventDefault();\n modal.destroy();\n }\n });\n });\n\n modal.getRoot().on(\n ModalEvents.save,\n () => {\n const radio = modalBody.querySelector(`${this.selectors.OPTIONSRADIO}:checked`);\n submitFunction(radio);\n }\n );\n }\n\n /**\n * Disable all add sections actions.\n *\n * @param {boolean} locked the new locked value.\n */\n _setAddSectionLocked(locked) {\n const targets = this.getElements(this.selectors.ADDSECTIONREGION);\n targets.forEach(element => {\n element.classList.toggle(this.classes.DISABLED, locked);\n const addSectionElement = element.querySelector(this.selectors.ADDSECTION);\n addSectionElement.classList.toggle(this.classes.DISABLED, locked);\n this.setElementLocked(addSectionElement, locked);\n // We tweak the element to show a tooltip as a title attribute.\n if (locked) {\n getString('sectionaddmax', 'core_courseformat')\n .then((text) => addSectionElement.setAttribute('title', text))\n .catch(Notification.exception);\n addSectionElement.style.pointerEvents = null; // Unlocks the pointer events.\n addSectionElement.style.userSelect = null; // Unlocks the pointer events.\n } else {\n addSectionElement.setAttribute('title', addSectionElement.dataset.addSections);\n }\n });\n const courseAddSection = this.getElement(this.selectors.COURSEADDSECTION);\n if (courseAddSection) {\n const addSection = courseAddSection.querySelector(this.selectors.ADDSECTION);\n addSection.classList.toggle(this.classes.DISPLAYNONE, locked);\n const noMoreSections = courseAddSection.querySelector(this.selectors.MAXSECTIONSWARNING);\n noMoreSections.classList.toggle(this.classes.DISPLAYNONE, !locked);\n }\n }\n\n /**\n * Replace an element with a copy with a different tag name.\n *\n * @param {Element} element the original element\n */\n _disableLink(element) {\n if (element) {\n element.style.pointerEvents = 'none';\n element.style.userSelect = 'none';\n element.classList.add(this.classes.DISABLED);\n element.classList.add(this.classes.ITALIC);\n element.setAttribute('aria-disabled', true);\n element.addEventListener('click', event => event.preventDefault());\n }\n }\n\n /**\n * Render a modal and return a body ready promise.\n *\n * @param {Modal} ModalClass the modal class\n * @param {object} modalParams the modal params\n * @return {Promise} the modal body ready promise\n */\n _modalBodyRenderedPromise(ModalClass, modalParams) {\n return new Promise((resolve, reject) => {\n ModalClass.create(modalParams).then((modal) => {\n modal.setRemoveOnClose(true);\n // Handle body loading event.\n modal.getRoot().on(ModalEvents.bodyRendered, () => {\n resolve(modal);\n });\n // Configure some extra modal params.\n if (modalParams.saveButtonText !== undefined) {\n modal.setSaveButtonText(modalParams.saveButtonText);\n }\n if (modalParams.deleteButtonText !== undefined) {\n modal.setDeleteButtonText(modalParams.saveButtonText);\n }\n modal.show();\n return;\n }).catch(() => {\n reject(`Cannot load modal content`);\n });\n });\n }\n\n /**\n * Hide and later destroy a modal.\n *\n * Behat will fail if we remove the modal while some boostrap collapse is executing.\n *\n * @param {Modal} modal\n * @param {HTMLElement} element the dom element to focus on.\n */\n _destroyModal(modal, element) {\n modal.hide();\n const pendingDestroy = new Pending(`courseformat/actions:destroyModal`);\n if (element) {\n element.focus();\n }\n setTimeout(() =>{\n modal.destroy();\n pendingDestroy.resolve();\n }, 500);\n }\n\n /**\n * Get the closest actions menu toggler to an action element.\n *\n * @param {HTMLElement} element the action link element\n * @returns {HTMLElement|undefined}\n */\n _getClosestActionMenuToogler(element) {\n const actionMenu = element.closest(this.selectors.ACTIONMENU);\n if (!actionMenu) {\n return undefined;\n }\n return actionMenu.querySelector(this.selectors.ACTIONMENUTOGGLER);\n }\n}\n"],"names":["directMutations","sectionHide","sectionShow","cmHide","cmShow","cmStealth","cmMoveRight","cmMoveLeft","cmNoGroups","cmSeparateGroups","cmVisibleGroups","BaseComponent","create","name","selectors","ACTIONLINK","SECTIONLINK","CMLINK","SECTIONNODE","MODALTOGGLER","ADDSECTION","CONTENTTREE","ACTIONMENU","ACTIONMENUTOGGLER","OPTIONSRADIO","COURSEADDSECTION","MAXSECTIONSWARNING","ADDSECTIONREGION","classes","DISABLED","ITALIC","DISPLAYNONE","actions","action","mutationReference","Object","entries","Error","stateReady","state","addEventListener","this","element","_dispatchClick","_checkSectionlist","CourseEvents","sectionRefreshed","getWatchers","watch","handler","event","target","closest","classList","contains","preventDefault","actionName","dataset","methodName","_actionMethodName","undefined","_requestMutationAction","requestName","charAt","toUpperCase","slice","_setAddSectionLocked","course","sectionlist","length","maxsections","_getTargetIds","ids","_target$dataset","id","push","bulkType","_target$dataset2","bulk","reactive","get","enabled","selectedType","selection","sectionIds","pendingModalReady","Pending","editTools","_getClosestActionMenuToogler","data","getExporter","titleText","sectionInfo","sectionid","sectiontitle","title","information","getFormatString","modal","_modalBodyRenderedPromise","Modal","body","Templates","render","modalBody","getBody","forEach","sectionId","currentElement","querySelector","_disableLink","ContentTree","SECTION","TOGGLER","COLLAPSE","matches","for","getAttribute","dispatch","_destroyModal","resolve","cmIds","exporter","cmInfo","cmid","cmname","hasdelegatedsection","cmId","ENTER","selector","_expandCmMoveModalParentSections","targetSectionId","targetCmId","droppedCmIds","dropData","cmDraggableData","nextcmid","section","cmlist","component","filter","sectionnode","toggler","find","collapsibleId","attr","replace","expandNode","collapse","parentElement","modname","sectionnum","beforemod","some","hassummary","rawtitle","_dispatchSectionDelete","bodyText","count","ModalDeleteCancel","getRoot","on","ModalEvents","delete","e","destroy","baseURI","includes","window","location","href","baseurl","mutationName","delegatedsection","delegatesectionid","type","parameters","URLSearchParams","search","has","allowstealth","canUseStealth","ModalSaveCancel","saveButtonText","_setupMutationRadioButtonModal","setButtonDisabled","submitFunction","radio","mutation","value","querySelectorAll","parentNode","checked","dbClickEvent","save","locked","getElements","toggle","addSectionElement","setElementLocked","then","text","setAttribute","catch","Notification","exception","style","pointerEvents","userSelect","addSections","courseAddSection","getElement","add","ModalClass","modalParams","Promise","reject","setRemoveOnClose","bodyRendered","setSaveButtonText","deleteButtonText","setDeleteButtonText","show","hide","pendingDestroy","focus","setTimeout","actionMenu"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"actions.min.js","sources":["../../../src/local/content/actions.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 * Course state actions dispatcher.\n *\n * This module captures all data-dispatch links in the course content and dispatch the proper\n * state mutation, including any confirmation and modal required.\n *\n * @module core_courseformat/local/content/actions\n * @class core_courseformat/local/content/actions\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent} from 'core/reactive';\nimport Collapse from 'theme_boost/bootstrap/collapse';\nimport Modal from 'core/modal';\nimport ModalSaveCancel from 'core/modal_save_cancel';\nimport ModalDeleteCancel from 'core/modal_delete_cancel';\nimport ModalEvents from 'core/modal_events';\nimport Templates from 'core/templates';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport {getFirst} from 'core/normalise';\nimport {toggleBulkSelectionAction} from 'core_courseformat/local/content/actions/bulkselection';\nimport * as CourseEvents from 'core_course/events';\nimport Pending from 'core/pending';\nimport ContentTree from 'core_courseformat/local/courseeditor/contenttree';\n// The jQuery module is only used for interacting with Boostrap 4. It can we removed when MDL-71979 is integrated.\nimport Notification from \"core/notification\";\n\n// Load global strings.\nprefetchStrings('core', ['movecoursesection', 'movecoursemodule', 'confirm', 'delete']);\n\n// Mutations are dispatched by the course content actions.\n// Formats can use this module addActions static method to add custom actions.\n// Direct mutations can be simple strings (mutation) name or functions.\nconst directMutations = {\n sectionHide: 'sectionHide',\n sectionShow: 'sectionShow',\n cmHide: 'cmHide',\n cmShow: 'cmShow',\n cmStealth: 'cmStealth',\n cmMoveRight: 'cmMoveRight',\n cmMoveLeft: 'cmMoveLeft',\n cmNoGroups: 'cmNoGroups',\n cmSeparateGroups: 'cmSeparateGroups',\n cmVisibleGroups: 'cmVisibleGroups',\n};\n\nexport default class extends BaseComponent {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'content_actions';\n // Default query selectors.\n this.selectors = {\n ACTIONLINK: `[data-action]`,\n // Move modal selectors.\n SECTIONLINK: `[data-for='section']`,\n CMLINK: `[data-for='cm']`,\n SECTIONNODE: `[data-for='sectionnode']`,\n MODALTOGGLER: `[data-bs-toggle='collapse']`,\n ADDSECTION: `[data-action='addSection']`,\n CONTENTTREE: `#destination-selector`,\n ACTIONMENU: `.action-menu`,\n ACTIONMENUTOGGLER: `[data-bs-toggle=\"dropdown\"]`,\n // Availability modal selectors.\n OPTIONSRADIO: `[type='radio']`,\n COURSEADDSECTION: `#course-addsection`,\n MAXSECTIONSWARNING: `[data-region='max-sections-warning']`,\n ADDSECTIONREGION: `[data-region='section-addsection']`,\n };\n // Component css classes.\n this.classes = {\n DISABLED: `disabled`,\n ITALIC: `fst-italic`,\n DISPLAYNONE: `d-none`,\n };\n }\n\n /**\n * Add extra actions to the module.\n *\n * @param {array} actions array of methods to execute\n */\n static addActions(actions) {\n for (const [action, mutationReference] of Object.entries(actions)) {\n if (typeof mutationReference !== 'function' && typeof mutationReference !== 'string') {\n throw new Error(`${action} action must be a mutation name or a function`);\n }\n directMutations[action] = mutationReference;\n }\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the state data.\n *\n */\n stateReady(state) {\n // Delegate dispatch clicks.\n this.addEventListener(\n this.element,\n 'click',\n this._dispatchClick\n );\n // Check section limit.\n this._checkSectionlist({state});\n // Add an Event listener to recalculate limits it if a section HTML is altered.\n this.addEventListener(\n this.element,\n CourseEvents.sectionRefreshed,\n () => this._checkSectionlist({state})\n );\n }\n\n /**\n * Return the component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n return [\n // Check section limit.\n {watch: `course.sectionlist:updated`, handler: this._checkSectionlist},\n ];\n }\n\n _dispatchClick(event) {\n const target = event.target.closest(this.selectors.ACTIONLINK);\n if (!target) {\n return;\n }\n if (target.classList.contains(this.classes.DISABLED)) {\n event.preventDefault();\n return;\n }\n\n // Invoke proper method.\n const actionName = target.dataset.action;\n const methodName = this._actionMethodName(actionName);\n\n if (this[methodName] !== undefined) {\n this[methodName](target, event);\n return;\n }\n\n // Check direct mutations or mutations handlers.\n if (directMutations[actionName] !== undefined) {\n if (typeof directMutations[actionName] === 'function') {\n directMutations[actionName](target, event);\n return;\n }\n this._requestMutationAction(target, event, directMutations[actionName]);\n return;\n }\n }\n\n _actionMethodName(name) {\n const requestName = name.charAt(0).toUpperCase() + name.slice(1);\n return `_request${requestName}`;\n }\n\n /**\n * Check the section list and disable some options if needed.\n *\n * @param {Object} detail the update details.\n * @param {Object} detail.state the state object.\n */\n _checkSectionlist({state}) {\n // Disable \"add section\" actions if the course max sections has been exceeded.\n this._setAddSectionLocked(state.course.sectionlist.length > state.course.maxsections);\n }\n\n /**\n * Return the ids represented by this element.\n *\n * Depending on the dataset attributes the action could represent a single id\n * or a bulk actions with all the current selected ids.\n *\n * @param {HTMLElement} target\n * @returns {Number[]} array of Ids\n */\n _getTargetIds(target) {\n let ids = [];\n if (target?.dataset?.id) {\n ids.push(target.dataset.id);\n }\n const bulkType = target?.dataset?.bulk;\n if (!bulkType) {\n return ids;\n }\n const bulk = this.reactive.get('bulk');\n if (bulk.enabled && bulk.selectedType === bulkType) {\n ids = [...ids, ...bulk.selection];\n }\n return ids;\n }\n\n /**\n * Handle a move section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestMoveSection(target, event) {\n // Check we have an id.\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n const pendingModalReady = new Pending(`courseformat/actions:prepareMoveSectionModal`);\n\n // The section edit menu to refocus on end.\n const editTools = this._getClosestActionMenuToogler(target);\n\n // Collect section information from the state.\n const exporter = this.reactive.getExporter();\n const data = exporter.course(this.reactive.state);\n let titleText = null;\n\n // Add the target section id and title.\n let sectionInfo = null;\n if (sectionIds.length == 1) {\n sectionInfo = this.reactive.get('section', sectionIds[0]);\n data.sectionid = sectionInfo.id;\n data.sectiontitle = sectionInfo.title;\n data.information = await this.reactive.getFormatString('sectionmove_info', data.sectiontitle);\n titleText = this.reactive.getFormatString('sectionmove_title');\n } else {\n data.information = await this.reactive.getFormatString('sectionsmove_info', sectionIds.length);\n titleText = this.reactive.getFormatString('sectionsmove_title');\n }\n\n\n // Create the modal.\n // Build the modal parameters from the event data.\n const modal = await this._modalBodyRenderedPromise(Modal, {\n title: titleText,\n body: Templates.render('core_courseformat/local/content/movesection', data),\n });\n\n const modalBody = getFirst(modal.getBody());\n\n // Disable current selected section ids.\n sectionIds.forEach(sectionId => {\n const currentElement = modalBody.querySelector(`${this.selectors.SECTIONLINK}[data-id='${sectionId}']`);\n this._disableLink(currentElement);\n });\n\n // Setup keyboard navigation.\n new ContentTree(\n modalBody.querySelector(this.selectors.CONTENTTREE),\n {\n SECTION: this.selectors.SECTIONNODE,\n TOGGLER: this.selectors.MODALTOGGLER,\n COLLAPSE: this.selectors.MODALTOGGLER,\n },\n true\n );\n\n // Capture click.\n modalBody.addEventListener('click', (event) => {\n const target = event.target;\n if (!target.matches('a') || target.dataset.for != 'section' || target.dataset.id === undefined) {\n return;\n }\n if (target.getAttribute('aria-disabled')) {\n return;\n }\n event.preventDefault();\n this.reactive.dispatch('sectionMoveAfter', sectionIds, target.dataset.id);\n this._destroyModal(modal, editTools);\n });\n\n pendingModalReady.resolve();\n }\n\n /**\n * Handle a move cm request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestMoveCm(target, event) {\n // Check we have an id.\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n const pendingModalReady = new Pending(`courseformat/actions:prepareMoveCmModal`);\n\n // The section edit menu to refocus on end.\n const editTools = this._getClosestActionMenuToogler(target);\n\n // Collect information from the state.\n const exporter = this.reactive.getExporter();\n const data = exporter.course(this.reactive.state);\n\n let titleText = null;\n if (cmIds.length == 1) {\n const cmInfo = this.reactive.get('cm', cmIds[0]);\n data.cmid = cmInfo.id;\n data.cmname = cmInfo.name;\n data.information = await this.reactive.getFormatString('cmmove_info', data.cmname);\n if (cmInfo.hasdelegatedsection) {\n titleText = this.reactive.getFormatString('cmmove_subsectiontitle');\n } else {\n titleText = this.reactive.getFormatString('cmmove_title');\n }\n } else {\n data.information = await this.reactive.getFormatString('cmsmove_info', cmIds.length);\n titleText = this.reactive.getFormatString('cmsmove_title');\n }\n\n // Create the modal.\n // Build the modal parameters from the event data.\n const modal = await this._modalBodyRenderedPromise(Modal, {\n title: titleText,\n body: Templates.render('core_courseformat/local/content/movecm', data),\n });\n\n const modalBody = getFirst(modal.getBody());\n\n // Disable current selected section ids.\n cmIds.forEach(cmId => {\n const currentElement = modalBody.querySelector(`${this.selectors.CMLINK}[data-id='${cmId}']`);\n this._disableLink(currentElement);\n });\n\n // Setup keyboard navigation.\n new ContentTree(\n modalBody.querySelector(this.selectors.CONTENTTREE),\n {\n SECTION: this.selectors.SECTIONNODE,\n TOGGLER: this.selectors.MODALTOGGLER,\n COLLAPSE: this.selectors.MODALTOGGLER,\n ENTER: this.selectors.SECTIONLINK,\n }\n );\n\n cmIds.forEach(cmId => {\n const cmInfo = this.reactive.get('cm', cmId);\n let selector;\n if (!cmInfo.hasdelegatedsection) {\n selector = `${this.selectors.CMLINK}[data-id='${cmId}']`;\n } else {\n selector = `${this.selectors.SECTIONLINK}[data-id='${cmInfo.sectionid}']`;\n }\n const currentElement = modalBody.querySelector(selector);\n this._expandCmMoveModalParentSections(modalBody, currentElement);\n });\n\n modalBody.addEventListener('click', (event) => {\n const target = event.target;\n if (!target.matches('a') || target.dataset.for === undefined || target.dataset.id === undefined) {\n return;\n }\n if (target.getAttribute('aria-disabled')) {\n return;\n }\n event.preventDefault();\n\n let targetSectionId;\n let targetCmId;\n let droppedCmIds = [...cmIds];\n if (target.dataset.for == 'cm') {\n const dropData = exporter.cmDraggableData(this.reactive.state, target.dataset.id);\n targetSectionId = dropData.sectionid;\n targetCmId = dropData.nextcmid;\n } else {\n const section = this.reactive.get('section', target.dataset.id);\n targetSectionId = target.dataset.id;\n targetCmId = section?.cmlist[0];\n }\n const section = this.reactive.get('section', targetSectionId);\n if (section.component) {\n // Remove cmIds which are not allowed to be moved to this delegated section (mostly\n // all other delegated cm).\n droppedCmIds = droppedCmIds.filter(cmId => {\n const cmInfo = this.reactive.get('cm', cmId);\n return !cmInfo.hasdelegatedsection;\n });\n }\n if (droppedCmIds.length === 0) {\n return; // No cm to move.\n }\n this.reactive.dispatch('cmMove', droppedCmIds, targetSectionId, targetCmId);\n this._destroyModal(modal, editTools);\n });\n\n pendingModalReady.resolve();\n }\n\n /**\n * Expand all the modal tree branches that contains the element.\n *\n * Bootstrap 4 uses jQuery to interact with collapsibles.\n * All jQuery in this code can be replaced when MDL-71979 is integrated.\n *\n * @private\n * @param {HTMLElement} modalBody the modal body element\n * @param {HTMLElement} element the element to display\n */\n _expandCmMoveModalParentSections(modalBody, element) {\n const sectionnode = element.closest(this.selectors.SECTIONNODE);\n if (!sectionnode) {\n return;\n }\n\n const toggler = sectionnode.querySelector(this.selectors.MODALTOGGLER);\n let collapsibleId = toggler.dataset.target ?? toggler.getAttribute('href');\n if (collapsibleId) {\n // We cannot be sure we have # in the id element name.\n collapsibleId = collapsibleId.replace('#', '');\n const expandNode = modalBody.querySelector(`#${collapsibleId}`);\n new Collapse(expandNode, {toggle: false}).show();\n }\n\n // Section are a tree structure, we need to expand all the parents.\n this._expandCmMoveModalParentSections(modalBody, sectionnode.parentElement);\n }\n\n /**\n * Handle a create section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestAddSection(target, event) {\n event.preventDefault();\n this.reactive.dispatch('addSection', target.dataset.id ?? 0);\n }\n\n /**\n * Handle a create subsection request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestAddModule(target, event) {\n event.preventDefault();\n this.reactive.dispatch('addModule', target.dataset.modname, target.dataset.sectionnum, target.dataset.beforemod);\n }\n\n /**\n * Handle a delete section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestDeleteSection(target, event) {\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n // We don't need confirmation to delete empty sections.\n let needsConfirmation = sectionIds.some(sectionId => {\n const sectionInfo = this.reactive.get('section', sectionId);\n const cmList = sectionInfo.cmlist ?? [];\n return (cmList.length || sectionInfo.hassummary || sectionInfo.rawtitle);\n });\n if (!needsConfirmation) {\n this._dispatchSectionDelete(sectionIds, target);\n return;\n }\n\n let bodyText = null;\n let titleText = null;\n if (sectionIds.length == 1) {\n titleText = this.reactive.getFormatString('sectiondelete_title');\n const sectionInfo = this.reactive.get('section', sectionIds[0]);\n bodyText = this.reactive.getFormatString('sectiondelete_info', {name: sectionInfo.title});\n } else {\n titleText = this.reactive.getFormatString('sectionsdelete_title');\n bodyText = this.reactive.getFormatString('sectionsdelete_info', {count: sectionIds.length});\n }\n\n const modal = await this._modalBodyRenderedPromise(ModalDeleteCancel, {\n title: titleText,\n body: bodyText,\n });\n\n modal.getRoot().on(\n ModalEvents.delete,\n e => {\n // Stop the default save button behaviour which is to close the modal.\n e.preventDefault();\n modal.destroy();\n this._dispatchSectionDelete(sectionIds, target);\n }\n );\n }\n\n /**\n * Dispatch the section delete action and handle the redirection if necessary.\n *\n * @param {Array} sectionIds the IDs of the sections to delete.\n * @param {Element} target the dispatch action element\n */\n async _dispatchSectionDelete(sectionIds, target) {\n await this.reactive.dispatch('sectionDelete', sectionIds);\n if (target.baseURI.includes('section.php')) {\n // Redirect to the course main page if the section is the current page.\n window.location.href = this.reactive.get('course').baseurl;\n }\n }\n\n /**\n * Handle a toggle cm selection.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestToggleSelectionCm(target, event) {\n toggleBulkSelectionAction(this.reactive, target, event, 'cm');\n }\n\n /**\n * Handle a toggle section selection.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestToggleSelectionSection(target, event) {\n toggleBulkSelectionAction(this.reactive, target, event, 'section');\n }\n\n /**\n * Basic mutation action helper.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n * @param {string} mutationName the mutation name\n */\n async _requestMutationAction(target, event, mutationName) {\n if (!target.dataset.id && target.dataset.for !== 'bulkaction') {\n return;\n }\n event.preventDefault();\n if (target.dataset.for === 'bulkaction') {\n // If the mutation is a bulk action we use the current selection.\n this.reactive.dispatch(mutationName, this.reactive.get('bulk').selection);\n } else {\n this.reactive.dispatch(mutationName, [target.dataset.id]);\n }\n }\n\n /**\n * Handle a course module duplicate request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestCmDuplicate(target, event) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n const sectionId = target.dataset.sectionid ?? null;\n event.preventDefault();\n this.reactive.dispatch('cmDuplicate', cmIds, sectionId);\n }\n\n /**\n * Handle a delete cm request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestCmDelete(target, event) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n let bodyText = null;\n let titleText = null;\n let delegatedsection = null;\n if (cmIds.length == 1) {\n const cmInfo = this.reactive.get('cm', cmIds[0]);\n if (cmInfo.hasdelegatedsection) {\n delegatedsection = cmInfo.delegatesectionid;\n titleText = this.reactive.getFormatString('cmdelete_subsectiontitle');\n bodyText = getString(\n 'sectiondelete_info',\n 'core_courseformat',\n {\n type: cmInfo.modname,\n name: cmInfo.name,\n }\n );\n } else {\n titleText = this.reactive.getFormatString('cmdelete_title');\n bodyText = getString(\n 'cmdelete_info',\n 'core_courseformat',\n {\n type: cmInfo.modname,\n name: cmInfo.name,\n }\n );\n }\n } else {\n titleText = getString('cmsdelete_title', 'core_courseformat');\n bodyText = getString(\n 'cmsdelete_info',\n 'core_courseformat',\n {count: cmIds.length}\n );\n }\n\n const modal = await this._modalBodyRenderedPromise(ModalDeleteCancel, {\n title: titleText,\n body: bodyText,\n });\n\n modal.getRoot().on(\n ModalEvents.delete,\n e => {\n // Stop the default save button behaviour which is to close the modal.\n e.preventDefault();\n modal.destroy();\n this.reactive.dispatch('cmDelete', cmIds);\n if (cmIds.length == 1 && delegatedsection && target.baseURI.includes('section.php')) {\n // Redirect to the course main page if the subsection is the current page.\n let parameters = new URLSearchParams(window.location.search);\n if (parameters.has('id') && parameters.get('id') == delegatedsection) {\n this._dispatchSectionDelete([delegatedsection], target);\n }\n }\n }\n );\n }\n\n /**\n * Handle a cm availability change request.\n *\n * @param {Element} target the dispatch action element\n */\n async _requestCmAvailability(target) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n // Show the availability modal to decide which action to trigger.\n const exporter = this.reactive.getExporter();\n const data = {\n allowstealth: exporter.canUseStealth(this.reactive.state, cmIds),\n };\n const modal = await this._modalBodyRenderedPromise(ModalSaveCancel, {\n title: getString('availability', 'core'),\n body: Templates.render('core_courseformat/local/content/cm/availabilitymodal', data),\n saveButtonText: getString('apply', 'core'),\n });\n\n this._setupMutationRadioButtonModal(modal, cmIds);\n }\n\n /**\n * Handle a section availability change request.\n *\n * @param {Element} target the dispatch action element\n */\n async _requestSectionAvailability(target) {\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n const title = (sectionIds.length == 1) ? 'sectionavailability_title' : 'sectionsavailability_title';\n // Show the availability modal to decide which action to trigger.\n const modal = await this._modalBodyRenderedPromise(ModalSaveCancel, {\n title: this.reactive.getFormatString(title),\n body: Templates.render('core_courseformat/local/content/section/availabilitymodal', []),\n saveButtonText: getString('apply', 'core'),\n });\n\n this._setupMutationRadioButtonModal(modal, sectionIds);\n }\n\n /**\n * Add events to a mutation selector radio buttons modal.\n * @param {Modal} modal\n * @param {Number[]} ids the section or cm ids to apply the mutation\n */\n _setupMutationRadioButtonModal(modal, ids) {\n // The save button is not enabled until the user selects an option.\n modal.setButtonDisabled('save', true);\n\n const submitFunction = (radio) => {\n const mutation = radio?.value;\n if (!mutation) {\n return false;\n }\n this.reactive.dispatch(mutation, ids);\n return true;\n };\n\n const modalBody = getFirst(modal.getBody());\n const radioOptions = modalBody.querySelectorAll(this.selectors.OPTIONSRADIO);\n radioOptions.forEach(radio => {\n radio.addEventListener('change', () => {\n modal.setButtonDisabled('save', false);\n });\n radio.parentNode.addEventListener('click', () => {\n radio.checked = true;\n modal.setButtonDisabled('save', false);\n });\n radio.parentNode.addEventListener('dblclick', dbClickEvent => {\n if (submitFunction(radio)) {\n dbClickEvent.preventDefault();\n modal.destroy();\n }\n });\n });\n\n modal.getRoot().on(\n ModalEvents.save,\n () => {\n const radio = modalBody.querySelector(`${this.selectors.OPTIONSRADIO}:checked`);\n submitFunction(radio);\n }\n );\n }\n\n /**\n * Disable all add sections actions.\n *\n * @param {boolean} locked the new locked value.\n */\n _setAddSectionLocked(locked) {\n const targets = this.getElements(this.selectors.ADDSECTIONREGION);\n targets.forEach(element => {\n element.classList.toggle(this.classes.DISABLED, locked);\n const addSectionElement = element.querySelector(this.selectors.ADDSECTION);\n addSectionElement.classList.toggle(this.classes.DISABLED, locked);\n this.setElementLocked(addSectionElement, locked);\n // We tweak the element to show a tooltip as a title attribute.\n if (locked) {\n getString('sectionaddmax', 'core_courseformat')\n .then((text) => addSectionElement.setAttribute('title', text))\n .catch(Notification.exception);\n addSectionElement.style.pointerEvents = null; // Unlocks the pointer events.\n addSectionElement.style.userSelect = null; // Unlocks the pointer events.\n } else {\n addSectionElement.setAttribute('title', addSectionElement.dataset.addSections);\n }\n });\n const courseAddSection = this.getElement(this.selectors.COURSEADDSECTION);\n if (courseAddSection) {\n const addSection = courseAddSection.querySelector(this.selectors.ADDSECTION);\n addSection.classList.toggle(this.classes.DISPLAYNONE, locked);\n const noMoreSections = courseAddSection.querySelector(this.selectors.MAXSECTIONSWARNING);\n noMoreSections.classList.toggle(this.classes.DISPLAYNONE, !locked);\n }\n }\n\n /**\n * Replace an element with a copy with a different tag name.\n *\n * @param {Element} element the original element\n */\n _disableLink(element) {\n if (element) {\n element.style.pointerEvents = 'none';\n element.style.userSelect = 'none';\n element.classList.add(this.classes.DISABLED);\n element.classList.add(this.classes.ITALIC);\n element.setAttribute('aria-disabled', true);\n element.addEventListener('click', event => event.preventDefault());\n }\n }\n\n /**\n * Render a modal and return a body ready promise.\n *\n * @param {Modal} ModalClass the modal class\n * @param {object} modalParams the modal params\n * @return {Promise} the modal body ready promise\n */\n _modalBodyRenderedPromise(ModalClass, modalParams) {\n return new Promise((resolve, reject) => {\n ModalClass.create(modalParams).then((modal) => {\n modal.setRemoveOnClose(true);\n // Handle body loading event.\n modal.getRoot().on(ModalEvents.bodyRendered, () => {\n resolve(modal);\n });\n // Configure some extra modal params.\n if (modalParams.saveButtonText !== undefined) {\n modal.setSaveButtonText(modalParams.saveButtonText);\n }\n if (modalParams.deleteButtonText !== undefined) {\n modal.setDeleteButtonText(modalParams.saveButtonText);\n }\n modal.show();\n return;\n }).catch(() => {\n reject(`Cannot load modal content`);\n });\n });\n }\n\n /**\n * Hide and later destroy a modal.\n *\n * Behat will fail if we remove the modal while some boostrap collapse is executing.\n *\n * @param {Modal} modal\n * @param {HTMLElement} element the dom element to focus on.\n */\n _destroyModal(modal, element) {\n modal.hide();\n const pendingDestroy = new Pending(`courseformat/actions:destroyModal`);\n if (element) {\n element.focus();\n }\n setTimeout(() =>{\n modal.destroy();\n pendingDestroy.resolve();\n }, 500);\n }\n\n /**\n * Get the closest actions menu toggler to an action element.\n *\n * @param {HTMLElement} element the action link element\n * @returns {HTMLElement|undefined}\n */\n _getClosestActionMenuToogler(element) {\n const actionMenu = element.closest(this.selectors.ACTIONMENU);\n if (!actionMenu) {\n return undefined;\n }\n return actionMenu.querySelector(this.selectors.ACTIONMENUTOGGLER);\n }\n}\n"],"names":["directMutations","sectionHide","sectionShow","cmHide","cmShow","cmStealth","cmMoveRight","cmMoveLeft","cmNoGroups","cmSeparateGroups","cmVisibleGroups","BaseComponent","create","name","selectors","ACTIONLINK","SECTIONLINK","CMLINK","SECTIONNODE","MODALTOGGLER","ADDSECTION","CONTENTTREE","ACTIONMENU","ACTIONMENUTOGGLER","OPTIONSRADIO","COURSEADDSECTION","MAXSECTIONSWARNING","ADDSECTIONREGION","classes","DISABLED","ITALIC","DISPLAYNONE","actions","action","mutationReference","Object","entries","Error","stateReady","state","addEventListener","this","element","_dispatchClick","_checkSectionlist","CourseEvents","sectionRefreshed","getWatchers","watch","handler","event","target","closest","classList","contains","preventDefault","actionName","dataset","methodName","_actionMethodName","undefined","_requestMutationAction","requestName","charAt","toUpperCase","slice","_setAddSectionLocked","course","sectionlist","length","maxsections","_getTargetIds","ids","_target$dataset","id","push","bulkType","_target$dataset2","bulk","reactive","get","enabled","selectedType","selection","sectionIds","pendingModalReady","Pending","editTools","_getClosestActionMenuToogler","data","getExporter","titleText","sectionInfo","sectionid","sectiontitle","title","information","getFormatString","modal","_modalBodyRenderedPromise","Modal","body","Templates","render","modalBody","getBody","forEach","sectionId","currentElement","querySelector","_disableLink","ContentTree","SECTION","TOGGLER","COLLAPSE","matches","for","getAttribute","dispatch","_destroyModal","resolve","cmIds","exporter","cmInfo","cmid","cmname","hasdelegatedsection","cmId","ENTER","selector","_expandCmMoveModalParentSections","targetSectionId","targetCmId","droppedCmIds","dropData","cmDraggableData","nextcmid","section","cmlist","component","filter","sectionnode","toggler","collapsibleId","replace","expandNode","Collapse","toggle","show","parentElement","modname","sectionnum","beforemod","some","hassummary","rawtitle","_dispatchSectionDelete","bodyText","count","ModalDeleteCancel","getRoot","on","ModalEvents","delete","e","destroy","baseURI","includes","window","location","href","baseurl","mutationName","delegatedsection","delegatesectionid","type","parameters","URLSearchParams","search","has","allowstealth","canUseStealth","ModalSaveCancel","saveButtonText","_setupMutationRadioButtonModal","setButtonDisabled","submitFunction","radio","mutation","value","querySelectorAll","parentNode","checked","dbClickEvent","save","locked","getElements","addSectionElement","setElementLocked","then","text","setAttribute","catch","Notification","exception","style","pointerEvents","userSelect","addSections","courseAddSection","getElement","add","ModalClass","modalParams","Promise","reject","setRemoveOnClose","bodyRendered","setSaveButtonText","deleteButtonText","setDeleteButtonText","hide","pendingDestroy","focus","setTimeout","actionMenu"],"mappings":";;;;;;;;;;;+tCA6CgB,OAAQ,CAAC,oBAAqB,mBAAoB,UAAW,iBAKvEA,gBAAkB,CACpBC,YAAa,cACbC,YAAa,cACbC,OAAQ,SACRC,OAAQ,SACRC,UAAW,YACXC,YAAa,cACbC,WAAY,aACZC,WAAY,aACZC,iBAAkB,mBAClBC,gBAAiB,0CAGQC,wBAKzBC,cAESC,KAAO,uBAEPC,UAAY,CACbC,2BAEAC,mCACAC,yBACAC,uCACAC,2CACAC,wCACAC,oCACAC,0BACAC,gDAEAC,8BACAC,sCACAC,0DACAC,4DAGCC,QAAU,CACXC,oBACAC,oBACAC,wCASUC,aACT,MAAOC,OAAQC,qBAAsBC,OAAOC,QAAQJ,SAAU,IAC9B,mBAAtBE,mBAAiE,iBAAtBA,wBAC5C,IAAIG,gBAASJ,yDAEvBjC,gBAAgBiC,QAAUC,mBAUlCI,WAAWC,YAEFC,iBACDC,KAAKC,QACL,QACAD,KAAKE,qBAGJC,kBAAkB,CAACL,MAAAA,aAEnBC,iBACDC,KAAKC,QACLG,aAAaC,kBACb,IAAML,KAAKG,kBAAkB,CAACL,MAAAA,UAStCQ,oBACW,CAEH,CAACC,mCAAqCC,QAASR,KAAKG,oBAI5DD,eAAeO,aACLC,OAASD,MAAMC,OAAOC,QAAQX,KAAK3B,UAAUC,gBAC9CoC,iBAGDA,OAAOE,UAAUC,SAASb,KAAKb,QAAQC,sBACvCqB,MAAMK,uBAKJC,WAAaL,OAAOM,QAAQxB,OAC5ByB,WAAajB,KAAKkB,kBAAkBH,oBAEjBI,IAArBnB,KAAKiB,wBAM2BE,IAAhC5D,gBAAgBwD,YAC2B,mBAAhCxD,gBAAgBwD,iBACvBxD,gBAAgBwD,YAAYL,OAAQD,iBAGnCW,uBAAuBV,OAAQD,MAAOlD,gBAAgBwD,yBAVtDE,YAAYP,OAAQD,OAejCS,kBAAkB9C,YACRiD,YAAcjD,KAAKkD,OAAO,GAAGC,cAAgBnD,KAAKoD,MAAM,2BAC5CH,aAStBlB,4BAAkBL,MAACA,iBAEV2B,qBAAqB3B,MAAM4B,OAAOC,YAAYC,OAAS9B,MAAM4B,OAAOG,aAY7EC,cAAcpB,iDACNqB,IAAM,GACNrB,MAAAA,gCAAAA,OAAQM,oCAARgB,gBAAiBC,IACjBF,IAAIG,KAAKxB,OAAOM,QAAQiB,UAEtBE,SAAWzB,MAAAA,iCAAAA,OAAQM,2CAARoB,iBAAiBC,SAC7BF,gBACMJ,UAELM,KAAOrC,KAAKsC,SAASC,IAAI,eAC3BF,KAAKG,SAAWH,KAAKI,eAAiBN,WACtCJ,IAAM,IAAIA,OAAQM,KAAKK,YAEpBX,8BASerB,OAAQD,aAExBkC,WAAa3C,KAAK8B,cAAcpB,WACb,GAArBiC,WAAWf,cAIfnB,MAAMK,uBAEA8B,kBAAoB,IAAIC,iEAGxBC,UAAY9C,KAAK+C,6BAA6BrC,QAI9CsC,KADWhD,KAAKsC,SAASW,cACTvB,OAAO1B,KAAKsC,SAASxC,WACvCoD,UAAY,KAGZC,YAAc,KACO,GAArBR,WAAWf,QACXuB,YAAcnD,KAAKsC,SAASC,IAAI,UAAWI,WAAW,IACtDK,KAAKI,UAAYD,YAAYlB,GAC7Be,KAAKK,aAAeF,YAAYG,MAChCN,KAAKO,kBAAoBvD,KAAKsC,SAASkB,gBAAgB,mBAAoBR,KAAKK,cAChFH,UAAYlD,KAAKsC,SAASkB,gBAAgB,uBAE1CR,KAAKO,kBAAoBvD,KAAKsC,SAASkB,gBAAgB,oBAAqBb,WAAWf,QACvFsB,UAAYlD,KAAKsC,SAASkB,gBAAgB,6BAMxCC,YAAczD,KAAK0D,0BAA0BC,eAAO,CACtDL,MAAOJ,UACPU,KAAMC,mBAAUC,OAAO,8CAA+Cd,QAGpEe,WAAY,uBAASN,MAAMO,WAGjCrB,WAAWsB,SAAQC,kBACTC,eAAiBJ,UAAUK,wBAAiBpE,KAAK3B,UAAUE,iCAAwB2F,sBACpFG,aAAaF,uBAIlBG,qBACAP,UAAUK,cAAcpE,KAAK3B,UAAUO,aACvC,CACI2F,QAASvE,KAAK3B,UAAUI,YACxB+F,QAASxE,KAAK3B,UAAUK,aACxB+F,SAAUzE,KAAK3B,UAAUK,eAE7B,GAIJqF,UAAUhE,iBAAiB,SAAUU,cAC3BC,OAASD,MAAMC,OAChBA,OAAOgE,QAAQ,MAA8B,WAAtBhE,OAAOM,QAAQ2D,UAA0CxD,IAAtBT,OAAOM,QAAQiB,KAG1EvB,OAAOkE,aAAa,mBAGxBnE,MAAMK,sBACDwB,SAASuC,SAAS,mBAAoBlC,WAAYjC,OAAOM,QAAQiB,SACjE6C,cAAcrB,MAAOX,gBAG9BF,kBAAkBmC,+BASDrE,OAAQD,aAEnBuE,MAAQhF,KAAK8B,cAAcpB,WACb,GAAhBsE,MAAMpD,cAIVnB,MAAMK,uBAEA8B,kBAAoB,IAAIC,4DAGxBC,UAAY9C,KAAK+C,6BAA6BrC,QAG9CuE,SAAWjF,KAAKsC,SAASW,cACzBD,KAAOiC,SAASvD,OAAO1B,KAAKsC,SAASxC,WAEvCoD,UAAY,QACI,GAAhB8B,MAAMpD,OAAa,OACbsD,OAASlF,KAAKsC,SAASC,IAAI,KAAMyC,MAAM,IAC7ChC,KAAKmC,KAAOD,OAAOjD,GACnBe,KAAKoC,OAASF,OAAO9G,KACrB4E,KAAKO,kBAAoBvD,KAAKsC,SAASkB,gBAAgB,cAAeR,KAAKoC,QAEvElC,UADAgC,OAAOG,oBACKrF,KAAKsC,SAASkB,gBAAgB,0BAE9BxD,KAAKsC,SAASkB,gBAAgB,qBAG9CR,KAAKO,kBAAoBvD,KAAKsC,SAASkB,gBAAgB,eAAgBwB,MAAMpD,QAC7EsB,UAAYlD,KAAKsC,SAASkB,gBAAgB,uBAKxCC,YAAczD,KAAK0D,0BAA0BC,eAAO,CACtDL,MAAOJ,UACPU,KAAMC,mBAAUC,OAAO,yCAA0Cd,QAG/De,WAAY,uBAASN,MAAMO,WAGjCgB,MAAMf,SAAQqB,aACJnB,eAAiBJ,UAAUK,wBAAiBpE,KAAK3B,UAAUG,4BAAmB8G,iBAC/EjB,aAAaF,uBAIlBG,qBACAP,UAAUK,cAAcpE,KAAK3B,UAAUO,aACvC,CACI2F,QAASvE,KAAK3B,UAAUI,YACxB+F,QAASxE,KAAK3B,UAAUK,aACxB+F,SAAUzE,KAAK3B,UAAUK,aACzB6G,MAAOvF,KAAK3B,UAAUE,cAI9ByG,MAAMf,SAAQqB,aACJJ,OAASlF,KAAKsC,SAASC,IAAI,KAAM+C,UACnCE,SAIAA,SAHCN,OAAOG,8BAGMrF,KAAK3B,UAAUE,iCAAwB2G,OAAO9B,0BAF9CpD,KAAK3B,UAAUG,4BAAmB8G,iBAI9CnB,eAAiBJ,UAAUK,cAAcoB,eAC1CC,iCAAiC1B,UAAWI,mBAGrDJ,UAAUhE,iBAAiB,SAAUU,cAC3BC,OAASD,MAAMC,WAChBA,OAAOgE,QAAQ,WAA+BvD,IAAvBT,OAAOM,QAAQ2D,UAA2CxD,IAAtBT,OAAOM,QAAQiB,aAG3EvB,OAAOkE,aAAa,4BAKpBc,gBACAC,WAHJlF,MAAMK,qBAIF8E,aAAe,IAAIZ,UACG,MAAtBtE,OAAOM,QAAQ2D,IAAa,OACtBkB,SAAWZ,SAASa,gBAAgB9F,KAAKsC,SAASxC,MAAOY,OAAOM,QAAQiB,IAC9EyD,gBAAkBG,SAASzC,UAC3BuC,WAAaE,SAASE,aACnB,OACGC,QAAUhG,KAAKsC,SAASC,IAAI,UAAW7B,OAAOM,QAAQiB,IAC5DyD,gBAAkBhF,OAAOM,QAAQiB,GACjC0D,WAAaK,MAAAA,eAAAA,QAASC,OAAO,GAEjBjG,KAAKsC,SAASC,IAAI,UAAWmD,iBACjCQ,YAGRN,aAAeA,aAAaO,QAAOb,OAChBtF,KAAKsC,SAASC,IAAI,KAAM+C,MACxBD,uBAGK,IAAxBO,aAAahE,cAGZU,SAASuC,SAAS,SAAUe,aAAcF,gBAAiBC,iBAC3Db,cAAcrB,MAAOX,eAG9BF,kBAAkBmC,UAatBU,iCAAiC1B,UAAW9D,yCAClCmG,YAAcnG,QAAQU,QAAQX,KAAK3B,UAAUI,iBAC9C2H,yBAICC,QAAUD,YAAYhC,cAAcpE,KAAK3B,UAAUK,kBACrD4H,4CAAgBD,QAAQrF,QAAQN,8DAAU2F,QAAQzB,aAAa,WAC/D0B,cAAe,CAEfA,cAAgBA,cAAcC,QAAQ,IAAK,UACrCC,WAAazC,UAAUK,yBAAkBkC,oBAC3CG,kBAASD,WAAY,CAACE,QAAQ,IAAQC,YAIzClB,iCAAiC1B,UAAWqC,YAAYQ,wCASxClG,OAAQD,8BAC7BA,MAAMK,sBACDwB,SAASuC,SAAS,wCAAcnE,OAAOM,QAAQiB,oDAAM,2BAStCvB,OAAQD,OAC5BA,MAAMK,sBACDwB,SAASuC,SAAS,YAAanE,OAAOM,QAAQ6F,QAASnG,OAAOM,QAAQ8F,WAAYpG,OAAOM,QAAQ+F,uCAS9ErG,OAAQD,aAC1BkC,WAAa3C,KAAK8B,cAAcpB,WACb,GAArBiC,WAAWf,iBAIfnB,MAAMK,kBAGkB6B,WAAWqE,MAAK9C,0CAC9Bf,YAAcnD,KAAKsC,SAASC,IAAI,UAAW2B,8CAClCf,YAAY8C,0DAAU,IACtBrE,QAAUuB,YAAY8D,YAAc9D,YAAY+D,6BAG1DC,uBAAuBxE,WAAYjC,YAIxC0G,SAAW,KACXlE,UAAY,QACS,GAArBP,WAAWf,OAAa,CACxBsB,UAAYlD,KAAKsC,SAASkB,gBAAgB,6BACpCL,YAAcnD,KAAKsC,SAASC,IAAI,UAAWI,WAAW,IAC5DyE,SAAWpH,KAAKsC,SAASkB,gBAAgB,qBAAsB,CAACpF,KAAM+E,YAAYG,aAElFJ,UAAYlD,KAAKsC,SAASkB,gBAAgB,wBAC1C4D,SAAWpH,KAAKsC,SAASkB,gBAAgB,sBAAuB,CAAC6D,MAAO1E,WAAWf,eAGjF6B,YAAczD,KAAK0D,0BAA0B4D,6BAAmB,CAClEhE,MAAOJ,UACPU,KAAMwD,WAGV3D,MAAM8D,UAAUC,GACZC,sBAAYC,QACZC,IAEIA,EAAE7G,iBACF2C,MAAMmE,eACDT,uBAAuBxE,WAAYjC,wCAWvBiC,WAAYjC,cAC/BV,KAAKsC,SAASuC,SAAS,gBAAiBlC,YAC1CjC,OAAOmH,QAAQC,SAAS,iBAExBC,OAAOC,SAASC,KAAOjI,KAAKsC,SAASC,IAAI,UAAU2F,yCAU3BxH,OAAQD,oDACVT,KAAKsC,SAAU5B,OAAQD,MAAO,2CASvBC,OAAQD,oDACfT,KAAKsC,SAAU5B,OAAQD,MAAO,wCAU/BC,OAAQD,MAAO0H,eACnCzH,OAAOM,QAAQiB,IAA6B,eAAvBvB,OAAOM,QAAQ2D,OAGzClE,MAAMK,iBACqB,eAAvBJ,OAAOM,QAAQ2D,SAEVrC,SAASuC,SAASsD,aAAcnI,KAAKsC,SAASC,IAAI,QAAQG,gBAE1DJ,SAASuC,SAASsD,aAAc,CAACzH,OAAOM,QAAQiB,gCAUnCvB,OAAQD,uCACxBuE,MAAQhF,KAAK8B,cAAcpB,WACb,GAAhBsE,MAAMpD,oBAGJsC,wCAAYxD,OAAOM,QAAQoC,iEAAa,KAC9C3C,MAAMK,sBACDwB,SAASuC,SAAS,cAAeG,MAAOd,kCAS1BxD,OAAQD,aACrBuE,MAAQhF,KAAK8B,cAAcpB,WACb,GAAhBsE,MAAMpD,cAIVnB,MAAMK,qBAEFsG,SAAW,KACXlE,UAAY,KACZkF,iBAAmB,QACH,GAAhBpD,MAAMpD,OAAa,OACbsD,OAASlF,KAAKsC,SAASC,IAAI,KAAMyC,MAAM,IACzCE,OAAOG,qBACP+C,iBAAmBlD,OAAOmD,kBAC1BnF,UAAYlD,KAAKsC,SAASkB,gBAAgB,4BAC1C4D,UAAW,kBACP,qBACA,oBACA,CACIkB,KAAMpD,OAAO2B,QACbzI,KAAM8G,OAAO9G,SAIrB8E,UAAYlD,KAAKsC,SAASkB,gBAAgB,kBAC1C4D,UAAW,kBACP,gBACA,oBACA,CACIkB,KAAMpD,OAAO2B,QACbzI,KAAM8G,OAAO9G,aAKzB8E,WAAY,kBAAU,kBAAmB,qBACzCkE,UAAW,kBACP,iBACA,oBACA,CAACC,MAAOrC,MAAMpD,eAIhB6B,YAAczD,KAAK0D,0BAA0B4D,6BAAmB,CAClEhE,MAAOJ,UACPU,KAAMwD,WAGV3D,MAAM8D,UAAUC,GACZC,sBAAYC,QACZC,OAEIA,EAAE7G,iBACF2C,MAAMmE,eACDtF,SAASuC,SAAS,WAAYG,OACf,GAAhBA,MAAMpD,QAAewG,kBAAoB1H,OAAOmH,QAAQC,SAAS,eAAgB,KAE7ES,WAAa,IAAIC,gBAAgBT,OAAOC,SAASS,QACjDF,WAAWG,IAAI,OAASH,WAAWhG,IAAI,OAAS6F,uBAC3CjB,uBAAuB,CAACiB,kBAAmB1H,yCAYvCA,cACnBsE,MAAQhF,KAAK8B,cAAcpB,WACb,GAAhBsE,MAAMpD,oBAKJoB,KAAO,CACT2F,aAFa3I,KAAKsC,SAASW,cAEJ2F,cAAc5I,KAAKsC,SAASxC,MAAOkF,QAExDvB,YAAczD,KAAK0D,0BAA0BmF,2BAAiB,CAChEvF,OAAO,kBAAU,eAAgB,QACjCM,KAAMC,mBAAUC,OAAO,uDAAwDd,MAC/E8F,gBAAgB,kBAAU,QAAS,eAGlCC,+BAA+BtF,MAAOuB,yCAQbtE,cACxBiC,WAAa3C,KAAK8B,cAAcpB,WACb,GAArBiC,WAAWf,oBAGT0B,MAA8B,GAArBX,WAAWf,OAAe,4BAA8B,6BAEjE6B,YAAczD,KAAK0D,0BAA0BmF,2BAAiB,CAChEvF,MAAOtD,KAAKsC,SAASkB,gBAAgBF,OACrCM,KAAMC,mBAAUC,OAAO,4DAA6D,IACpFgF,gBAAgB,kBAAU,QAAS,eAGlCC,+BAA+BtF,MAAOd,YAQ/CoG,+BAA+BtF,MAAO1B,KAElC0B,MAAMuF,kBAAkB,QAAQ,SAE1BC,eAAkBC,cACdC,SAAWD,MAAAA,aAAAA,MAAOE,cACnBD,gBAGA7G,SAASuC,SAASsE,SAAUpH,MAC1B,IAGLgC,WAAY,uBAASN,MAAMO,WACZD,UAAUsF,iBAAiBrJ,KAAK3B,UAAUU,cAClDkF,SAAQiF,QACjBA,MAAMnJ,iBAAiB,UAAU,KAC7B0D,MAAMuF,kBAAkB,QAAQ,MAEpCE,MAAMI,WAAWvJ,iBAAiB,SAAS,KACvCmJ,MAAMK,SAAU,EAChB9F,MAAMuF,kBAAkB,QAAQ,MAEpCE,MAAMI,WAAWvJ,iBAAiB,YAAYyJ,eACtCP,eAAeC,SACfM,aAAa1I,iBACb2C,MAAMmE,iBAKlBnE,MAAM8D,UAAUC,GACZC,sBAAYgC,MACZ,WACUP,MAAQnF,UAAUK,wBAAiBpE,KAAK3B,UAAUU,0BACxDkK,eAAeC,UAU3BzH,qBAAqBiI,QACD1J,KAAK2J,YAAY3J,KAAK3B,UAAUa,kBACxC+E,SAAQhE,UACZA,QAAQW,UAAU8F,OAAO1G,KAAKb,QAAQC,SAAUsK,cAC1CE,kBAAoB3J,QAAQmE,cAAcpE,KAAK3B,UAAUM,YAC/DiL,kBAAkBhJ,UAAU8F,OAAO1G,KAAKb,QAAQC,SAAUsK,aACrDG,iBAAiBD,kBAAmBF,QAErCA,2BACU,gBAAiB,qBACtBI,MAAMC,MAASH,kBAAkBI,aAAa,QAASD,QACvDE,MAAMC,sBAAaC,WACxBP,kBAAkBQ,MAAMC,cAAgB,KACxCT,kBAAkBQ,MAAME,WAAa,MAErCV,kBAAkBI,aAAa,QAASJ,kBAAkB5I,QAAQuJ,sBAGpEC,iBAAmBxK,KAAKyK,WAAWzK,KAAK3B,UAAUW,qBACpDwL,iBAAkB,CACCA,iBAAiBpG,cAAcpE,KAAK3B,UAAUM,YACtDiC,UAAU8F,OAAO1G,KAAKb,QAAQG,YAAaoK,QAC/Bc,iBAAiBpG,cAAcpE,KAAK3B,UAAUY,oBACtD2B,UAAU8F,OAAO1G,KAAKb,QAAQG,aAAcoK,SASnErF,aAAapE,SACLA,UACAA,QAAQmK,MAAMC,cAAgB,OAC9BpK,QAAQmK,MAAME,WAAa,OAC3BrK,QAAQW,UAAU8J,IAAI1K,KAAKb,QAAQC,UACnCa,QAAQW,UAAU8J,IAAI1K,KAAKb,QAAQE,QACnCY,QAAQ+J,aAAa,iBAAiB,GACtC/J,QAAQF,iBAAiB,SAASU,OAASA,MAAMK,oBAWzD4C,0BAA0BiH,WAAYC,oBAC3B,IAAIC,SAAQ,CAAC9F,QAAS+F,UACzBH,WAAWxM,OAAOyM,aAAad,MAAMrG,QACjCA,MAAMsH,kBAAiB,GAEvBtH,MAAM8D,UAAUC,GAAGC,sBAAYuD,cAAc,KACzCjG,QAAQtB,eAGuBtC,IAA/ByJ,YAAY9B,gBACZrF,MAAMwH,kBAAkBL,YAAY9B,qBAEH3H,IAAjCyJ,YAAYM,kBACZzH,MAAM0H,oBAAoBP,YAAY9B,gBAE1CrF,MAAMkD,UAEPsD,OAAM,KACLa,0CAaZhG,cAAcrB,MAAOxD,SACjBwD,MAAM2H,aACAC,eAAiB,IAAIxI,sDACvB5C,SACAA,QAAQqL,QAEZC,YAAW,KACP9H,MAAMmE,UACNyD,eAAetG,YAChB,KASPhC,6BAA6B9C,eACnBuL,WAAavL,QAAQU,QAAQX,KAAK3B,UAAUQ,eAC7C2M,kBAGEA,WAAWpH,cAAcpE,KAAK3B,UAAUS"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/contenttree.min.js b/course/format/amd/build/local/courseeditor/contenttree.min.js index 9b937c49e4975..33e32fc5064ec 100644 --- a/course/format/amd/build/local/courseeditor/contenttree.min.js +++ b/course/format/amd/build/local/courseeditor/contenttree.min.js @@ -1,4 +1,4 @@ -define("core_courseformat/local/courseeditor/contenttree",["exports","jquery","core/tree","core/normalise"],(function(_exports,_jquery,_tree,_normalise){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("core_courseformat/local/courseeditor/contenttree",["exports","theme_boost/bootstrap/collapse","jquery","core/tree","core/normalise"],(function(_exports,_collapse,_jquery,_tree,_normalise){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Course index keyboard navigation and aria-tree compatibility. * @@ -10,6 +10,6 @@ define("core_courseformat/local/courseeditor/contenttree",["exports","jquery","c * @class core_courseformat/local/courseeditor/contenttree * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),_tree=_interopRequireDefault(_tree);class _default extends _tree.default{constructor(mainElement,selectors,preventcache){var _selectors$ENTER;super(mainElement),this.selectors={SECTION:selectors.SECTION,TOGGLER:selectors.TOGGLER,COLLAPSE:selectors.COLLAPSE,ENTER:null!==(_selectors$ENTER=selectors.ENTER)&&void 0!==_selectors$ENTER?_selectors$ENTER:selectors.TOGGLER},preventcache&&(this._getVisibleItems=this.getVisibleItems,this.getVisibleItems=()=>(this.refreshVisibleItemsCache(),this._getVisibleItems())),this.treeRoot.on("hidden.bs.collapse shown.bs.collapse",(()=>{this.refreshVisibleItemsCache()})),this.registerEnterCallback(this.enterCallback.bind(this))}getActiveItem(){const activeItem=this.treeRoot.data("activeItem");if(activeItem)return(0,_normalise.getList)(activeItem)[0]}enterCallback(jQueryItem){const item=(0,_normalise.getList)(jQueryItem)[0];if(this.isGroupItem(jQueryItem)){const enter=item.querySelector(this.selectors.ENTER);"#"!==enter.getAttribute("href")&&(window.location.href=enter.getAttribute("href")),enter.click()}else{const link=item.querySelector("a");"#"!==link.getAttribute("href")?window.location.href=link.getAttribute("href"):link.click()}}handleItemClick(event,jQueryItem){event.target.closest(this.selectors.COLLAPSE)?super.handleItemClick(event,jQueryItem):(jQueryItem.focus(),this.isGroupItem(jQueryItem)&&this.expandGroup(jQueryItem))}isGroupCollapsed(jQueryItem){return"false"===(0,_normalise.getList)(jQueryItem)[0].querySelector("[aria-expanded]").getAttribute("aria-expanded")}toggleGroup(item){var _toggler$data;const toggler=item.find(this.selectors.COLLAPSE);let collapsibleId=null!==(_toggler$data=toggler.data("target"))&&void 0!==_toggler$data?_toggler$data:toggler.attr("href");if(!collapsibleId)return;collapsibleId=collapsibleId.replace("#","");(0,_jquery.default)("#".concat(collapsibleId)).length&&(0,_jquery.default)("#".concat(collapsibleId)).collapse("toggle")}expandGroup(item){this.isGroupCollapsed(item)&&this.toggleGroup(item)}collapseGroup(item){this.isGroupCollapsed(item)||this.toggleGroup(item)}expandAllGroups(){(0,_normalise.getList)(this.treeRoot)[0].querySelectorAll(this.selectors.SECTION).forEach((item=>{this.expandGroup((0,_jquery.default)(item))}))}}return _exports.default=_default,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_collapse=_interopRequireDefault(_collapse),_jquery=_interopRequireDefault(_jquery),_tree=_interopRequireDefault(_tree);class _default extends _tree.default{constructor(mainElement,selectors,preventcache){var _selectors$ENTER;super(mainElement),this.selectors={SECTION:selectors.SECTION,TOGGLER:selectors.TOGGLER,COLLAPSE:selectors.COLLAPSE,ENTER:null!==(_selectors$ENTER=selectors.ENTER)&&void 0!==_selectors$ENTER?_selectors$ENTER:selectors.TOGGLER},preventcache&&(this._getVisibleItems=this.getVisibleItems,this.getVisibleItems=()=>(this.refreshVisibleItemsCache(),this._getVisibleItems())),this.treeRoot[0].addEventListener("hidden.bs.collapse shown.bs.collapse",(()=>{this.refreshVisibleItemsCache()})),this.registerEnterCallback(this.enterCallback.bind(this))}getActiveItem(){const activeItem=this.treeRoot.data("activeItem");if(activeItem)return(0,_normalise.getList)(activeItem)[0]}enterCallback(jQueryItem){const item=(0,_normalise.getList)(jQueryItem)[0];if(this.isGroupItem(jQueryItem)){const enter=item.querySelector(this.selectors.ENTER);"#"!==enter.getAttribute("href")&&(window.location.href=enter.getAttribute("href")),enter.click()}else{const link=item.querySelector("a");"#"!==link.getAttribute("href")?window.location.href=link.getAttribute("href"):link.click()}}handleItemClick(event,jQueryItem){event.target.closest(this.selectors.COLLAPSE)?super.handleItemClick(event,jQueryItem):(jQueryItem.focus(),this.isGroupItem(jQueryItem)&&this.expandGroup(jQueryItem))}isGroupCollapsed(jQueryItem){return"false"===(0,_normalise.getList)(jQueryItem)[0].querySelector("[aria-expanded]").getAttribute("aria-expanded")}toggleGroup(item){var _toggler$dataset$targ,_toggler$dataset;const toggler=item[0].querySelector(this.selectors.COLLAPSE);let collapsibleId=null!==(_toggler$dataset$targ=null===(_toggler$dataset=toggler.dataset)||void 0===_toggler$dataset?void 0:_toggler$dataset.target)&&void 0!==_toggler$dataset$targ?_toggler$dataset$targ:toggler.getAttribute("href");if(!collapsibleId)return;collapsibleId=collapsibleId.replace("#","");const collapsible=document.getElementById(collapsibleId);collapsible&&_collapse.default.getOrCreateInstance(collapsible).toggle()}expandGroup(item){this.isGroupCollapsed(item)&&this.toggleGroup(item)}collapseGroup(item){this.isGroupCollapsed(item)||this.toggleGroup(item)}expandAllGroups(){(0,_normalise.getList)(this.treeRoot)[0].querySelectorAll(this.selectors.SECTION).forEach((item=>{this.expandGroup((0,_jquery.default)(item))}))}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=contenttree.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/contenttree.min.js.map b/course/format/amd/build/local/courseeditor/contenttree.min.js.map index 1196bd4aa4752..e9cbb25cfe4ab 100644 --- a/course/format/amd/build/local/courseeditor/contenttree.min.js.map +++ b/course/format/amd/build/local/courseeditor/contenttree.min.js.map @@ -1 +1 @@ -{"version":3,"file":"contenttree.min.js","sources":["../../../src/local/courseeditor/contenttree.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 * Course index keyboard navigation and aria-tree compatibility.\n *\n * Node tree and bootstrap collapsibles don't use the same HTML structure. However,\n * all keybindings and logic is compatible. This class translate the primitive opetations\n * to a bootstrap collapsible structure.\n *\n * @module core_courseformat/local/courseeditor/contenttree\n * @class core_courseformat/local/courseeditor/contenttree\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n// The core/tree uses jQuery to expand all nodes.\nimport jQuery from 'jquery';\nimport Tree from 'core/tree';\nimport {getList} from 'core/normalise';\n\nexport default class extends Tree {\n\n /**\n * Setup the core/tree keyboard navigation.\n *\n * @param {Element|undefined} mainElement an alternative main element in case it is not from the parent component\n * @param {Object|undefined} selectors alternative selectors\n * @param {boolean} preventcache if the elements cache must be disabled.\n */\n constructor(mainElement, selectors, preventcache) {\n // Init this value with the parent DOM element.\n super(mainElement);\n\n // Get selectors from parent.\n this.selectors = {\n SECTION: selectors.SECTION,\n TOGGLER: selectors.TOGGLER,\n COLLAPSE: selectors.COLLAPSE,\n ENTER: selectors.ENTER ?? selectors.TOGGLER,\n };\n\n // The core/tree library saves the visible elements cache inside the main tree node.\n // However, in edit mode content can change suddenly so we need to refresh caches when needed.\n if (preventcache) {\n this._getVisibleItems = this.getVisibleItems;\n this.getVisibleItems = () => {\n this.refreshVisibleItemsCache();\n return this._getVisibleItems();\n };\n }\n // All jQuery events can be replaced when MDL-71979 is integrated.\n this.treeRoot.on('hidden.bs.collapse shown.bs.collapse', () => {\n this.refreshVisibleItemsCache();\n });\n // Register a custom callback for pressing enter key.\n this.registerEnterCallback(this.enterCallback.bind(this));\n }\n\n /**\n * Return the current active node.\n *\n * @return {Element|undefined} the active item if any\n */\n getActiveItem() {\n const activeItem = this.treeRoot.data('activeItem');\n if (activeItem) {\n return getList(activeItem)[0];\n }\n return undefined;\n }\n\n /**\n * Handle enter key on a collpasible node.\n *\n * @param {JQuery} jQueryItem the jQuery object\n */\n enterCallback(jQueryItem) {\n const item = getList(jQueryItem)[0];\n if (this.isGroupItem(jQueryItem)) {\n // Group elements is like clicking a topic but without loosing the focus.\n const enter = item.querySelector(this.selectors.ENTER);\n if (enter.getAttribute('href') !== '#') {\n window.location.href = enter.getAttribute('href');\n }\n enter.click();\n } else {\n // Activity links just follow the link href.\n const link = item.querySelector('a');\n if (link.getAttribute('href') !== '#') {\n window.location.href = link.getAttribute('href');\n } else {\n link.click();\n }\n return;\n }\n }\n\n /**\n * Handle an item click.\n *\n * @param {Event} event the click event\n * @param {jQuery} jQueryItem the item clicked\n */\n handleItemClick(event, jQueryItem) {\n const isChevron = event.target.closest(this.selectors.COLLAPSE);\n // Only chevron clicks toogle the sections always.\n if (isChevron) {\n super.handleItemClick(event, jQueryItem);\n return;\n }\n // This is a title or activity name click.\n jQueryItem.focus();\n if (this.isGroupItem(jQueryItem)) {\n this.expandGroup(jQueryItem);\n }\n }\n\n /**\n * Check if a gorup item is collapsed.\n *\n * @param {JQuery} jQueryItem the jQuery object\n * @returns {boolean} if the element is collapsed\n */\n isGroupCollapsed(jQueryItem) {\n const item = getList(jQueryItem)[0];\n const toggler = item.querySelector(`[aria-expanded]`);\n return toggler.getAttribute('aria-expanded') === 'false';\n }\n\n /**\n * Toggle a group item.\n *\n * @param {JQuery} item the jQuery object\n */\n toggleGroup(item) {\n // All jQuery in this segment of code can be replaced when MDL-71979 is integrated.\n const toggler = item.find(this.selectors.COLLAPSE);\n let collapsibleId = toggler.data('target') ?? toggler.attr('href');\n if (!collapsibleId) {\n return;\n }\n collapsibleId = collapsibleId.replace('#', '');\n\n // Bootstrap 4 uses jQuery to interact with collapsibles.\n const collapsible = jQuery(`#${collapsibleId}`);\n if (collapsible.length) {\n jQuery(`#${collapsibleId}`).collapse('toggle');\n }\n }\n\n /**\n * Expand a group item.\n *\n * @param {JQuery} item the jQuery object\n */\n expandGroup(item) {\n if (this.isGroupCollapsed(item)) {\n this.toggleGroup(item);\n }\n }\n\n /**\n * Collpase a group item.\n *\n * @param {JQuery} item the jQuery object\n */\n collapseGroup(item) {\n if (!this.isGroupCollapsed(item)) {\n this.toggleGroup(item);\n }\n }\n\n /**\n * Expand all groups.\n */\n expandAllGroups() {\n const togglers = getList(this.treeRoot)[0].querySelectorAll(this.selectors.SECTION);\n togglers.forEach(item => {\n this.expandGroup(jQuery(item));\n });\n }\n}\n"],"names":["Tree","constructor","mainElement","selectors","preventcache","SECTION","TOGGLER","COLLAPSE","ENTER","_getVisibleItems","this","getVisibleItems","refreshVisibleItemsCache","treeRoot","on","registerEnterCallback","enterCallback","bind","getActiveItem","activeItem","data","jQueryItem","item","isGroupItem","enter","querySelector","getAttribute","window","location","href","click","link","handleItemClick","event","target","closest","focus","expandGroup","isGroupCollapsed","toggleGroup","toggler","find","collapsibleId","attr","replace","length","collapse","collapseGroup","expandAllGroups","querySelectorAll","forEach"],"mappings":";;;;;;;;;;;;wLAiC6BA,cASzBC,YAAYC,YAAaC,UAAWC,yCAE1BF,kBAGDC,UAAY,CACbE,QAASF,UAAUE,QACnBC,QAASH,UAAUG,QACnBC,SAAUJ,UAAUI,SACpBC,+BAAOL,UAAUK,mDAASL,UAAUG,SAKpCF,oBACKK,iBAAmBC,KAAKC,qBACxBA,gBAAkB,UACdC,2BACEF,KAAKD,0BAIfI,SAASC,GAAG,wCAAwC,UAChDF,mCAGJG,sBAAsBL,KAAKM,cAAcC,KAAKP,OAQvDQ,sBACUC,WAAaT,KAAKG,SAASO,KAAK,iBAClCD,kBACO,sBAAQA,YAAY,GAUnCH,cAAcK,kBACJC,MAAO,sBAAQD,YAAY,MAC7BX,KAAKa,YAAYF,YAAa,OAExBG,MAAQF,KAAKG,cAAcf,KAAKP,UAAUK,OACb,MAA/BgB,MAAME,aAAa,UACnBC,OAAOC,SAASC,KAAOL,MAAME,aAAa,SAE9CF,MAAMM,mBAGAC,KAAOT,KAAKG,cAAc,KACE,MAA9BM,KAAKL,aAAa,QAClBC,OAAOC,SAASC,KAAOE,KAAKL,aAAa,QAEzCK,KAAKD,SAYjBE,gBAAgBC,MAAOZ,YACDY,MAAMC,OAAOC,QAAQzB,KAAKP,UAAUI,gBAG5CyB,gBAAgBC,MAAOZ,aAIjCA,WAAWe,QACP1B,KAAKa,YAAYF,kBACZgB,YAAYhB,aAUzBiB,iBAAiBjB,kBAGoC,WAFpC,sBAAQA,YAAY,GACZI,iCACNC,aAAa,iBAQhCa,YAAYjB,8BAEFkB,QAAUlB,KAAKmB,KAAK/B,KAAKP,UAAUI,cACrCmC,oCAAgBF,QAAQpB,KAAK,iDAAaoB,QAAQG,KAAK,YACtDD,qBAGLA,cAAgBA,cAAcE,QAAQ,IAAK,KAGvB,8BAAWF,gBACfG,uCACDH,gBAAiBI,SAAS,UAS7CT,YAAYf,MACJZ,KAAK4B,iBAAiBhB,YACjBiB,YAAYjB,MASzByB,cAAczB,MACLZ,KAAK4B,iBAAiBhB,YAClBiB,YAAYjB,MAOzB0B,mBACqB,sBAAQtC,KAAKG,UAAU,GAAGoC,iBAAiBvC,KAAKP,UAAUE,SAClE6C,SAAQ5B,YACRe,aAAY,mBAAOf"} \ No newline at end of file +{"version":3,"file":"contenttree.min.js","sources":["../../../src/local/courseeditor/contenttree.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 * Course index keyboard navigation and aria-tree compatibility.\n *\n * Node tree and bootstrap collapsibles don't use the same HTML structure. However,\n * all keybindings and logic is compatible. This class translate the primitive opetations\n * to a bootstrap collapsible structure.\n *\n * @module core_courseformat/local/courseeditor/contenttree\n * @class core_courseformat/local/courseeditor/contenttree\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n// The core/tree uses jQuery to expand all nodes.\nimport Collapse from 'theme_boost/bootstrap/collapse';\nimport jQuery from 'jquery';\nimport Tree from 'core/tree';\nimport {getList} from 'core/normalise';\n\nexport default class extends Tree {\n\n /**\n * Setup the core/tree keyboard navigation.\n *\n * @param {Element|undefined} mainElement an alternative main element in case it is not from the parent component\n * @param {Object|undefined} selectors alternative selectors\n * @param {boolean} preventcache if the elements cache must be disabled.\n */\n constructor(mainElement, selectors, preventcache) {\n // Init this value with the parent DOM element.\n super(mainElement);\n\n // Get selectors from parent.\n this.selectors = {\n SECTION: selectors.SECTION,\n TOGGLER: selectors.TOGGLER,\n COLLAPSE: selectors.COLLAPSE,\n ENTER: selectors.ENTER ?? selectors.TOGGLER,\n };\n\n // The core/tree library saves the visible elements cache inside the main tree node.\n // However, in edit mode content can change suddenly so we need to refresh caches when needed.\n if (preventcache) {\n this._getVisibleItems = this.getVisibleItems;\n this.getVisibleItems = () => {\n this.refreshVisibleItemsCache();\n return this._getVisibleItems();\n };\n }\n this.treeRoot[0].addEventListener('hidden.bs.collapse shown.bs.collapse', () => {\n this.refreshVisibleItemsCache();\n });\n // Register a custom callback for pressing enter key.\n this.registerEnterCallback(this.enterCallback.bind(this));\n }\n\n /**\n * Return the current active node.\n *\n * @return {Element|undefined} the active item if any\n */\n getActiveItem() {\n const activeItem = this.treeRoot.data('activeItem');\n if (activeItem) {\n return getList(activeItem)[0];\n }\n return undefined;\n }\n\n /**\n * Handle enter key on a collpasible node.\n *\n * @param {JQuery} jQueryItem the jQuery object\n */\n enterCallback(jQueryItem) {\n const item = getList(jQueryItem)[0];\n if (this.isGroupItem(jQueryItem)) {\n // Group elements is like clicking a topic but without loosing the focus.\n const enter = item.querySelector(this.selectors.ENTER);\n if (enter.getAttribute('href') !== '#') {\n window.location.href = enter.getAttribute('href');\n }\n enter.click();\n } else {\n // Activity links just follow the link href.\n const link = item.querySelector('a');\n if (link.getAttribute('href') !== '#') {\n window.location.href = link.getAttribute('href');\n } else {\n link.click();\n }\n return;\n }\n }\n\n /**\n * Handle an item click.\n *\n * @param {Event} event the click event\n * @param {jQuery} jQueryItem the item clicked\n */\n handleItemClick(event, jQueryItem) {\n const isChevron = event.target.closest(this.selectors.COLLAPSE);\n // Only chevron clicks toogle the sections always.\n if (isChevron) {\n super.handleItemClick(event, jQueryItem);\n return;\n }\n // This is a title or activity name click.\n jQueryItem.focus();\n if (this.isGroupItem(jQueryItem)) {\n this.expandGroup(jQueryItem);\n }\n }\n\n /**\n * Check if a gorup item is collapsed.\n *\n * @param {JQuery} jQueryItem the jQuery object\n * @returns {boolean} if the element is collapsed\n */\n isGroupCollapsed(jQueryItem) {\n const item = getList(jQueryItem)[0];\n const toggler = item.querySelector(`[aria-expanded]`);\n return toggler.getAttribute('aria-expanded') === 'false';\n }\n\n /**\n * Toggle a group item.\n *\n * @param {JQuery} item the jQuery object\n */\n toggleGroup(item) {\n const toggler = item[0].querySelector(this.selectors.COLLAPSE);\n let collapsibleId = toggler.dataset?.target ?? toggler.getAttribute('href');\n if (!collapsibleId) {\n return;\n }\n collapsibleId = collapsibleId.replace('#', '');\n\n const collapsible = document.getElementById(collapsibleId);\n if (collapsible) {\n Collapse.getOrCreateInstance(collapsible).toggle();\n }\n }\n\n /**\n * Expand a group item.\n *\n * @param {JQuery} item the jQuery object\n */\n expandGroup(item) {\n if (this.isGroupCollapsed(item)) {\n this.toggleGroup(item);\n }\n }\n\n /**\n * Collpase a group item.\n *\n * @param {JQuery} item the jQuery object\n */\n collapseGroup(item) {\n if (!this.isGroupCollapsed(item)) {\n this.toggleGroup(item);\n }\n }\n\n /**\n * Expand all groups.\n */\n expandAllGroups() {\n const togglers = getList(this.treeRoot)[0].querySelectorAll(this.selectors.SECTION);\n togglers.forEach(item => {\n this.expandGroup(jQuery(item));\n });\n }\n}\n"],"names":["Tree","constructor","mainElement","selectors","preventcache","SECTION","TOGGLER","COLLAPSE","ENTER","_getVisibleItems","this","getVisibleItems","refreshVisibleItemsCache","treeRoot","addEventListener","registerEnterCallback","enterCallback","bind","getActiveItem","activeItem","data","jQueryItem","item","isGroupItem","enter","querySelector","getAttribute","window","location","href","click","link","handleItemClick","event","target","closest","focus","expandGroup","isGroupCollapsed","toggleGroup","toggler","collapsibleId","dataset","_toggler$dataset","replace","collapsible","document","getElementById","getOrCreateInstance","toggle","collapseGroup","expandAllGroups","querySelectorAll","forEach"],"mappings":";;;;;;;;;;;;oOAkC6BA,cASzBC,YAAYC,YAAaC,UAAWC,yCAE1BF,kBAGDC,UAAY,CACbE,QAASF,UAAUE,QACnBC,QAASH,UAAUG,QACnBC,SAAUJ,UAAUI,SACpBC,+BAAOL,UAAUK,mDAASL,UAAUG,SAKpCF,oBACKK,iBAAmBC,KAAKC,qBACxBA,gBAAkB,UACdC,2BACEF,KAAKD,0BAGfI,SAAS,GAAGC,iBAAiB,wCAAwC,UACjEF,mCAGJG,sBAAsBL,KAAKM,cAAcC,KAAKP,OAQvDQ,sBACUC,WAAaT,KAAKG,SAASO,KAAK,iBAClCD,kBACO,sBAAQA,YAAY,GAUnCH,cAAcK,kBACJC,MAAO,sBAAQD,YAAY,MAC7BX,KAAKa,YAAYF,YAAa,OAExBG,MAAQF,KAAKG,cAAcf,KAAKP,UAAUK,OACb,MAA/BgB,MAAME,aAAa,UACnBC,OAAOC,SAASC,KAAOL,MAAME,aAAa,SAE9CF,MAAMM,mBAGAC,KAAOT,KAAKG,cAAc,KACE,MAA9BM,KAAKL,aAAa,QAClBC,OAAOC,SAASC,KAAOE,KAAKL,aAAa,QAEzCK,KAAKD,SAYjBE,gBAAgBC,MAAOZ,YACDY,MAAMC,OAAOC,QAAQzB,KAAKP,UAAUI,gBAG5CyB,gBAAgBC,MAAOZ,aAIjCA,WAAWe,QACP1B,KAAKa,YAAYF,kBACZgB,YAAYhB,aAUzBiB,iBAAiBjB,kBAGoC,WAFpC,sBAAQA,YAAY,GACZI,iCACNC,aAAa,iBAQhCa,YAAYjB,uDACFkB,QAAUlB,KAAK,GAAGG,cAAcf,KAAKP,UAAUI,cACjDkC,qEAAgBD,QAAQE,2CAARC,iBAAiBT,8DAAUM,QAAQd,aAAa,YAC/De,qBAGLA,cAAgBA,cAAcG,QAAQ,IAAK,UAErCC,YAAcC,SAASC,eAAeN,eACxCI,+BACSG,oBAAoBH,aAAaI,SASlDZ,YAAYf,MACJZ,KAAK4B,iBAAiBhB,YACjBiB,YAAYjB,MASzB4B,cAAc5B,MACLZ,KAAK4B,iBAAiBhB,YAClBiB,YAAYjB,MAOzB6B,mBACqB,sBAAQzC,KAAKG,UAAU,GAAGuC,iBAAiB1C,KAAKP,UAAUE,SAClEgD,SAAQ/B,YACRe,aAAY,mBAAOf"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseindex/courseindex.min.js b/course/format/amd/build/local/courseindex/courseindex.min.js index fe7f576a9b229..1f90ffda7c391 100644 --- a/course/format/amd/build/local/courseindex/courseindex.min.js +++ b/course/format/amd/build/local/courseindex/courseindex.min.js @@ -1,4 +1,4 @@ -define("core_courseformat/local/courseindex/courseindex",["exports","core/reactive","core_courseformat/courseeditor","jquery","core_courseformat/local/courseeditor/contenttree"],(function(_exports,_reactive,_courseeditor,_jquery,_contenttree){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("core_courseformat/local/courseindex/courseindex",["exports","core/reactive","theme_boost/bootstrap/collapse","core_courseformat/courseeditor","core_courseformat/local/courseeditor/contenttree"],(function(_exports,_reactive,_collapse,_courseeditor,_contenttree){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Course index main component. * @@ -6,6 +6,6 @@ define("core_courseformat/local/courseindex/courseindex",["exports","core/reacti * @class core_courseformat/local/courseindex/courseindex * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),_contenttree=_interopRequireDefault(_contenttree);class Component extends _reactive.BaseComponent{create(){this.name="courseindex",this.selectors={SECTION:"[data-for='section']",SECTION_CMLIST:"[data-for='cmlist']",CM:"[data-for='cm']",TOGGLER:'[data-action="togglecourseindexsection"]',COLLAPSE:'[data-bs-toggle="collapse"]',DRAWER:".drawer"},this.classes={SECTIONHIDDEN:"dimmed",CMHIDDEN:"dimmed",SECTIONCURRENT:"current",COLLAPSED:"collapsed",SHOW:"show"},this.sections={},this.cms={}}static init(target,selectors){return new this({element:document.getElementById(target),reactive:(0,_courseeditor.getCurrentCourseEditor)(),selectors:selectors})}stateReady(state){this.addEventListener(this.element,"click",this._sectionTogglers);this.getElements(this.selectors.SECTION).forEach((section=>{this.sections[section.dataset.id]=section}));this.getElements(this.selectors.CM).forEach((cm=>{this.cms[cm.dataset.id]=cm})),this._expandPageCmSectionIfNecessary(state),this._refreshPageItem({element:state.course,state:state}),this.contentTree=new _contenttree.default(this.element,this.selectors,this.reactive.isEditing)}getWatchers(){return[{watch:"section.indexcollapsed:updated",handler:this._refreshSectionCollapsed},{watch:"cm:created",handler:this._createCm},{watch:"cm:deleted",handler:this._deleteCm},{watch:"section:created",handler:this._createSection},{watch:"section:deleted",handler:this._deleteSection},{watch:"course.pageItem:created",handler:this._refreshPageItem},{watch:"course.pageItem:updated",handler:this._refreshPageItem},{watch:"course.sectionlist:updated",handler:this._refreshCourseSectionlist},{watch:"section.cmlist:updated",handler:this._refreshSectionCmlist}]}_sectionTogglers(event){const sectionlink=event.target.closest(this.selectors.TOGGLER),isChevron=event.target.closest(this.selectors.COLLAPSE);if(sectionlink||isChevron){var _toggler$classList$co;const section=event.target.closest(this.selectors.SECTION),toggler=section.querySelector(this.selectors.COLLAPSE),isCollapsed=null!==(_toggler$classList$co=null==toggler?void 0:toggler.classList.contains(this.classes.COLLAPSED))&&void 0!==_toggler$classList$co&&_toggler$classList$co,sectionId=section.getAttribute("data-id");sectionlink&&!isCollapsed||this.reactive.dispatch("sectionIndexCollapsed",[sectionId],!isCollapsed)}}_refreshSectionCollapsed(_ref){var _toggler$classList$co2;let{element:element}=_ref;const target=this.getElement(this.selectors.SECTION,element.id);if(!target)throw new Error("Unkown section with ID ".concat(element.id));const toggler=target.querySelector(this.selectors.COLLAPSE),isCollapsed=null!==(_toggler$classList$co2=null==toggler?void 0:toggler.classList.contains(this.classes.COLLAPSED))&&void 0!==_toggler$classList$co2&&_toggler$classList$co2;element.indexcollapsed!==isCollapsed&&this._expandSectionNode(element)}_expandSectionNode(element,forceValue){var _toggler$dataset$targ;const toggler=this.getElement(this.selectors.SECTION,element.id).querySelector(this.selectors.COLLAPSE);let collapsibleId=null!==(_toggler$dataset$targ=toggler.dataset.target)&&void 0!==_toggler$dataset$targ?_toggler$dataset$targ:toggler.getAttribute("href");if(!collapsibleId)return;collapsibleId=collapsibleId.replace("#","");const collapsible=document.getElementById(collapsibleId);if(!collapsible)return;void 0===forceValue&&(forceValue=!element.indexcollapsed);const togglerValue=forceValue?"show":"hide";(0,_jquery.default)(collapsible).collapse(togglerValue)}_refreshPageItem(_ref2){var _element$pageItem;let{element:element,state:state}=_ref2;if(null==element||null===(_element$pageItem=element.pageItem)||void 0===_element$pageItem||!_element$pageItem.isStatic||"cm"!=element.pageItem.type)return;const section=state.section.get(element.pageItem.sectionId);section.indexcollapsed&&(this._expandSectionNode(section,!0),setTimeout((()=>{var _this$cms$element$pag;return null===(_this$cms$element$pag=this.cms[element.pageItem.id])||void 0===_this$cms$element$pag?void 0:_this$cms$element$pag.scrollIntoView({block:"nearest"})}),250))}_expandPageCmSectionIfNecessary(state){const pageCmInfo=this.reactive.getPageAnchorCmInfo();pageCmInfo&&this._expandSectionNode(state.section.get(pageCmInfo.sectionid),!0)}async _createCm(_ref3){let{state:state,element:element}=_ref3;const fakeelement=document.createElement("li");fakeelement.classList.add("bg-pulse-grey","w-100"),fakeelement.innerHTML=" ",this.cms[element.id]=fakeelement,this._refreshSectionCmlist({state:state,element:state.section.get(element.sectionid)});const data=this.reactive.getExporter().cm(state,element),newelement=(await this.renderComponent(fakeelement,"core_courseformat/local/courseindex/cm",data)).getElement();this.cms[element.id]=newelement,fakeelement.parentNode.replaceChild(newelement,fakeelement)}async _createSection(_ref4){let{state:state,element:element}=_ref4;const fakeelement=document.createElement("div");fakeelement.classList.add("bg-pulse-grey","w-100"),fakeelement.innerHTML=" ",this.sections[element.id]=fakeelement,this._refreshCourseSectionlist({state:state,element:state.course});const data=this.reactive.getExporter().section(state,element),newelement=(await this.renderComponent(fakeelement,"core_courseformat/local/courseindex/section",data)).getElement();this.sections[element.id]=newelement,fakeelement.parentNode.replaceChild(newelement,fakeelement)}_refreshSectionCmlist(_ref5){var _element$cmlist;let{element:element}=_ref5;const cmlist=null!==(_element$cmlist=element.cmlist)&&void 0!==_element$cmlist?_element$cmlist:[],listparent=this.getElement(this.selectors.SECTION_CMLIST,element.id);listparent&&this._fixOrder(listparent,cmlist,this.cms)}_refreshCourseSectionlist(_ref6){let{state:state}=_ref6;const sectionlist=this.reactive.getExporter().listedSectionIds(state);this._fixOrder(this.element,sectionlist,this.sections)}_fixOrder(container,neworder,allitems){if(!neworder.length)return container.classList.add("hidden"),void(container.innerHTML="");for(container.classList.remove("hidden"),neworder.forEach(((itemid,index)=>{const item=allitems[itemid],currentitem=container.children[index];void 0!==currentitem||null==item?currentitem!==item&&item&&container.insertBefore(item,currentitem):container.append(item)}));container.children.length>neworder.length;)container.removeChild(container.lastChild)}_deleteCm(_ref7){let{element:element}=_ref7;delete this.cms[element.id]}_deleteSection(_ref8){let{element:element}=_ref8;delete this.sections[element.id]}}return _exports.default=Component,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_collapse=_interopRequireDefault(_collapse),_contenttree=_interopRequireDefault(_contenttree);class Component extends _reactive.BaseComponent{create(){this.name="courseindex",this.selectors={SECTION:"[data-for='section']",SECTION_CMLIST:"[data-for='cmlist']",CM:"[data-for='cm']",TOGGLER:'[data-action="togglecourseindexsection"]',COLLAPSE:'[data-bs-toggle="collapse"]',DRAWER:".drawer"},this.classes={SECTIONHIDDEN:"dimmed",CMHIDDEN:"dimmed",SECTIONCURRENT:"current",COLLAPSED:"collapsed",SHOW:"show"},this.sections={},this.cms={}}static init(target,selectors){return new this({element:document.getElementById(target),reactive:(0,_courseeditor.getCurrentCourseEditor)(),selectors:selectors})}stateReady(state){this.addEventListener(this.element,"click",this._sectionTogglers);this.getElements(this.selectors.SECTION).forEach((section=>{this.sections[section.dataset.id]=section}));this.getElements(this.selectors.CM).forEach((cm=>{this.cms[cm.dataset.id]=cm})),this._expandPageCmSectionIfNecessary(state),this._refreshPageItem({element:state.course,state:state}),this.contentTree=new _contenttree.default(this.element,this.selectors,this.reactive.isEditing)}getWatchers(){return[{watch:"section.indexcollapsed:updated",handler:this._refreshSectionCollapsed},{watch:"cm:created",handler:this._createCm},{watch:"cm:deleted",handler:this._deleteCm},{watch:"section:created",handler:this._createSection},{watch:"section:deleted",handler:this._deleteSection},{watch:"course.pageItem:created",handler:this._refreshPageItem},{watch:"course.pageItem:updated",handler:this._refreshPageItem},{watch:"course.sectionlist:updated",handler:this._refreshCourseSectionlist},{watch:"section.cmlist:updated",handler:this._refreshSectionCmlist}]}_sectionTogglers(event){const sectionlink=event.target.closest(this.selectors.TOGGLER),isChevron=event.target.closest(this.selectors.COLLAPSE);if(sectionlink||isChevron){var _toggler$classList$co;const section=event.target.closest(this.selectors.SECTION),toggler=section.querySelector(this.selectors.COLLAPSE),isCollapsed=null!==(_toggler$classList$co=null==toggler?void 0:toggler.classList.contains(this.classes.COLLAPSED))&&void 0!==_toggler$classList$co&&_toggler$classList$co,sectionId=section.getAttribute("data-id");sectionlink&&!isCollapsed||this.reactive.dispatch("sectionIndexCollapsed",[sectionId],!isCollapsed)}}_refreshSectionCollapsed(_ref){var _toggler$classList$co2;let{element:element}=_ref;const target=this.getElement(this.selectors.SECTION,element.id);if(!target)throw new Error("Unkown section with ID ".concat(element.id));const toggler=target.querySelector(this.selectors.COLLAPSE),isCollapsed=null!==(_toggler$classList$co2=null==toggler?void 0:toggler.classList.contains(this.classes.COLLAPSED))&&void 0!==_toggler$classList$co2&&_toggler$classList$co2;element.indexcollapsed!==isCollapsed&&this._expandSectionNode(element)}_expandSectionNode(element,forceValue){var _toggler$dataset$targ;const toggler=this.getElement(this.selectors.SECTION,element.id).querySelector(this.selectors.COLLAPSE);let collapsibleId=null!==(_toggler$dataset$targ=toggler.dataset.target)&&void 0!==_toggler$dataset$targ?_toggler$dataset$targ:toggler.getAttribute("href");if(!collapsibleId)return;collapsibleId=collapsibleId.replace("#","");const collapsible=document.getElementById(collapsibleId);collapsible&&(void 0===forceValue&&(forceValue=!element.indexcollapsed),forceValue?_collapse.default.getOrCreateInstance(collapsible,{toggle:!1}).show():_collapse.default.getOrCreateInstance(collapsible,{toggle:!1}).hide())}_refreshPageItem(_ref2){var _element$pageItem;let{element:element,state:state}=_ref2;if(null==element||null===(_element$pageItem=element.pageItem)||void 0===_element$pageItem||!_element$pageItem.isStatic||"cm"!=element.pageItem.type)return;const section=state.section.get(element.pageItem.sectionId);section.indexcollapsed&&(this._expandSectionNode(section,!0),setTimeout((()=>{var _this$cms$element$pag;return null===(_this$cms$element$pag=this.cms[element.pageItem.id])||void 0===_this$cms$element$pag?void 0:_this$cms$element$pag.scrollIntoView({block:"nearest"})}),250))}_expandPageCmSectionIfNecessary(state){const pageCmInfo=this.reactive.getPageAnchorCmInfo();pageCmInfo&&this._expandSectionNode(state.section.get(pageCmInfo.sectionid),!0)}async _createCm(_ref3){let{state:state,element:element}=_ref3;const fakeelement=document.createElement("li");fakeelement.classList.add("bg-pulse-grey","w-100"),fakeelement.innerHTML=" ",this.cms[element.id]=fakeelement,this._refreshSectionCmlist({state:state,element:state.section.get(element.sectionid)});const data=this.reactive.getExporter().cm(state,element),newelement=(await this.renderComponent(fakeelement,"core_courseformat/local/courseindex/cm",data)).getElement();this.cms[element.id]=newelement,fakeelement.parentNode.replaceChild(newelement,fakeelement)}async _createSection(_ref4){let{state:state,element:element}=_ref4;const fakeelement=document.createElement("div");fakeelement.classList.add("bg-pulse-grey","w-100"),fakeelement.innerHTML=" ",this.sections[element.id]=fakeelement,this._refreshCourseSectionlist({state:state,element:state.course});const data=this.reactive.getExporter().section(state,element),newelement=(await this.renderComponent(fakeelement,"core_courseformat/local/courseindex/section",data)).getElement();this.sections[element.id]=newelement,fakeelement.parentNode.replaceChild(newelement,fakeelement)}_refreshSectionCmlist(_ref5){var _element$cmlist;let{element:element}=_ref5;const cmlist=null!==(_element$cmlist=element.cmlist)&&void 0!==_element$cmlist?_element$cmlist:[],listparent=this.getElement(this.selectors.SECTION_CMLIST,element.id);listparent&&this._fixOrder(listparent,cmlist,this.cms)}_refreshCourseSectionlist(_ref6){let{state:state}=_ref6;const sectionlist=this.reactive.getExporter().listedSectionIds(state);this._fixOrder(this.element,sectionlist,this.sections)}_fixOrder(container,neworder,allitems){if(!neworder.length)return container.classList.add("hidden"),void(container.innerHTML="");for(container.classList.remove("hidden"),neworder.forEach(((itemid,index)=>{const item=allitems[itemid],currentitem=container.children[index];void 0!==currentitem||null==item?currentitem!==item&&item&&container.insertBefore(item,currentitem):container.append(item)}));container.children.length>neworder.length;)container.removeChild(container.lastChild)}_deleteCm(_ref7){let{element:element}=_ref7;delete this.cms[element.id]}_deleteSection(_ref8){let{element:element}=_ref8;delete this.sections[element.id]}}return _exports.default=Component,_exports.default})); //# sourceMappingURL=courseindex.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/courseindex/courseindex.min.js.map b/course/format/amd/build/local/courseindex/courseindex.min.js.map index 3e516d5f9fff1..9fd0a7323f368 100644 --- a/course/format/amd/build/local/courseindex/courseindex.min.js.map +++ b/course/format/amd/build/local/courseindex/courseindex.min.js.map @@ -1 +1 @@ -{"version":3,"file":"courseindex.min.js","sources":["../../../src/local/courseindex/courseindex.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 * Course index main component.\n *\n * @module core_courseformat/local/courseindex/courseindex\n * @class core_courseformat/local/courseindex/courseindex\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent} from 'core/reactive';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport jQuery from 'jquery';\nimport ContentTree from 'core_courseformat/local/courseeditor/contenttree';\n\nexport default class Component extends BaseComponent {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'courseindex';\n // Default query selectors.\n this.selectors = {\n SECTION: `[data-for='section']`,\n SECTION_CMLIST: `[data-for='cmlist']`,\n CM: `[data-for='cm']`,\n TOGGLER: `[data-action=\"togglecourseindexsection\"]`,\n COLLAPSE: `[data-bs-toggle=\"collapse\"]`,\n DRAWER: `.drawer`,\n };\n // Default classes to toggle on refresh.\n this.classes = {\n SECTIONHIDDEN: 'dimmed',\n CMHIDDEN: 'dimmed',\n SECTIONCURRENT: 'current',\n COLLAPSED: `collapsed`,\n SHOW: `show`,\n };\n // Arrays to keep cms and sections elements.\n this.sections = {};\n this.cms = {};\n }\n\n /**\n * Static method to create a component instance form the mustache template.\n *\n * @param {element|string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new this({\n element: document.getElementById(target),\n reactive: getCurrentCourseEditor(),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the state data\n */\n stateReady(state) {\n // Activate section togglers.\n this.addEventListener(this.element, 'click', this._sectionTogglers);\n\n // Get cms and sections elements.\n const sections = this.getElements(this.selectors.SECTION);\n sections.forEach((section) => {\n this.sections[section.dataset.id] = section;\n });\n const cms = this.getElements(this.selectors.CM);\n cms.forEach((cm) => {\n this.cms[cm.dataset.id] = cm;\n });\n\n this._expandPageCmSectionIfNecessary(state);\n this._refreshPageItem({element: state.course, state});\n\n // Configure Aria Tree.\n this.contentTree = new ContentTree(this.element, this.selectors, this.reactive.isEditing);\n }\n\n getWatchers() {\n return [\n {watch: `section.indexcollapsed:updated`, handler: this._refreshSectionCollapsed},\n {watch: `cm:created`, handler: this._createCm},\n {watch: `cm:deleted`, handler: this._deleteCm},\n {watch: `section:created`, handler: this._createSection},\n {watch: `section:deleted`, handler: this._deleteSection},\n {watch: `course.pageItem:created`, handler: this._refreshPageItem},\n {watch: `course.pageItem:updated`, handler: this._refreshPageItem},\n // Sections and cm sorting.\n {watch: `course.sectionlist:updated`, handler: this._refreshCourseSectionlist},\n {watch: `section.cmlist:updated`, handler: this._refreshSectionCmlist},\n ];\n }\n\n /**\n * Setup sections toggler.\n *\n * Toggler click is delegated to the main course index element because new sections can\n * appear at any moment and this way we prevent accidental double bindings.\n *\n * @param {Event} event the triggered event\n */\n _sectionTogglers(event) {\n const sectionlink = event.target.closest(this.selectors.TOGGLER);\n const isChevron = event.target.closest(this.selectors.COLLAPSE);\n\n if (sectionlink || isChevron) {\n\n const section = event.target.closest(this.selectors.SECTION);\n const toggler = section.querySelector(this.selectors.COLLAPSE);\n const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;\n\n // Update the state.\n const sectionId = section.getAttribute('data-id');\n if (!sectionlink || isCollapsed) {\n this.reactive.dispatch(\n 'sectionIndexCollapsed',\n [sectionId],\n !isCollapsed\n );\n }\n }\n }\n\n /**\n * Update section collapsed.\n *\n * @param {object} args\n * @param {object} args.element The leement to be expanded\n */\n _refreshSectionCollapsed({element}) {\n const target = this.getElement(this.selectors.SECTION, element.id);\n if (!target) {\n throw new Error(`Unkown section with ID ${element.id}`);\n }\n // Check if it is already done.\n const toggler = target.querySelector(this.selectors.COLLAPSE);\n const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;\n\n if (element.indexcollapsed !== isCollapsed) {\n this._expandSectionNode(element);\n }\n }\n\n /**\n * Expand a section node.\n *\n * By default the method will use element.indexcollapsed to decide if the\n * section is opened or closed. However, using forceValue it is possible\n * to open or close a section independant from the indexcollapsed attribute.\n *\n * @param {Object} element the course module state element\n * @param {boolean} forceValue optional forced expanded value\n */\n _expandSectionNode(element, forceValue) {\n const target = this.getElement(this.selectors.SECTION, element.id);\n const toggler = target.querySelector(this.selectors.COLLAPSE);\n let collapsibleId = toggler.dataset.target ?? toggler.getAttribute(\"href\");\n if (!collapsibleId) {\n return;\n }\n collapsibleId = collapsibleId.replace('#', '');\n const collapsible = document.getElementById(collapsibleId);\n if (!collapsible) {\n return;\n }\n\n if (forceValue === undefined) {\n forceValue = (element.indexcollapsed) ? false : true;\n }\n\n // Course index is based on Bootstrap 4 collapsibles. To collapse them we need jQuery to\n // interact with collapsibles methods. Hopefully, this will change in Bootstrap 5 because\n // it does not require jQuery anymore (when MDL-71979 is integrated).\n const togglerValue = (forceValue) ? 'show' : 'hide';\n jQuery(collapsible).collapse(togglerValue);\n }\n\n /**\n * Handle a page item update.\n *\n * @param {Object} details the update details\n * @param {Object} details.state the state data.\n * @param {Object} details.element the course state data.\n */\n _refreshPageItem({element, state}) {\n if (!element?.pageItem?.isStatic || element.pageItem.type != 'cm') {\n return;\n }\n // Check if we need to uncollapse the section and scroll to the element.\n const section = state.section.get(element.pageItem.sectionId);\n if (section.indexcollapsed) {\n this._expandSectionNode(section, true);\n setTimeout(\n () => this.cms[element.pageItem.id]?.scrollIntoView({block: \"nearest\"}),\n 250\n );\n }\n }\n\n /**\n * Expand a section if the current page is a section's cm.\n *\n * @private\n * @param {Object} state the course state.\n */\n _expandPageCmSectionIfNecessary(state) {\n const pageCmInfo = this.reactive.getPageAnchorCmInfo();\n if (!pageCmInfo) {\n return;\n }\n this._expandSectionNode(state.section.get(pageCmInfo.sectionid), true);\n }\n\n /**\n * Create a newcm instance.\n *\n * @param {object} param\n * @param {Object} param.state\n * @param {Object} param.element\n */\n async _createCm({state, element}) {\n // Create a fake node while the component is loading.\n const fakeelement = document.createElement('li');\n fakeelement.classList.add('bg-pulse-grey', 'w-100');\n fakeelement.innerHTML = ' ';\n this.cms[element.id] = fakeelement;\n // Place the fake node on the correct position.\n this._refreshSectionCmlist({\n state,\n element: state.section.get(element.sectionid),\n });\n // Collect render data.\n const exporter = this.reactive.getExporter();\n const data = exporter.cm(state, element);\n // Create the new content.\n const newcomponent = await this.renderComponent(fakeelement, 'core_courseformat/local/courseindex/cm', data);\n // Replace the fake node with the real content.\n const newelement = newcomponent.getElement();\n this.cms[element.id] = newelement;\n fakeelement.parentNode.replaceChild(newelement, fakeelement);\n }\n\n /**\n * Create a new section instance.\n *\n * @param {Object} details the update details.\n * @param {Object} details.state the state data.\n * @param {Object} details.element the element data.\n */\n async _createSection({state, element}) {\n // Create a fake node while the component is loading.\n const fakeelement = document.createElement('div');\n fakeelement.classList.add('bg-pulse-grey', 'w-100');\n fakeelement.innerHTML = ' ';\n this.sections[element.id] = fakeelement;\n // Place the fake node on the correct position.\n this._refreshCourseSectionlist({\n state,\n element: state.course,\n });\n // Collect render data.\n const exporter = this.reactive.getExporter();\n const data = exporter.section(state, element);\n // Create the new content.\n const newcomponent = await this.renderComponent(fakeelement, 'core_courseformat/local/courseindex/section', data);\n // Replace the fake node with the real content.\n const newelement = newcomponent.getElement();\n this.sections[element.id] = newelement;\n fakeelement.parentNode.replaceChild(newelement, fakeelement);\n }\n\n /**\n * Refresh a section cm list.\n *\n * @param {object} param\n * @param {Object} param.element\n */\n _refreshSectionCmlist({element}) {\n const cmlist = element.cmlist ?? [];\n const listparent = this.getElement(this.selectors.SECTION_CMLIST, element.id);\n if (!listparent) {\n return;\n }\n this._fixOrder(listparent, cmlist, this.cms);\n }\n\n /**\n * Refresh the section list.\n *\n * @param {object} param\n * @param {Object} param.state\n */\n _refreshCourseSectionlist({state}) {\n const sectionlist = this.reactive.getExporter().listedSectionIds(state);\n this._fixOrder(this.element, sectionlist, this.sections);\n }\n\n /**\n * Fix/reorder the section or cms order.\n *\n * @param {Element} container the HTML element to reorder.\n * @param {Array} neworder an array with the ids order\n * @param {Array} allitems the list of html elements that can be placed in the container\n */\n _fixOrder(container, neworder, allitems) {\n\n // Empty lists should not be visible.\n if (!neworder.length) {\n container.classList.add('hidden');\n container.innerHTML = '';\n return;\n }\n\n // Grant the list is visible (in case it was empty).\n container.classList.remove('hidden');\n\n // Move the elements in order at the beginning of the list.\n neworder.forEach((itemid, index) => {\n const item = allitems[itemid];\n // Get the current element at that position.\n const currentitem = container.children[index];\n if (currentitem === undefined && item != undefined) {\n container.append(item);\n return;\n }\n if (currentitem !== item && item) {\n container.insertBefore(item, currentitem);\n }\n });\n // Remove the remaining elements.\n while (container.children.length > neworder.length) {\n container.removeChild(container.lastChild);\n }\n }\n\n /**\n * Remove a cm from the list.\n *\n * The actual DOM element removal is delegated to the cm component.\n *\n * @param {object} param\n * @param {Object} param.element\n */\n _deleteCm({element}) {\n delete this.cms[element.id];\n }\n\n /**\n * Remove a section from the list.\n *\n * The actual DOM element removal is delegated to the section component.\n *\n * @param {Object} details the update details.\n * @param {Object} details.element the element data.\n */\n _deleteSection({element}) {\n delete this.sections[element.id];\n }\n}\n"],"names":["Component","BaseComponent","create","name","selectors","SECTION","SECTION_CMLIST","CM","TOGGLER","COLLAPSE","DRAWER","classes","SECTIONHIDDEN","CMHIDDEN","SECTIONCURRENT","COLLAPSED","SHOW","sections","cms","target","this","element","document","getElementById","reactive","stateReady","state","addEventListener","_sectionTogglers","getElements","forEach","section","dataset","id","cm","_expandPageCmSectionIfNecessary","_refreshPageItem","course","contentTree","ContentTree","isEditing","getWatchers","watch","handler","_refreshSectionCollapsed","_createCm","_deleteCm","_createSection","_deleteSection","_refreshCourseSectionlist","_refreshSectionCmlist","event","sectionlink","closest","isChevron","toggler","querySelector","isCollapsed","classList","contains","sectionId","getAttribute","dispatch","getElement","Error","indexcollapsed","_expandSectionNode","forceValue","collapsibleId","replace","collapsible","undefined","togglerValue","collapse","pageItem","_element$pageItem","isStatic","type","get","setTimeout","_this$cms$element$pag","scrollIntoView","block","pageCmInfo","getPageAnchorCmInfo","sectionid","fakeelement","createElement","add","innerHTML","data","getExporter","newelement","renderComponent","parentNode","replaceChild","cmlist","listparent","_fixOrder","sectionlist","listedSectionIds","container","neworder","allitems","length","remove","itemid","index","item","currentitem","children","insertBefore","append","removeChild","lastChild"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"courseindex.min.js","sources":["../../../src/local/courseindex/courseindex.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 * Course index main component.\n *\n * @module core_courseformat/local/courseindex/courseindex\n * @class core_courseformat/local/courseindex/courseindex\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent} from 'core/reactive';\nimport Collapse from 'theme_boost/bootstrap/collapse';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport ContentTree from 'core_courseformat/local/courseeditor/contenttree';\n\nexport default class Component extends BaseComponent {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'courseindex';\n // Default query selectors.\n this.selectors = {\n SECTION: `[data-for='section']`,\n SECTION_CMLIST: `[data-for='cmlist']`,\n CM: `[data-for='cm']`,\n TOGGLER: `[data-action=\"togglecourseindexsection\"]`,\n COLLAPSE: `[data-bs-toggle=\"collapse\"]`,\n DRAWER: `.drawer`,\n };\n // Default classes to toggle on refresh.\n this.classes = {\n SECTIONHIDDEN: 'dimmed',\n CMHIDDEN: 'dimmed',\n SECTIONCURRENT: 'current',\n COLLAPSED: `collapsed`,\n SHOW: `show`,\n };\n // Arrays to keep cms and sections elements.\n this.sections = {};\n this.cms = {};\n }\n\n /**\n * Static method to create a component instance form the mustache template.\n *\n * @param {element|string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new this({\n element: document.getElementById(target),\n reactive: getCurrentCourseEditor(),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the state data\n */\n stateReady(state) {\n // Activate section togglers.\n this.addEventListener(this.element, 'click', this._sectionTogglers);\n\n // Get cms and sections elements.\n const sections = this.getElements(this.selectors.SECTION);\n sections.forEach((section) => {\n this.sections[section.dataset.id] = section;\n });\n const cms = this.getElements(this.selectors.CM);\n cms.forEach((cm) => {\n this.cms[cm.dataset.id] = cm;\n });\n\n this._expandPageCmSectionIfNecessary(state);\n this._refreshPageItem({element: state.course, state});\n\n // Configure Aria Tree.\n this.contentTree = new ContentTree(this.element, this.selectors, this.reactive.isEditing);\n }\n\n getWatchers() {\n return [\n {watch: `section.indexcollapsed:updated`, handler: this._refreshSectionCollapsed},\n {watch: `cm:created`, handler: this._createCm},\n {watch: `cm:deleted`, handler: this._deleteCm},\n {watch: `section:created`, handler: this._createSection},\n {watch: `section:deleted`, handler: this._deleteSection},\n {watch: `course.pageItem:created`, handler: this._refreshPageItem},\n {watch: `course.pageItem:updated`, handler: this._refreshPageItem},\n // Sections and cm sorting.\n {watch: `course.sectionlist:updated`, handler: this._refreshCourseSectionlist},\n {watch: `section.cmlist:updated`, handler: this._refreshSectionCmlist},\n ];\n }\n\n /**\n * Setup sections toggler.\n *\n * Toggler click is delegated to the main course index element because new sections can\n * appear at any moment and this way we prevent accidental double bindings.\n *\n * @param {Event} event the triggered event\n */\n _sectionTogglers(event) {\n const sectionlink = event.target.closest(this.selectors.TOGGLER);\n const isChevron = event.target.closest(this.selectors.COLLAPSE);\n\n if (sectionlink || isChevron) {\n\n const section = event.target.closest(this.selectors.SECTION);\n const toggler = section.querySelector(this.selectors.COLLAPSE);\n const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;\n\n // Update the state.\n const sectionId = section.getAttribute('data-id');\n if (!sectionlink || isCollapsed) {\n this.reactive.dispatch(\n 'sectionIndexCollapsed',\n [sectionId],\n !isCollapsed\n );\n }\n }\n }\n\n /**\n * Update section collapsed.\n *\n * @param {object} args\n * @param {object} args.element The leement to be expanded\n */\n _refreshSectionCollapsed({element}) {\n const target = this.getElement(this.selectors.SECTION, element.id);\n if (!target) {\n throw new Error(`Unkown section with ID ${element.id}`);\n }\n // Check if it is already done.\n const toggler = target.querySelector(this.selectors.COLLAPSE);\n const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;\n\n if (element.indexcollapsed !== isCollapsed) {\n this._expandSectionNode(element);\n }\n }\n\n /**\n * Expand a section node.\n *\n * By default the method will use element.indexcollapsed to decide if the\n * section is opened or closed. However, using forceValue it is possible\n * to open or close a section independant from the indexcollapsed attribute.\n *\n * @param {Object} element the course module state element\n * @param {boolean} forceValue optional forced expanded value\n */\n _expandSectionNode(element, forceValue) {\n const target = this.getElement(this.selectors.SECTION, element.id);\n const toggler = target.querySelector(this.selectors.COLLAPSE);\n let collapsibleId = toggler.dataset.target ?? toggler.getAttribute(\"href\");\n if (!collapsibleId) {\n return;\n }\n collapsibleId = collapsibleId.replace('#', '');\n const collapsible = document.getElementById(collapsibleId);\n if (!collapsible) {\n return;\n }\n\n if (forceValue === undefined) {\n forceValue = (element.indexcollapsed) ? false : true;\n }\n\n if (forceValue) {\n Collapse.getOrCreateInstance(collapsible, {toggle: false}).show();\n } else {\n Collapse.getOrCreateInstance(collapsible, {toggle: false}).hide();\n }\n }\n\n /**\n * Handle a page item update.\n *\n * @param {Object} details the update details\n * @param {Object} details.state the state data.\n * @param {Object} details.element the course state data.\n */\n _refreshPageItem({element, state}) {\n if (!element?.pageItem?.isStatic || element.pageItem.type != 'cm') {\n return;\n }\n // Check if we need to uncollapse the section and scroll to the element.\n const section = state.section.get(element.pageItem.sectionId);\n if (section.indexcollapsed) {\n this._expandSectionNode(section, true);\n setTimeout(\n () => this.cms[element.pageItem.id]?.scrollIntoView({block: \"nearest\"}),\n 250\n );\n }\n }\n\n /**\n * Expand a section if the current page is a section's cm.\n *\n * @private\n * @param {Object} state the course state.\n */\n _expandPageCmSectionIfNecessary(state) {\n const pageCmInfo = this.reactive.getPageAnchorCmInfo();\n if (!pageCmInfo) {\n return;\n }\n this._expandSectionNode(state.section.get(pageCmInfo.sectionid), true);\n }\n\n /**\n * Create a newcm instance.\n *\n * @param {object} param\n * @param {Object} param.state\n * @param {Object} param.element\n */\n async _createCm({state, element}) {\n // Create a fake node while the component is loading.\n const fakeelement = document.createElement('li');\n fakeelement.classList.add('bg-pulse-grey', 'w-100');\n fakeelement.innerHTML = ' ';\n this.cms[element.id] = fakeelement;\n // Place the fake node on the correct position.\n this._refreshSectionCmlist({\n state,\n element: state.section.get(element.sectionid),\n });\n // Collect render data.\n const exporter = this.reactive.getExporter();\n const data = exporter.cm(state, element);\n // Create the new content.\n const newcomponent = await this.renderComponent(fakeelement, 'core_courseformat/local/courseindex/cm', data);\n // Replace the fake node with the real content.\n const newelement = newcomponent.getElement();\n this.cms[element.id] = newelement;\n fakeelement.parentNode.replaceChild(newelement, fakeelement);\n }\n\n /**\n * Create a new section instance.\n *\n * @param {Object} details the update details.\n * @param {Object} details.state the state data.\n * @param {Object} details.element the element data.\n */\n async _createSection({state, element}) {\n // Create a fake node while the component is loading.\n const fakeelement = document.createElement('div');\n fakeelement.classList.add('bg-pulse-grey', 'w-100');\n fakeelement.innerHTML = ' ';\n this.sections[element.id] = fakeelement;\n // Place the fake node on the correct position.\n this._refreshCourseSectionlist({\n state,\n element: state.course,\n });\n // Collect render data.\n const exporter = this.reactive.getExporter();\n const data = exporter.section(state, element);\n // Create the new content.\n const newcomponent = await this.renderComponent(fakeelement, 'core_courseformat/local/courseindex/section', data);\n // Replace the fake node with the real content.\n const newelement = newcomponent.getElement();\n this.sections[element.id] = newelement;\n fakeelement.parentNode.replaceChild(newelement, fakeelement);\n }\n\n /**\n * Refresh a section cm list.\n *\n * @param {object} param\n * @param {Object} param.element\n */\n _refreshSectionCmlist({element}) {\n const cmlist = element.cmlist ?? [];\n const listparent = this.getElement(this.selectors.SECTION_CMLIST, element.id);\n if (!listparent) {\n return;\n }\n this._fixOrder(listparent, cmlist, this.cms);\n }\n\n /**\n * Refresh the section list.\n *\n * @param {object} param\n * @param {Object} param.state\n */\n _refreshCourseSectionlist({state}) {\n const sectionlist = this.reactive.getExporter().listedSectionIds(state);\n this._fixOrder(this.element, sectionlist, this.sections);\n }\n\n /**\n * Fix/reorder the section or cms order.\n *\n * @param {Element} container the HTML element to reorder.\n * @param {Array} neworder an array with the ids order\n * @param {Array} allitems the list of html elements that can be placed in the container\n */\n _fixOrder(container, neworder, allitems) {\n\n // Empty lists should not be visible.\n if (!neworder.length) {\n container.classList.add('hidden');\n container.innerHTML = '';\n return;\n }\n\n // Grant the list is visible (in case it was empty).\n container.classList.remove('hidden');\n\n // Move the elements in order at the beginning of the list.\n neworder.forEach((itemid, index) => {\n const item = allitems[itemid];\n // Get the current element at that position.\n const currentitem = container.children[index];\n if (currentitem === undefined && item != undefined) {\n container.append(item);\n return;\n }\n if (currentitem !== item && item) {\n container.insertBefore(item, currentitem);\n }\n });\n // Remove the remaining elements.\n while (container.children.length > neworder.length) {\n container.removeChild(container.lastChild);\n }\n }\n\n /**\n * Remove a cm from the list.\n *\n * The actual DOM element removal is delegated to the cm component.\n *\n * @param {object} param\n * @param {Object} param.element\n */\n _deleteCm({element}) {\n delete this.cms[element.id];\n }\n\n /**\n * Remove a section from the list.\n *\n * The actual DOM element removal is delegated to the section component.\n *\n * @param {Object} details the update details.\n * @param {Object} details.element the element data.\n */\n _deleteSection({element}) {\n delete this.sections[element.id];\n }\n}\n"],"names":["Component","BaseComponent","create","name","selectors","SECTION","SECTION_CMLIST","CM","TOGGLER","COLLAPSE","DRAWER","classes","SECTIONHIDDEN","CMHIDDEN","SECTIONCURRENT","COLLAPSED","SHOW","sections","cms","target","this","element","document","getElementById","reactive","stateReady","state","addEventListener","_sectionTogglers","getElements","forEach","section","dataset","id","cm","_expandPageCmSectionIfNecessary","_refreshPageItem","course","contentTree","ContentTree","isEditing","getWatchers","watch","handler","_refreshSectionCollapsed","_createCm","_deleteCm","_createSection","_deleteSection","_refreshCourseSectionlist","_refreshSectionCmlist","event","sectionlink","closest","isChevron","toggler","querySelector","isCollapsed","classList","contains","sectionId","getAttribute","dispatch","getElement","Error","indexcollapsed","_expandSectionNode","forceValue","collapsibleId","replace","collapsible","undefined","getOrCreateInstance","toggle","show","hide","pageItem","_element$pageItem","isStatic","type","get","setTimeout","_this$cms$element$pag","scrollIntoView","block","pageCmInfo","getPageAnchorCmInfo","sectionid","fakeelement","createElement","add","innerHTML","data","getExporter","newelement","renderComponent","parentNode","replaceChild","cmlist","listparent","_fixOrder","sectionlist","listedSectionIds","container","neworder","allitems","length","remove","itemid","index","item","currentitem","children","insertBefore","append","removeChild","lastChild"],"mappings":"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"} \ No newline at end of file diff --git a/course/format/amd/src/local/content.js b/course/format/amd/src/local/content.js index 64084c33339f6..a7dab32c87eb0 100644 --- a/course/format/amd/src/local/content.js +++ b/course/format/amd/src/local/content.js @@ -23,6 +23,7 @@ */ import {BaseComponent} from 'core/reactive'; +import Collapse from 'theme_boost/bootstrap/collapse'; import {debounce} from 'core/utils'; import {getCurrentCourseEditor} from 'core_courseformat/courseeditor'; import Config from 'core/config'; @@ -33,8 +34,6 @@ import Fragment from 'core/fragment'; import Templates from 'core/templates'; import DispatchActions from 'core_courseformat/local/content/actions'; import * as CourseEvents from 'core_course/events'; -// The jQuery module is only used for interacting with Boostrap 4. It can we removed when MDL-71979 is integrated. -import jQuery from 'jquery'; import Pending from 'core/pending'; export default class Component extends BaseComponent { @@ -291,11 +290,11 @@ export default class Component extends BaseComponent { if (!collapsible) { return; } - - // Course index is based on Bootstrap 4 collapsibles. To collapse them we need jQuery to - // interact with collapsibles methods. Hopefully, this will change in Bootstrap 5 because - // it does not require jQuery anymore (when MDL-71979 is integrated). - jQuery(collapsible).collapse(element.contentcollapsed ? 'hide' : 'show'); + if (element.contentcollapsed) { + Collapse.getOrCreateInstance(collapsible, {toggle: false}).hide(); + } else { + Collapse.getOrCreateInstance(collapsible, {toggle: false}).show(); + } } this._refreshAllSectionsToggler(state); diff --git a/course/format/amd/src/local/content/actions.js b/course/format/amd/src/local/content/actions.js index b09af030687f3..b60f4818436ec 100644 --- a/course/format/amd/src/local/content/actions.js +++ b/course/format/amd/src/local/content/actions.js @@ -26,6 +26,7 @@ */ import {BaseComponent} from 'core/reactive'; +import Collapse from 'theme_boost/bootstrap/collapse'; import Modal from 'core/modal'; import ModalSaveCancel from 'core/modal_save_cancel'; import ModalDeleteCancel from 'core/modal_delete_cancel'; @@ -39,7 +40,6 @@ import * as CourseEvents from 'core_course/events'; import Pending from 'core/pending'; import ContentTree from 'core_courseformat/local/courseeditor/contenttree'; // The jQuery module is only used for interacting with Boostrap 4. It can we removed when MDL-71979 is integrated. -import jQuery from 'jquery'; import Notification from "core/notification"; // Load global strings. @@ -363,8 +363,6 @@ export default class extends BaseComponent { } ); - // Open the cm section node if possible (Bootstrap 4 uses jQuery to interact with collapsibles). - // All jQuery in this code can be replaced when MDL-71979 is integrated. cmIds.forEach(cmId => { const cmInfo = this.reactive.get('cm', cmId); let selector; @@ -434,13 +432,13 @@ export default class extends BaseComponent { return; } - const toggler = jQuery(sectionnode).find(this.selectors.MODALTOGGLER); - let collapsibleId = toggler.data('target') ?? toggler.attr('href'); + const toggler = sectionnode.querySelector(this.selectors.MODALTOGGLER); + let collapsibleId = toggler.dataset.target ?? toggler.getAttribute('href'); if (collapsibleId) { // We cannot be sure we have # in the id element name. collapsibleId = collapsibleId.replace('#', ''); const expandNode = modalBody.querySelector(`#${collapsibleId}`); - jQuery(expandNode).collapse('show'); + new Collapse(expandNode, {toggle: false}).show(); } // Section are a tree structure, we need to expand all the parents. diff --git a/course/format/amd/src/local/courseeditor/contenttree.js b/course/format/amd/src/local/courseeditor/contenttree.js index 2fc675e85ff51..e0694b5f69a6c 100644 --- a/course/format/amd/src/local/courseeditor/contenttree.js +++ b/course/format/amd/src/local/courseeditor/contenttree.js @@ -27,6 +27,7 @@ */ // The core/tree uses jQuery to expand all nodes. +import Collapse from 'theme_boost/bootstrap/collapse'; import jQuery from 'jquery'; import Tree from 'core/tree'; import {getList} from 'core/normalise'; @@ -61,8 +62,7 @@ export default class extends Tree { return this._getVisibleItems(); }; } - // All jQuery events can be replaced when MDL-71979 is integrated. - this.treeRoot.on('hidden.bs.collapse shown.bs.collapse', () => { + this.treeRoot[0].addEventListener('hidden.bs.collapse shown.bs.collapse', () => { this.refreshVisibleItemsCache(); }); // Register a custom callback for pressing enter key. @@ -146,18 +146,16 @@ export default class extends Tree { * @param {JQuery} item the jQuery object */ toggleGroup(item) { - // All jQuery in this segment of code can be replaced when MDL-71979 is integrated. - const toggler = item.find(this.selectors.COLLAPSE); - let collapsibleId = toggler.data('target') ?? toggler.attr('href'); + const toggler = item[0].querySelector(this.selectors.COLLAPSE); + let collapsibleId = toggler.dataset?.target ?? toggler.getAttribute('href'); if (!collapsibleId) { return; } collapsibleId = collapsibleId.replace('#', ''); - // Bootstrap 4 uses jQuery to interact with collapsibles. - const collapsible = jQuery(`#${collapsibleId}`); - if (collapsible.length) { - jQuery(`#${collapsibleId}`).collapse('toggle'); + const collapsible = document.getElementById(collapsibleId); + if (collapsible) { + Collapse.getOrCreateInstance(collapsible).toggle(); } } diff --git a/course/format/amd/src/local/courseindex/courseindex.js b/course/format/amd/src/local/courseindex/courseindex.js index 8c73c40a7e632..3314d7f517e41 100644 --- a/course/format/amd/src/local/courseindex/courseindex.js +++ b/course/format/amd/src/local/courseindex/courseindex.js @@ -23,8 +23,8 @@ */ import {BaseComponent} from 'core/reactive'; +import Collapse from 'theme_boost/bootstrap/collapse'; import {getCurrentCourseEditor} from 'core_courseformat/courseeditor'; -import jQuery from 'jquery'; import ContentTree from 'core_courseformat/local/courseeditor/contenttree'; export default class Component extends BaseComponent { @@ -190,11 +190,11 @@ export default class Component extends BaseComponent { forceValue = (element.indexcollapsed) ? false : true; } - // Course index is based on Bootstrap 4 collapsibles. To collapse them we need jQuery to - // interact with collapsibles methods. Hopefully, this will change in Bootstrap 5 because - // it does not require jQuery anymore (when MDL-71979 is integrated). - const togglerValue = (forceValue) ? 'show' : 'hide'; - jQuery(collapsible).collapse(togglerValue); + if (forceValue) { + Collapse.getOrCreateInstance(collapsible, {toggle: false}).show(); + } else { + Collapse.getOrCreateInstance(collapsible, {toggle: false}).hide(); + } } /** diff --git a/lib/amd/build/dynamic_tabs.min.js b/lib/amd/build/dynamic_tabs.min.js index 9eb86673630f8..7a4ea705f26bc 100644 --- a/lib/amd/build/dynamic_tabs.min.js +++ b/lib/amd/build/dynamic_tabs.min.js @@ -5,6 +5,6 @@ define("core/dynamic_tabs",["exports","jquery","core/templates","core/loadingico * @module core/dynamic_tabs * @copyright 2021 David Matamoros based on code from Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const SELECTORS={dynamicTabs:".dynamictabs",activeTab:".dynamictabs .nav-link.active",allActiveTabs:'.dynamictabs .nav-link[data-bs-toggle="tab"]:not(.disabled)',tabContent:".dynamictabs .tab-pane [data-tab-content]",tabToggle:'a[data-bs-toggle="tab"]',tabPane:".dynamictabs .tab-pane",forTabName:tabName=>'.dynamictabs [data-tab-content="'.concat(tabName,'"]'),forTabId:tabName=>'.dynamictabs [data-bs-toggle="tab"][href="#'.concat(tabName,'"]')};_exports.init=()=>{const tabToggle=(0,_jquery.default)(SELECTORS.tabToggle);if(tabToggle.on("click",(event=>{(0,_changechecker.isAnyWatchedFormDirty)()&&(event.preventDefault(),event.stopPropagation(),(0,_str.getStrings)([{key:"changesmade",component:"moodle"},{key:"changesmadereallygoaway",component:"moodle"},{key:"confirm",component:"moodle"}]).then((_ref=>{let[strChangesMade,strChangesMadeReally,strConfirm]=_ref;return _notification.default.confirm(strChangesMade,strChangesMadeReally,strConfirm,null,(()=>{(0,_changechecker.resetAllFormDirtyStates)(),(0,_jquery.default)(event.target).trigger(event.type)}))})).catch(_notification.default.exception))})),tabToggle.on("show.bs.tab",(function(){const previousTabName=getActiveTabName();if(previousTabName){document.querySelector(SELECTORS.forTabName(previousTabName)).textContent=""}})).on("shown.bs.tab",(function(){const tab=(0,_jquery.default)((0,_jquery.default)(this).attr("href"));1===tab.length&&loadTab(tab.attr("id"))})),!openTabFromHash()){const tabs=document.querySelector(SELECTORS.allActiveTabs);if(tabs)openTab(tabs.getAttribute("aria-controls"));else{const tabPane=document.querySelector(SELECTORS.tabPane);tabPane&&(tabPane.classList.add("active","show"),loadTab(tabPane.getAttribute("id")))}}};const getActiveTabName=()=>{const element=document.querySelector(SELECTORS.activeTab);return(null==element?void 0:element.getAttribute("aria-controls"))||null},loadTab=tabName=>{var _ref2,_tabName;tabName=null!==(_ref2=null!==(_tabName=tabName)&&void 0!==_tabName?_tabName:getActiveTabName())&&void 0!==_ref2?_ref2:(()=>{const element=document.querySelector(SELECTORS.tabContent);return(null==element?void 0:element.dataset.tabContent)||null})();const tab=document.querySelector(SELECTORS.forTabName(tabName));if(!tab)return;const pendingPromise=new _pending.default("core/dynamic_tabs:loadTab:"+tabName);(0,_loadingicon.addIconToContainer)(tab).then((()=>{let tabArgs={...tab.dataset};return delete tabArgs.tabClass,delete tabArgs.tabContent,(0,_dynamic_tabs.getContent)(tab.dataset.tabClass,JSON.stringify(tabArgs))})).then((response=>Promise.all([_jquery.default.parseHTML(response.javascript,null,!0).map((node=>node.innerHTML)).join("\n"),_templates.default.renderForPromise(response.template,JSON.parse(response.content))]))).then((_ref3=>{let[responseJs,{html:html,js:js}]=_ref3;return _templates.default.replaceNodeContents(tab,html,js+responseJs)})).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)},openTab=tabName=>{const tab=(tabName=>document.querySelector(SELECTORS.forTabId(tabName)))(tabName);return!!tab&&(loadTab(tabName),tab.classList.add("active"),(tabName=>document.getElementById(tabName))(tabName).classList.add("active","show"),!0)},openTabFromHash=()=>{const hash=document.location.hash;return!!hash.match(/^#\w+$/g)&&openTab(hash.replace(/^#/g,""))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const SELECTORS={dynamicTabs:".dynamictabs",activeTab:".dynamictabs .nav-link.active",allActiveTabs:'.dynamictabs .nav-link[data-bs-toggle="tab"]:not(.disabled)',tabContent:".dynamictabs .tab-pane [data-tab-content]",tabToggle:'a[data-bs-toggle="tab"]',tabPane:".dynamictabs .tab-pane",forTabName:tabName=>'.dynamictabs [data-tab-content="'.concat(tabName,'"]'),forTabId:tabName=>'.dynamictabs [data-bs-toggle="tab"][href="#'.concat(tabName,'"]')};_exports.init=()=>{if(document.querySelectorAll(SELECTORS.tabToggle).forEach((tabToggle=>{tabToggle.addEventListener("click",(event=>{(0,_changechecker.isAnyWatchedFormDirty)()&&(event.preventDefault(),event.stopPropagation(),(0,_str.getStrings)([{key:"changesmade",component:"moodle"},{key:"changesmadereallygoaway",component:"moodle"},{key:"confirm",component:"moodle"}]).then((_ref=>{let[strChangesMade,strChangesMadeReally,strConfirm]=_ref;return _notification.default.confirm(strChangesMade,strChangesMadeReally,strConfirm,null,(()=>{(0,_changechecker.resetAllFormDirtyStates)(),(0,_jquery.default)(event.target).trigger(event.type)}))})).catch(_notification.default.exception))})),tabToggle.addEventListener("show.bs.tab",(()=>{const previousTabName=getActiveTabName();if(previousTabName){document.querySelector(SELECTORS.forTabName(previousTabName)).textContent=""}})),tabToggle.addEventListener("shown.bs.tab",(()=>{const tabPane=document.getElementById(tabToggle.getAttribute("href").replace(/^#/,""));tabPane&&loadTab(tabPane.id)}))})),!openTabFromHash()){const tabs=document.querySelector(SELECTORS.allActiveTabs);if(tabs)openTab(tabs.getAttribute("aria-controls"));else{const tabPane=document.querySelector(SELECTORS.tabPane);tabPane&&(tabPane.classList.add("active","show"),loadTab(tabPane.getAttribute("id")))}}};const getActiveTabName=()=>{const element=document.querySelector(SELECTORS.activeTab);return(null==element?void 0:element.getAttribute("aria-controls"))||null},loadTab=tabName=>{var _ref2,_tabName;tabName=null!==(_ref2=null!==(_tabName=tabName)&&void 0!==_tabName?_tabName:getActiveTabName())&&void 0!==_ref2?_ref2:(()=>{const element=document.querySelector(SELECTORS.tabContent);return(null==element?void 0:element.dataset.tabContent)||null})();const tab=document.querySelector(SELECTORS.forTabName(tabName));if(!tab)return;const pendingPromise=new _pending.default("core/dynamic_tabs:loadTab:"+tabName);(0,_loadingicon.addIconToContainer)(tab).then((()=>{let tabArgs={...tab.dataset};return delete tabArgs.tabClass,delete tabArgs.tabContent,(0,_dynamic_tabs.getContent)(tab.dataset.tabClass,JSON.stringify(tabArgs))})).then((response=>Promise.all([_jquery.default.parseHTML(response.javascript,null,!0).map((node=>node.innerHTML)).join("\n"),_templates.default.renderForPromise(response.template,JSON.parse(response.content))]))).then((_ref3=>{let[responseJs,{html:html,js:js}]=_ref3;return _templates.default.replaceNodeContents(tab,html,js+responseJs)})).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)},openTab=tabName=>{const tab=(tabName=>document.querySelector(SELECTORS.forTabId(tabName)))(tabName);return!!tab&&(loadTab(tabName),tab.classList.add("active"),(tabName=>document.getElementById(tabName))(tabName).classList.add("active","show"),!0)},openTabFromHash=()=>{const hash=document.location.hash;return!!hash.match(/^#\w+$/g)&&openTab(hash.replace(/^#/g,""))}})); //# sourceMappingURL=dynamic_tabs.min.js.map \ No newline at end of file diff --git a/lib/amd/build/dynamic_tabs.min.js.map b/lib/amd/build/dynamic_tabs.min.js.map index c56ea03b0420e..0d15792fddc97 100644 --- a/lib/amd/build/dynamic_tabs.min.js.map +++ b/lib/amd/build/dynamic_tabs.min.js.map @@ -1 +1 @@ -{"version":3,"file":"dynamic_tabs.min.js","sources":["../src/dynamic_tabs.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 * Dynamic Tabs UI element with AJAX loading of tabs content\n *\n * @module core/dynamic_tabs\n * @copyright 2021 David Matamoros based on code from Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Templates from 'core/templates';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {getStrings} from 'core/str';\nimport {getContent} from 'core/local/repository/dynamic_tabs';\nimport {isAnyWatchedFormDirty, resetAllFormDirtyStates} from 'core_form/changechecker';\n\nconst SELECTORS = {\n dynamicTabs: '.dynamictabs',\n activeTab: '.dynamictabs .nav-link.active',\n allActiveTabs: '.dynamictabs .nav-link[data-bs-toggle=\"tab\"]:not(.disabled)',\n tabContent: '.dynamictabs .tab-pane [data-tab-content]',\n tabToggle: 'a[data-bs-toggle=\"tab\"]',\n tabPane: '.dynamictabs .tab-pane',\n};\n\nSELECTORS.forTabName = tabName => `.dynamictabs [data-tab-content=\"${tabName}\"]`;\nSELECTORS.forTabId = tabName => `.dynamictabs [data-bs-toggle=\"tab\"][href=\"#${tabName}\"]`;\n\n/**\n * Initialises the tabs view on the page (only one tabs view per page is supported)\n */\nexport const init = () => {\n const tabToggle = $(SELECTORS.tabToggle);\n\n // Listen to click, warn user if they are navigating away with unsaved form changes.\n tabToggle.on('click', (event) => {\n if (!isAnyWatchedFormDirty()) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n\n getStrings([\n {key: 'changesmade', component: 'moodle'},\n {key: 'changesmadereallygoaway', component: 'moodle'},\n {key: 'confirm', component: 'moodle'},\n ]).then(([strChangesMade, strChangesMadeReally, strConfirm]) =>\n // Reset form dirty state on confirmation, re-trigger the event.\n Notification.confirm(strChangesMade, strChangesMadeReally, strConfirm, null, () => {\n resetAllFormDirtyStates();\n $(event.target).trigger(event.type);\n })\n ).catch(Notification.exception);\n });\n\n // This code listens to Bootstrap events 'show.bs.tab' and 'shown.bs.tab' which is triggered using JQuery and\n // can not be converted yet to native events.\n tabToggle\n .on('show.bs.tab', function() {\n // Clean content from previous tab.\n const previousTabName = getActiveTabName();\n if (previousTabName) {\n const previousTab = document.querySelector(SELECTORS.forTabName(previousTabName));\n previousTab.textContent = '';\n }\n })\n .on('shown.bs.tab', function() {\n const tab = $($(this).attr('href'));\n if (tab.length !== 1) {\n return;\n }\n loadTab(tab.attr('id'));\n });\n\n if (!openTabFromHash()) {\n const tabs = document.querySelector(SELECTORS.allActiveTabs);\n if (tabs) {\n openTab(tabs.getAttribute('aria-controls'));\n } else {\n // We may hide tabs if there is only one available, just load the contents of the first tab.\n const tabPane = document.querySelector(SELECTORS.tabPane);\n if (tabPane) {\n tabPane.classList.add('active', 'show');\n loadTab(tabPane.getAttribute('id'));\n }\n }\n }\n};\n\n/**\n * Returns id/name of the currently active tab\n *\n * @return {String|null}\n */\nconst getActiveTabName = () => {\n const element = document.querySelector(SELECTORS.activeTab);\n return element?.getAttribute('aria-controls') || null;\n};\n\n/**\n * Returns the id/name of the first tab\n *\n * @return {String|null}\n */\nconst getFirstTabName = () => {\n const element = document.querySelector(SELECTORS.tabContent);\n return element?.dataset.tabContent || null;\n};\n\n/**\n * Loads contents of a tab using an AJAX request\n *\n * @param {String} tabName\n */\nconst loadTab = (tabName) => {\n // If tabName is not specified find the active tab, or if is not defined, the first available tab.\n tabName = tabName ?? getActiveTabName() ?? getFirstTabName();\n const tab = document.querySelector(SELECTORS.forTabName(tabName));\n if (!tab) {\n return;\n }\n\n const pendingPromise = new Pending('core/dynamic_tabs:loadTab:' + tabName);\n\n addIconToContainer(tab)\n .then(() => {\n let tabArgs = {...tab.dataset};\n delete tabArgs.tabClass;\n delete tabArgs.tabContent;\n return getContent(tab.dataset.tabClass, JSON.stringify(tabArgs));\n })\n .then(response => Promise.all([\n $.parseHTML(response.javascript, null, true).map(node => node.innerHTML).join(\"\\n\"),\n Templates.renderForPromise(response.template, JSON.parse(response.content)),\n ]))\n .then(([responseJs, {html, js}]) => Templates.replaceNodeContents(tab, html, js + responseJs))\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n};\n\n/**\n * Return the tab given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTab = (tabName) => {\n return document.querySelector(SELECTORS.forTabId(tabName));\n};\n\n/**\n * Return the tab pane given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTabPane = (tabName) => {\n return document.getElementById(tabName);\n};\n\n/**\n * Open the tab on page load. If this script loads before theme_boost/tab we need to open tab ourselves\n *\n * @param {String} tabName\n * @return {Boolean}\n */\nconst openTab = (tabName) => {\n const tab = getTab(tabName);\n if (!tab) {\n return false;\n }\n\n loadTab(tabName);\n tab.classList.add('active');\n getTabPane(tabName).classList.add('active', 'show');\n return true;\n};\n\n/**\n * If there is a location hash that is the same as the tab name - open this tab.\n *\n * @return {Boolean}\n */\nconst openTabFromHash = () => {\n const hash = document.location.hash;\n if (hash.match(/^#\\w+$/g)) {\n return openTab(hash.replace(/^#/g, ''));\n }\n\n return false;\n};\n"],"names":["SELECTORS","dynamicTabs","activeTab","allActiveTabs","tabContent","tabToggle","tabPane","tabName","on","event","preventDefault","stopPropagation","key","component","then","_ref","strChangesMade","strChangesMadeReally","strConfirm","Notification","confirm","target","trigger","type","catch","exception","previousTabName","getActiveTabName","document","querySelector","forTabName","textContent","tab","this","attr","length","loadTab","openTabFromHash","tabs","openTab","getAttribute","classList","add","element","dataset","getFirstTabName","pendingPromise","Pending","tabArgs","tabClass","JSON","stringify","response","Promise","all","$","parseHTML","javascript","map","node","innerHTML","join","Templates","renderForPromise","template","parse","content","_ref3","responseJs","html","js","replaceNodeContents","resolve","forTabId","getTab","getElementById","getTabPane","hash","location","match","replace"],"mappings":";;;;;;;4QAgCMA,UAAY,CACdC,YAAa,eACbC,UAAW,gCACXC,cAAe,8DACfC,WAAY,4CACZC,UAAW,0BACXC,QAAS,yBAGbN,WAAuBO,mDAA8CA,cACrEP,SAAqBO,8DAAyDA,6BAK1D,WACVF,WAAY,mBAAEL,UAAUK,cAG9BA,UAAUG,GAAG,SAAUC,SACd,4CAILA,MAAMC,iBACND,MAAME,sCAEK,CACP,CAACC,IAAK,cAAeC,UAAW,UAChC,CAACD,IAAK,0BAA2BC,UAAW,UAC5C,CAACD,IAAK,UAAWC,UAAW,YAC7BC,MAAKC,WAAEC,eAAgBC,qBAAsBC,wBAE5CC,sBAAaC,QAAQJ,eAAgBC,qBAAsBC,WAAY,MAAM,sEAEvET,MAAMY,QAAQC,QAAQb,MAAMc,YAEpCC,MAAML,sBAAaM,eAKzBpB,UACKG,GAAG,eAAe,iBAETkB,gBAAkBC,sBACpBD,gBAAiB,CACGE,SAASC,cAAc7B,UAAU8B,WAAWJ,kBACpDK,YAAc,OAGjCvB,GAAG,gBAAgB,iBACVwB,KAAM,oBAAE,mBAAEC,MAAMC,KAAK,SACR,IAAfF,IAAIG,QAGRC,QAAQJ,IAAIE,KAAK,WAGpBG,kBAAmB,OACdC,KAAOV,SAASC,cAAc7B,UAAUG,kBAC1CmC,KACAC,QAAQD,KAAKE,aAAa,sBACvB,OAEGlC,QAAUsB,SAASC,cAAc7B,UAAUM,SAC7CA,UACAA,QAAQmC,UAAUC,IAAI,SAAU,QAChCN,QAAQ9B,QAAQkC,aAAa,iBAWvCb,iBAAmB,WACfgB,QAAUf,SAASC,cAAc7B,UAAUE,kBAC1CyC,MAAAA,eAAAA,QAASH,aAAa,mBAAoB,MAkB/CJ,QAAW7B,6BAEbA,uCAAUA,qCAAWoB,0CAZD,YACdgB,QAAUf,SAASC,cAAc7B,UAAUI,mBAC1CuC,MAAAA,eAAAA,QAASC,QAAQxC,aAAc,MAUKyC,SACrCb,IAAMJ,SAASC,cAAc7B,UAAU8B,WAAWvB,cACnDyB,iBAICc,eAAiB,IAAIC,iBAAQ,6BAA+BxC,6CAE/CyB,KAClBlB,MAAK,SACEkC,QAAU,IAAIhB,IAAIY,uBACfI,QAAQC,gBACRD,QAAQ5C,YACR,4BAAW4B,IAAIY,QAAQK,SAAUC,KAAKC,UAAUH,aAE1DlC,MAAKsC,UAAYC,QAAQC,IAAI,CAC1BC,gBAAEC,UAAUJ,SAASK,WAAY,MAAM,GAAMC,KAAIC,MAAQA,KAAKC,YAAWC,KAAK,MAC9EC,mBAAUC,iBAAiBX,SAASY,SAAUd,KAAKe,MAAMb,SAASc,cAErEpD,MAAKqD,YAAEC,YAAYC,KAACA,KAADC,GAAOA,kBAASR,mBAAUS,oBAAoBvC,IAAKqC,KAAMC,GAAKF,eACjFtD,MAAK,IAAMgC,eAAe0B,YAC1BhD,MAAML,sBAAaM,YA6BlBc,QAAWhC,gBACPyB,IArBMzB,CAAAA,SACLqB,SAASC,cAAc7B,UAAUyE,SAASlE,UAoBrCmE,CAAOnE,iBACdyB,MAILI,QAAQ7B,SACRyB,IAAIS,UAAUC,IAAI,UAjBFnC,CAAAA,SACTqB,SAAS+C,eAAepE,SAiB/BqE,CAAWrE,SAASkC,UAAUC,IAAI,SAAU,SACrC,IAQLL,gBAAkB,WACdwC,KAAOjD,SAASkD,SAASD,aAC3BA,KAAKE,MAAM,YACJxC,QAAQsC,KAAKG,QAAQ,MAAO"} \ No newline at end of file +{"version":3,"file":"dynamic_tabs.min.js","sources":["../src/dynamic_tabs.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 * Dynamic Tabs UI element with AJAX loading of tabs content\n *\n * @module core/dynamic_tabs\n * @copyright 2021 David Matamoros based on code from Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Templates from 'core/templates';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {getStrings} from 'core/str';\nimport {getContent} from 'core/local/repository/dynamic_tabs';\nimport {isAnyWatchedFormDirty, resetAllFormDirtyStates} from 'core_form/changechecker';\n\nconst SELECTORS = {\n dynamicTabs: '.dynamictabs',\n activeTab: '.dynamictabs .nav-link.active',\n allActiveTabs: '.dynamictabs .nav-link[data-bs-toggle=\"tab\"]:not(.disabled)',\n tabContent: '.dynamictabs .tab-pane [data-tab-content]',\n tabToggle: 'a[data-bs-toggle=\"tab\"]',\n tabPane: '.dynamictabs .tab-pane',\n};\n\nSELECTORS.forTabName = tabName => `.dynamictabs [data-tab-content=\"${tabName}\"]`;\nSELECTORS.forTabId = tabName => `.dynamictabs [data-bs-toggle=\"tab\"][href=\"#${tabName}\"]`;\n\n/**\n * Initialises the tabs view on the page (only one tabs view per page is supported)\n */\nexport const init = () => {\n const tabToggles = document.querySelectorAll(SELECTORS.tabToggle);\n\n tabToggles.forEach(tabToggle => {\n // Listen to click, warn user if they are navigating away with unsaved form changes.\n tabToggle.addEventListener('click', (event) => {\n if (!isAnyWatchedFormDirty()) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n\n getStrings([\n {key: 'changesmade', component: 'moodle'},\n {key: 'changesmadereallygoaway', component: 'moodle'},\n {key: 'confirm', component: 'moodle'},\n ]).then(([strChangesMade, strChangesMadeReally, strConfirm]) =>\n // Reset form dirty state on confirmation, re-trigger the event.\n Notification.confirm(strChangesMade, strChangesMadeReally, strConfirm, null, () => {\n resetAllFormDirtyStates();\n $(event.target).trigger(event.type);\n })\n ).catch(Notification.exception);\n });\n\n tabToggle.addEventListener('show.bs.tab', () => {\n // Clean content from previous tab.\n const previousTabName = getActiveTabName();\n if (previousTabName) {\n const previousTab = document.querySelector(SELECTORS.forTabName(previousTabName));\n previousTab.textContent = '';\n }\n });\n\n tabToggle.addEventListener('shown.bs.tab', () => {\n const tabPane = document.getElementById(tabToggle.getAttribute('href').replace(/^#/, ''));\n if (tabPane) {\n loadTab(tabPane.id);\n }\n });\n });\n\n if (!openTabFromHash()) {\n const tabs = document.querySelector(SELECTORS.allActiveTabs);\n if (tabs) {\n openTab(tabs.getAttribute('aria-controls'));\n } else {\n // We may hide tabs if there is only one available, just load the contents of the first tab.\n const tabPane = document.querySelector(SELECTORS.tabPane);\n if (tabPane) {\n tabPane.classList.add('active', 'show');\n loadTab(tabPane.getAttribute('id'));\n }\n }\n }\n};\n\n/**\n * Returns id/name of the currently active tab\n *\n * @return {String|null}\n */\nconst getActiveTabName = () => {\n const element = document.querySelector(SELECTORS.activeTab);\n return element?.getAttribute('aria-controls') || null;\n};\n\n/**\n * Returns the id/name of the first tab\n *\n * @return {String|null}\n */\nconst getFirstTabName = () => {\n const element = document.querySelector(SELECTORS.tabContent);\n return element?.dataset.tabContent || null;\n};\n\n/**\n * Loads contents of a tab using an AJAX request\n *\n * @param {String} tabName\n */\nconst loadTab = (tabName) => {\n // If tabName is not specified find the active tab, or if is not defined, the first available tab.\n tabName = tabName ?? getActiveTabName() ?? getFirstTabName();\n const tab = document.querySelector(SELECTORS.forTabName(tabName));\n if (!tab) {\n return;\n }\n\n const pendingPromise = new Pending('core/dynamic_tabs:loadTab:' + tabName);\n\n addIconToContainer(tab)\n .then(() => {\n let tabArgs = {...tab.dataset};\n delete tabArgs.tabClass;\n delete tabArgs.tabContent;\n return getContent(tab.dataset.tabClass, JSON.stringify(tabArgs));\n })\n .then(response => Promise.all([\n $.parseHTML(response.javascript, null, true).map(node => node.innerHTML).join(\"\\n\"),\n Templates.renderForPromise(response.template, JSON.parse(response.content)),\n ]))\n .then(([responseJs, {html, js}]) => Templates.replaceNodeContents(tab, html, js + responseJs))\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n};\n\n/**\n * Return the tab given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTab = (tabName) => {\n return document.querySelector(SELECTORS.forTabId(tabName));\n};\n\n/**\n * Return the tab pane given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTabPane = (tabName) => {\n return document.getElementById(tabName);\n};\n\n/**\n * Open the tab on page load. If this script loads before theme_boost/tab we need to open tab ourselves\n *\n * @param {String} tabName\n * @return {Boolean}\n */\nconst openTab = (tabName) => {\n const tab = getTab(tabName);\n if (!tab) {\n return false;\n }\n\n loadTab(tabName);\n tab.classList.add('active');\n getTabPane(tabName).classList.add('active', 'show');\n return true;\n};\n\n/**\n * If there is a location hash that is the same as the tab name - open this tab.\n *\n * @return {Boolean}\n */\nconst openTabFromHash = () => {\n const hash = document.location.hash;\n if (hash.match(/^#\\w+$/g)) {\n return openTab(hash.replace(/^#/g, ''));\n }\n\n return false;\n};\n"],"names":["SELECTORS","dynamicTabs","activeTab","allActiveTabs","tabContent","tabToggle","tabPane","tabName","document","querySelectorAll","forEach","addEventListener","event","preventDefault","stopPropagation","key","component","then","_ref","strChangesMade","strChangesMadeReally","strConfirm","Notification","confirm","target","trigger","type","catch","exception","previousTabName","getActiveTabName","querySelector","forTabName","textContent","getElementById","getAttribute","replace","loadTab","id","openTabFromHash","tabs","openTab","classList","add","element","dataset","getFirstTabName","tab","pendingPromise","Pending","tabArgs","tabClass","JSON","stringify","response","Promise","all","$","parseHTML","javascript","map","node","innerHTML","join","Templates","renderForPromise","template","parse","content","_ref3","responseJs","html","js","replaceNodeContents","resolve","forTabId","getTab","getTabPane","hash","location","match"],"mappings":";;;;;;;4QAgCMA,UAAY,CACdC,YAAa,eACbC,UAAW,gCACXC,cAAe,8DACfC,WAAY,4CACZC,UAAW,0BACXC,QAAS,yBAGbN,WAAuBO,mDAA8CA,cACrEP,SAAqBO,8DAAyDA,6BAK1D,QACGC,SAASC,iBAAiBT,UAAUK,WAE5CK,SAAQL,YAEfA,UAAUM,iBAAiB,SAAUC,SAC5B,4CAILA,MAAMC,iBACND,MAAME,sCAEK,CACP,CAACC,IAAK,cAAeC,UAAW,UAChC,CAACD,IAAK,0BAA2BC,UAAW,UAC5C,CAACD,IAAK,UAAWC,UAAW,YAC7BC,MAAKC,WAAEC,eAAgBC,qBAAsBC,wBAE5CC,sBAAaC,QAAQJ,eAAgBC,qBAAsBC,WAAY,MAAM,sEAEvET,MAAMY,QAAQC,QAAQb,MAAMc,YAEpCC,MAAML,sBAAaM,eAGzBvB,UAAUM,iBAAiB,eAAe,WAEhCkB,gBAAkBC,sBACpBD,gBAAiB,CACGrB,SAASuB,cAAc/B,UAAUgC,WAAWH,kBACpDI,YAAc,OAIlC5B,UAAUM,iBAAiB,gBAAgB,WACjCL,QAAUE,SAAS0B,eAAe7B,UAAU8B,aAAa,QAAQC,QAAQ,KAAM,KACjF9B,SACA+B,QAAQ/B,QAAQgC,WAKvBC,kBAAmB,OACdC,KAAOhC,SAASuB,cAAc/B,UAAUG,kBAC1CqC,KACAC,QAAQD,KAAKL,aAAa,sBACvB,OAEG7B,QAAUE,SAASuB,cAAc/B,UAAUM,SAC7CA,UACAA,QAAQoC,UAAUC,IAAI,SAAU,QAChCN,QAAQ/B,QAAQ6B,aAAa,iBAWvCL,iBAAmB,WACfc,QAAUpC,SAASuB,cAAc/B,UAAUE,kBAC1C0C,MAAAA,eAAAA,QAAST,aAAa,mBAAoB,MAkB/CE,QAAW9B,6BAEbA,uCAAUA,qCAAWuB,0CAZD,YACdc,QAAUpC,SAASuB,cAAc/B,UAAUI,mBAC1CwC,MAAAA,eAAAA,QAASC,QAAQzC,aAAc,MAUK0C,SACrCC,IAAMvC,SAASuB,cAAc/B,UAAUgC,WAAWzB,cACnDwC,iBAICC,eAAiB,IAAIC,iBAAQ,6BAA+B1C,6CAE/CwC,KAClB9B,MAAK,SACEiC,QAAU,IAAIH,IAAIF,uBACfK,QAAQC,gBACRD,QAAQ9C,YACR,4BAAW2C,IAAIF,QAAQM,SAAUC,KAAKC,UAAUH,aAE1DjC,MAAKqC,UAAYC,QAAQC,IAAI,CAC1BC,gBAAEC,UAAUJ,SAASK,WAAY,MAAM,GAAMC,KAAIC,MAAQA,KAAKC,YAAWC,KAAK,MAC9EC,mBAAUC,iBAAiBX,SAASY,SAAUd,KAAKe,MAAMb,SAASc,cAErEnD,MAAKoD,YAAEC,YAAYC,KAACA,KAADC,GAAOA,kBAASR,mBAAUS,oBAAoB1B,IAAKwB,KAAMC,GAAKF,eACjFrD,MAAK,IAAM+B,eAAe0B,YAC1B/C,MAAML,sBAAaM,YA6BlBa,QAAWlC,gBACPwC,IArBMxC,CAAAA,SACLC,SAASuB,cAAc/B,UAAU2E,SAASpE,UAoBrCqE,CAAOrE,iBACdwC,MAILV,QAAQ9B,SACRwC,IAAIL,UAAUC,IAAI,UAjBFpC,CAAAA,SACTC,SAAS0B,eAAe3B,SAiB/BsE,CAAWtE,SAASmC,UAAUC,IAAI,SAAU,SACrC,IAQLJ,gBAAkB,WACduC,KAAOtE,SAASuE,SAASD,aAC3BA,KAAKE,MAAM,YACJvC,QAAQqC,KAAK1C,QAAQ,MAAO"} \ No newline at end of file diff --git a/lib/amd/build/moremenu.min.js b/lib/amd/build/moremenu.min.js index 840e36dfb1706..b1260db9988ce 100644 --- a/lib/amd/build/moremenu.min.js +++ b/lib/amd/build/moremenu.min.js @@ -6,6 +6,6 @@ define("core/moremenu",["exports","jquery","core/menu_navigation"],(function(_ex * @copyright 2021 Moodle * @author Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),_menu_navigation=_interopRequireDefault(_menu_navigation);const Selectors_regions={moredropdown:'[data-region="moredropdown"]',morebutton:'[data-region="morebutton"]'},Selectors_classes={dropdownitem:"dropdown-item",dropdownmoremenu:"dropdownmoremenu",hidden:"d-none",active:"active",nav:"nav",navlink:"nav-link",observed:"observed"},Selectors_attributes={menu:'[role="menu"]',dropdowntoggle:'[data-toggle="dropdown"]'};let isTabListMenu=!1;const autoCollapse=menu=>{const maxHeight=menu.parentNode.offsetHeight+1,moreDropdown=menu.querySelector(Selectors_regions.moredropdown),moreButton=menu.querySelector(Selectors_regions.morebutton);if(menu.offsetHeight>maxHeight){moreButton.classList.remove(Selectors_classes.hidden);let menuHeight=0;Array.from(menu.children).reverse().forEach((item=>{item.classList.contains(Selectors_classes.dropdownmoremenu)?menu.offsetHeight>maxHeight&&(menuHeight=menu.offsetHeight):menu.offsetHeight>maxHeight?moveIntoMoreDropdown(menu,item,!0):menuHeight>maxHeight&&(moveIntoMoreDropdown(menu,item,!0),menuHeight=0)}))}else"children"in moreDropdown&&(Array.from(moreDropdown.children).forEach((item=>{if(menu.offsetHeightmaxHeight&&autoCollapse(menu);menu.parentNode.classList.add(Selectors_classes.observed)},moveIntoMoreDropdown=function(menu,navNode){let prepend=arguments.length>2&&void 0!==arguments[2]&&arguments[2];const moreDropdown=menu.querySelector(Selectors_regions.moredropdown),dropdownToggle=menu.querySelector(Selectors_attributes.dropdowntoggle),navLink=navNode.querySelector("."+Selectors_classes.navlink);navLink.classList.contains(Selectors_classes.active)&&(dropdownToggle.classList.add(Selectors_classes.active),dropdownToggle.setAttribute("tabindex","0"),navLink.setAttribute("tabindex","-1"),isTabListMenu&&navLink.removeAttribute("aria-selected"),navLink.setAttribute("aria-current","true")),navLink.setAttribute("role","menuitem"),navLink.classList.remove(Selectors_classes.navlink),navLink.classList.add(Selectors_classes.dropdownitem),prepend?moreDropdown.prepend(navNode):moreDropdown.append(navNode)},moveOutOfMoreDropdown=(menu,navNode)=>{const moreButton=menu.querySelector(Selectors_regions.morebutton),dropdownToggle=menu.querySelector(Selectors_attributes.dropdowntoggle),navLink=navNode.querySelector("."+Selectors_classes.dropdownitem);isTabListMenu&&navLink.setAttribute("role","tab"),navLink.classList.contains(Selectors_classes.active)&&(dropdownToggle.classList.remove(Selectors_classes.active),dropdownToggle.setAttribute("tabindex","-1"),navLink.setAttribute("tabindex","0"),isTabListMenu&&(navLink.removeAttribute("aria-current"),navLink.setAttribute("aria-selected","true"))),navLink.classList.remove(Selectors_classes.dropdownitem),navLink.classList.add(Selectors_classes.navlink),menu.insertBefore(navNode,moreButton)};return _exports.default=menu=>{isTabListMenu="tablist"===menu.getAttribute("role");if(!window.location.hash){const itemRole=isTabListMenu?"tab":"menuitem",menuListItem=menu.firstElementChild,roleSelector="[role=".concat(itemRole,"]"),menuItem=menuListItem.querySelector(roleSelector),ariaAttribute=isTabListMenu?"aria-selected":"aria-current";menu.querySelector("[".concat(ariaAttribute,"='true']"))||(menuItem.setAttribute(ariaAttribute,"true"),menuItem.setAttribute("tabindex","0"))}if("children"in menu){const moreButton=menu.querySelector(Selectors_regions.morebutton);Array.from(menu.children).forEach((item=>{item.classList.contains(Selectors_classes.dropdownmoremenu)||"true"!==item.dataset.forceintomoremenu||(moveIntoMoreDropdown(menu,item,!1),moreButton.classList.contains(Selectors_classes.hidden)&&moreButton.classList.remove(Selectors_classes.hidden))}))}autoCollapse(menu),(0,_menu_navigation.default)(menu),window.addEventListener("resize",(()=>{autoCollapse(menu),(0,_menu_navigation.default)(menu)}));const toggledropdown=e=>{const innerMenu=e.target.parentNode.querySelector(Selectors_attributes.menu);innerMenu&&innerMenu.classList.toggle("show"),e.stopPropagation()};(0,_jquery.default)("."+Selectors_classes.dropdownmoremenu).on("show.bs.dropdown",(function(){menu.querySelector(Selectors_regions.moredropdown).querySelectorAll(".dropdown").forEach((dropdown=>{dropdown.removeEventListener("click",toggledropdown,!0),dropdown.addEventListener("click",toggledropdown,!0)}))}))},_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),_menu_navigation=_interopRequireDefault(_menu_navigation);const Selectors_regions={moredropdown:'[data-region="moredropdown"]',morebutton:'[data-region="morebutton"]'},Selectors_classes={dropdownitem:"dropdown-item",dropdownmoremenu:"dropdownmoremenu",hidden:"d-none",active:"active",nav:"nav",navlink:"nav-link",observed:"observed"},Selectors_attributes={menu:'[role="menu"]',dropdowntoggle:'[data-bs-toggle="dropdown"]'};let isTabListMenu=!1;const autoCollapse=menu=>{const maxHeight=menu.parentNode.offsetHeight+1,moreDropdown=menu.querySelector(Selectors_regions.moredropdown),moreButton=menu.querySelector(Selectors_regions.morebutton);if(menu.offsetHeight>maxHeight){moreButton.classList.remove(Selectors_classes.hidden);let menuHeight=0;Array.from(menu.children).reverse().forEach((item=>{item.classList.contains(Selectors_classes.dropdownmoremenu)?menu.offsetHeight>maxHeight&&(menuHeight=menu.offsetHeight):menu.offsetHeight>maxHeight?moveIntoMoreDropdown(menu,item,!0):menuHeight>maxHeight&&(moveIntoMoreDropdown(menu,item,!0),menuHeight=0)}))}else"children"in moreDropdown&&(Array.from(moreDropdown.children).forEach((item=>{if(menu.offsetHeightmaxHeight&&autoCollapse(menu);menu.parentNode.classList.add(Selectors_classes.observed)},moveIntoMoreDropdown=function(menu,navNode){let prepend=arguments.length>2&&void 0!==arguments[2]&&arguments[2];const moreDropdown=menu.querySelector(Selectors_regions.moredropdown),dropdownToggle=menu.querySelector(Selectors_attributes.dropdowntoggle),navLink=navNode.querySelector("."+Selectors_classes.navlink);navLink.classList.contains(Selectors_classes.active)&&(dropdownToggle.classList.add(Selectors_classes.active),dropdownToggle.setAttribute("tabindex","0"),navLink.setAttribute("tabindex","-1"),isTabListMenu&&navLink.removeAttribute("aria-selected"),navLink.setAttribute("aria-current","true")),navLink.setAttribute("role","menuitem"),navLink.classList.remove(Selectors_classes.navlink),navLink.classList.add(Selectors_classes.dropdownitem),prepend?moreDropdown.prepend(navNode):moreDropdown.append(navNode)},moveOutOfMoreDropdown=(menu,navNode)=>{const moreButton=menu.querySelector(Selectors_regions.morebutton),dropdownToggle=menu.querySelector(Selectors_attributes.dropdowntoggle),navLink=navNode.querySelector("."+Selectors_classes.dropdownitem);isTabListMenu&&navLink.setAttribute("role","tab"),navLink.classList.contains(Selectors_classes.active)&&(dropdownToggle.classList.remove(Selectors_classes.active),dropdownToggle.setAttribute("tabindex","-1"),navLink.setAttribute("tabindex","0"),isTabListMenu&&(navLink.removeAttribute("aria-current"),navLink.setAttribute("aria-selected","true"))),navLink.classList.remove(Selectors_classes.dropdownitem),navLink.classList.add(Selectors_classes.navlink),menu.insertBefore(navNode,moreButton)};return _exports.default=menu=>{isTabListMenu="tablist"===menu.getAttribute("role");if(!window.location.hash){const itemRole=isTabListMenu?"tab":"menuitem",menuListItem=menu.firstElementChild,roleSelector="[role=".concat(itemRole,"]"),menuItem=menuListItem.querySelector(roleSelector),ariaAttribute=isTabListMenu?"aria-selected":"aria-current";menu.querySelector("[".concat(ariaAttribute,"='true']"))||(menuItem.setAttribute(ariaAttribute,"true"),menuItem.setAttribute("tabindex","0"))}if("children"in menu){const moreButton=menu.querySelector(Selectors_regions.morebutton);Array.from(menu.children).forEach((item=>{item.classList.contains(Selectors_classes.dropdownmoremenu)||"true"!==item.dataset.forceintomoremenu||(moveIntoMoreDropdown(menu,item,!1),moreButton.classList.contains(Selectors_classes.hidden)&&moreButton.classList.remove(Selectors_classes.hidden))}))}autoCollapse(menu),(0,_menu_navigation.default)(menu),window.addEventListener("resize",(()=>{autoCollapse(menu),(0,_menu_navigation.default)(menu)}));const toggledropdown=e=>{const innerMenu=e.target.parentNode.querySelector(Selectors_attributes.menu);innerMenu&&innerMenu.classList.toggle("show"),e.stopPropagation()};(0,_jquery.default)("."+Selectors_classes.dropdownmoremenu).on("show.bs.dropdown",(function(){menu.querySelector(Selectors_regions.moredropdown).querySelectorAll(".dropdown").forEach((dropdown=>{dropdown.removeEventListener("click",toggledropdown,!0),dropdown.addEventListener("click",toggledropdown,!0)}))}))},_exports.default})); //# sourceMappingURL=moremenu.min.js.map \ No newline at end of file diff --git a/lib/amd/build/moremenu.min.js.map b/lib/amd/build/moremenu.min.js.map index e1dfa582fd331..371e2c00cde86 100644 --- a/lib/amd/build/moremenu.min.js.map +++ b/lib/amd/build/moremenu.min.js.map @@ -1 +1 @@ -{"version":3,"file":"moremenu.min.js","sources":["../src/moremenu.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 * Moves wrapping navigation items into a more menu.\n *\n * @module core/moremenu\n * @copyright 2021 Moodle\n * @author Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport menu_navigation from \"core/menu_navigation\";\n/**\n * Moremenu selectors.\n */\nconst Selectors = {\n regions: {\n moredropdown: '[data-region=\"moredropdown\"]',\n morebutton: '[data-region=\"morebutton\"]'\n },\n classes: {\n dropdownitem: 'dropdown-item',\n dropdownmoremenu: 'dropdownmoremenu',\n hidden: 'd-none',\n active: 'active',\n nav: 'nav',\n navlink: 'nav-link',\n observed: 'observed',\n },\n attributes: {\n menu: '[role=\"menu\"]',\n dropdowntoggle: '[data-toggle=\"dropdown\"]'\n }\n};\n\nlet isTabListMenu = false;\n\n/**\n * Auto Collapse navigation items that wrap into a dropdown menu.\n *\n * @param {HTMLElement} menu The navbar container.\n */\nconst autoCollapse = menu => {\n\n const maxHeight = menu.parentNode.offsetHeight + 1;\n\n const moreDropdown = menu.querySelector(Selectors.regions.moredropdown);\n const moreButton = menu.querySelector(Selectors.regions.morebutton);\n\n // If the menu items wrap and the menu height is larger than the height of the\n // parent then start pushing navlinks into the moreDropdown.\n if (menu.offsetHeight > maxHeight) {\n moreButton.classList.remove(Selectors.classes.hidden);\n\n let menuHeight = 0;\n const menuNodes = Array.from(menu.children).reverse();\n menuNodes.forEach(item => {\n if (!item.classList.contains(Selectors.classes.dropdownmoremenu)) {\n // After moving the menu items into the moreDropdown check again\n // if the menu height is still larger then the height of the parent.\n if (menu.offsetHeight > maxHeight) {\n // Move this node into the more dropdown menu.\n moveIntoMoreDropdown(menu, item, true);\n } else if (menuHeight > maxHeight) {\n moveIntoMoreDropdown(menu, item, true);\n menuHeight = 0;\n }\n } else if (menu.offsetHeight > maxHeight) {\n // Assign menu height to be used to check with menu parent.\n menuHeight = menu.offsetHeight;\n }\n });\n } else {\n // If the menu height is smaller than the height of the parent, then try returning navlinks to the menu.\n if ('children' in moreDropdown) {\n // Iterate through the nodes within the more dropdown menu.\n Array.from(moreDropdown.children).forEach(item => {\n // Don't move the node to the more menu if it is explicitly defined that\n // this node should be displayed in the more dropdown menu at all times.\n if (menu.offsetHeight < maxHeight && item.dataset.forceintomoremenu !== 'true') {\n const lastNode = moreDropdown.removeChild(item);\n // Move this node from the more dropdown menu into the main section of the menu.\n moveOutOfMoreDropdown(menu, lastNode);\n }\n });\n // If there are no more nodes in the more dropdown menu we can hide the moreButton.\n if (Array.from(moreDropdown.children).length === 0) {\n moreButton.classList.add(Selectors.classes.hidden);\n }\n }\n\n if (menu.offsetHeight > maxHeight) {\n autoCollapse(menu);\n }\n }\n menu.parentNode.classList.add(Selectors.classes.observed);\n};\n\n/**\n * Move a node into the \"more\" dropdown menu.\n *\n * This method forces a given navigation node to be added and displayed within the \"more\" dropdown menu.\n *\n * @param {HTMLElement} menu The navbar moremenu.\n * @param {HTMLElement} navNode The navigation node.\n * @param {boolean} prepend Whether to prepend or append the node to the content in the more dropdown menu.\n */\nconst moveIntoMoreDropdown = (menu, navNode, prepend = false) => {\n const moreDropdown = menu.querySelector(Selectors.regions.moredropdown);\n const dropdownToggle = menu.querySelector(Selectors.attributes.dropdowntoggle);\n\n const navLink = navNode.querySelector('.' + Selectors.classes.navlink);\n // If there are navLinks that contain an active link in the moreDropdown\n // make the dropdownToggle in the moreButton active.\n if (navLink.classList.contains(Selectors.classes.active)) {\n dropdownToggle.classList.add(Selectors.classes.active);\n dropdownToggle.setAttribute('tabindex', '0');\n navLink.setAttribute('tabindex', '-1'); // So that we don't have a single tabbable menu item.\n // Remove aria-selected if the more menu is rendered as a tab list.\n if (isTabListMenu) {\n navLink.removeAttribute('aria-selected');\n }\n navLink.setAttribute('aria-current', 'true');\n }\n\n // This will become a menu item instead of a tab.\n navLink.setAttribute('role', 'menuitem');\n\n // Change the styling of the navLink to a dropdownitem and push it into\n // the moreDropdown.\n navLink.classList.remove(Selectors.classes.navlink);\n navLink.classList.add(Selectors.classes.dropdownitem);\n if (prepend) {\n moreDropdown.prepend(navNode);\n } else {\n moreDropdown.append(navNode);\n }\n};\n\n/**\n * Move a node out of the \"more\" dropdown menu.\n *\n * This method forces a given node from the \"more\" dropdown menu to be displayed in the main section of the menu.\n *\n * @param {HTMLElement} menu The navbar moremenu.\n * @param {HTMLElement} navNode The navigation node.\n */\nconst moveOutOfMoreDropdown = (menu, navNode) => {\n const moreButton = menu.querySelector(Selectors.regions.morebutton);\n const dropdownToggle = menu.querySelector(Selectors.attributes.dropdowntoggle);\n const navLink = navNode.querySelector('.' + Selectors.classes.dropdownitem);\n\n // If the more menu is rendered as a tab list,\n // this will become a tab instead of a menuitem when moved out of the more menu dropdown.\n if (isTabListMenu) {\n navLink.setAttribute('role', 'tab');\n }\n\n // Stop displaying the active state on the dropdownToggle if\n // the active navlink is removed.\n if (navLink.classList.contains(Selectors.classes.active)) {\n dropdownToggle.classList.remove(Selectors.classes.active);\n dropdownToggle.setAttribute('tabindex', '-1');\n navLink.setAttribute('tabindex', '0');\n if (isTabListMenu) {\n // Replace aria selection state when necessary.\n navLink.removeAttribute('aria-current');\n navLink.setAttribute('aria-selected', 'true');\n }\n }\n navLink.classList.remove(Selectors.classes.dropdownitem);\n navLink.classList.add(Selectors.classes.navlink);\n menu.insertBefore(navNode, moreButton);\n};\n\n/**\n * Initialise the more menus.\n *\n * @param {HTMLElement} menu The navbar moremenu.\n */\nexport default menu => {\n isTabListMenu = menu.getAttribute('role') === 'tablist';\n\n // Select the first menu item if there's nothing initially selected.\n const hash = window.location.hash;\n if (!hash) {\n const itemRole = isTabListMenu ? 'tab' : 'menuitem';\n const menuListItem = menu.firstElementChild;\n const roleSelector = `[role=${itemRole}]`;\n const menuItem = menuListItem.querySelector(roleSelector);\n const ariaAttribute = isTabListMenu ? 'aria-selected' : 'aria-current';\n if (!menu.querySelector(`[${ariaAttribute}='true']`)) {\n menuItem.setAttribute(ariaAttribute, 'true');\n menuItem.setAttribute('tabindex', '0');\n }\n }\n\n // Pre-populate the \"more\" dropdown menu with navigation nodes which are set to be displayed in this menu\n // by default at all times.\n if ('children' in menu) {\n const moreButton = menu.querySelector(Selectors.regions.morebutton);\n const menuNodes = Array.from(menu.children);\n menuNodes.forEach((item) => {\n if (!item.classList.contains(Selectors.classes.dropdownmoremenu) &&\n item.dataset.forceintomoremenu === 'true') {\n // Append this node into the more dropdown menu.\n moveIntoMoreDropdown(menu, item, false);\n // After adding the node into the more dropdown menu, make sure that the more dropdown menu button\n // is displayed.\n if (moreButton.classList.contains(Selectors.classes.hidden)) {\n moreButton.classList.remove(Selectors.classes.hidden);\n }\n }\n });\n }\n // Populate the more dropdown menu with additional nodes if necessary, depending on the current screen size.\n autoCollapse(menu);\n menu_navigation(menu);\n\n // When the screen size changes make sure the menu still fits.\n window.addEventListener('resize', () => {\n autoCollapse(menu);\n menu_navigation(menu);\n });\n\n const toggledropdown = e => {\n const innerMenu = e.target.parentNode.querySelector(Selectors.attributes.menu);\n if (innerMenu) {\n innerMenu.classList.toggle('show');\n }\n e.stopPropagation();\n };\n\n // If there are dropdowns in the MoreMenu, add a new\n // event listener to show the contents on click and prevent the\n // moreMenu from closing.\n $('.' + Selectors.classes.dropdownmoremenu).on('show.bs.dropdown', function() {\n const moreDropdown = menu.querySelector(Selectors.regions.moredropdown);\n moreDropdown.querySelectorAll('.dropdown').forEach((dropdown) => {\n dropdown.removeEventListener('click', toggledropdown, true);\n dropdown.addEventListener('click', toggledropdown, true);\n });\n });\n};\n"],"names":["Selectors","moredropdown","morebutton","dropdownitem","dropdownmoremenu","hidden","active","nav","navlink","observed","menu","dropdowntoggle","isTabListMenu","autoCollapse","maxHeight","parentNode","offsetHeight","moreDropdown","querySelector","moreButton","classList","remove","menuHeight","Array","from","children","reverse","forEach","item","contains","moveIntoMoreDropdown","dataset","forceintomoremenu","lastNode","removeChild","moveOutOfMoreDropdown","length","add","navNode","prepend","dropdownToggle","navLink","setAttribute","removeAttribute","append","insertBefore","getAttribute","window","location","hash","itemRole","menuListItem","firstElementChild","roleSelector","menuItem","ariaAttribute","addEventListener","toggledropdown","e","innerMenu","target","toggle","stopPropagation","on","querySelectorAll","dropdown","removeEventListener"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"moremenu.min.js","sources":["../src/moremenu.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 * Moves wrapping navigation items into a more menu.\n *\n * @module core/moremenu\n * @copyright 2021 Moodle\n * @author Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport menu_navigation from \"core/menu_navigation\";\n/**\n * Moremenu selectors.\n */\nconst Selectors = {\n regions: {\n moredropdown: '[data-region=\"moredropdown\"]',\n morebutton: '[data-region=\"morebutton\"]'\n },\n classes: {\n dropdownitem: 'dropdown-item',\n dropdownmoremenu: 'dropdownmoremenu',\n hidden: 'd-none',\n active: 'active',\n nav: 'nav',\n navlink: 'nav-link',\n observed: 'observed',\n },\n attributes: {\n menu: '[role=\"menu\"]',\n dropdowntoggle: '[data-bs-toggle=\"dropdown\"]'\n }\n};\n\nlet isTabListMenu = false;\n\n/**\n * Auto Collapse navigation items that wrap into a dropdown menu.\n *\n * @param {HTMLElement} menu The navbar container.\n */\nconst autoCollapse = menu => {\n\n const maxHeight = menu.parentNode.offsetHeight + 1;\n\n const moreDropdown = menu.querySelector(Selectors.regions.moredropdown);\n const moreButton = menu.querySelector(Selectors.regions.morebutton);\n\n // If the menu items wrap and the menu height is larger than the height of the\n // parent then start pushing navlinks into the moreDropdown.\n if (menu.offsetHeight > maxHeight) {\n moreButton.classList.remove(Selectors.classes.hidden);\n\n let menuHeight = 0;\n const menuNodes = Array.from(menu.children).reverse();\n menuNodes.forEach(item => {\n if (!item.classList.contains(Selectors.classes.dropdownmoremenu)) {\n // After moving the menu items into the moreDropdown check again\n // if the menu height is still larger then the height of the parent.\n if (menu.offsetHeight > maxHeight) {\n // Move this node into the more dropdown menu.\n moveIntoMoreDropdown(menu, item, true);\n } else if (menuHeight > maxHeight) {\n moveIntoMoreDropdown(menu, item, true);\n menuHeight = 0;\n }\n } else if (menu.offsetHeight > maxHeight) {\n // Assign menu height to be used to check with menu parent.\n menuHeight = menu.offsetHeight;\n }\n });\n } else {\n // If the menu height is smaller than the height of the parent, then try returning navlinks to the menu.\n if ('children' in moreDropdown) {\n // Iterate through the nodes within the more dropdown menu.\n Array.from(moreDropdown.children).forEach(item => {\n // Don't move the node to the more menu if it is explicitly defined that\n // this node should be displayed in the more dropdown menu at all times.\n if (menu.offsetHeight < maxHeight && item.dataset.forceintomoremenu !== 'true') {\n const lastNode = moreDropdown.removeChild(item);\n // Move this node from the more dropdown menu into the main section of the menu.\n moveOutOfMoreDropdown(menu, lastNode);\n }\n });\n // If there are no more nodes in the more dropdown menu we can hide the moreButton.\n if (Array.from(moreDropdown.children).length === 0) {\n moreButton.classList.add(Selectors.classes.hidden);\n }\n }\n\n if (menu.offsetHeight > maxHeight) {\n autoCollapse(menu);\n }\n }\n menu.parentNode.classList.add(Selectors.classes.observed);\n};\n\n/**\n * Move a node into the \"more\" dropdown menu.\n *\n * This method forces a given navigation node to be added and displayed within the \"more\" dropdown menu.\n *\n * @param {HTMLElement} menu The navbar moremenu.\n * @param {HTMLElement} navNode The navigation node.\n * @param {boolean} prepend Whether to prepend or append the node to the content in the more dropdown menu.\n */\nconst moveIntoMoreDropdown = (menu, navNode, prepend = false) => {\n const moreDropdown = menu.querySelector(Selectors.regions.moredropdown);\n const dropdownToggle = menu.querySelector(Selectors.attributes.dropdowntoggle);\n\n const navLink = navNode.querySelector('.' + Selectors.classes.navlink);\n // If there are navLinks that contain an active link in the moreDropdown\n // make the dropdownToggle in the moreButton active.\n if (navLink.classList.contains(Selectors.classes.active)) {\n dropdownToggle.classList.add(Selectors.classes.active);\n dropdownToggle.setAttribute('tabindex', '0');\n navLink.setAttribute('tabindex', '-1'); // So that we don't have a single tabbable menu item.\n // Remove aria-selected if the more menu is rendered as a tab list.\n if (isTabListMenu) {\n navLink.removeAttribute('aria-selected');\n }\n navLink.setAttribute('aria-current', 'true');\n }\n\n // This will become a menu item instead of a tab.\n navLink.setAttribute('role', 'menuitem');\n\n // Change the styling of the navLink to a dropdownitem and push it into\n // the moreDropdown.\n navLink.classList.remove(Selectors.classes.navlink);\n navLink.classList.add(Selectors.classes.dropdownitem);\n if (prepend) {\n moreDropdown.prepend(navNode);\n } else {\n moreDropdown.append(navNode);\n }\n};\n\n/**\n * Move a node out of the \"more\" dropdown menu.\n *\n * This method forces a given node from the \"more\" dropdown menu to be displayed in the main section of the menu.\n *\n * @param {HTMLElement} menu The navbar moremenu.\n * @param {HTMLElement} navNode The navigation node.\n */\nconst moveOutOfMoreDropdown = (menu, navNode) => {\n const moreButton = menu.querySelector(Selectors.regions.morebutton);\n const dropdownToggle = menu.querySelector(Selectors.attributes.dropdowntoggle);\n const navLink = navNode.querySelector('.' + Selectors.classes.dropdownitem);\n\n // If the more menu is rendered as a tab list,\n // this will become a tab instead of a menuitem when moved out of the more menu dropdown.\n if (isTabListMenu) {\n navLink.setAttribute('role', 'tab');\n }\n\n // Stop displaying the active state on the dropdownToggle if\n // the active navlink is removed.\n if (navLink.classList.contains(Selectors.classes.active)) {\n dropdownToggle.classList.remove(Selectors.classes.active);\n dropdownToggle.setAttribute('tabindex', '-1');\n navLink.setAttribute('tabindex', '0');\n if (isTabListMenu) {\n // Replace aria selection state when necessary.\n navLink.removeAttribute('aria-current');\n navLink.setAttribute('aria-selected', 'true');\n }\n }\n navLink.classList.remove(Selectors.classes.dropdownitem);\n navLink.classList.add(Selectors.classes.navlink);\n menu.insertBefore(navNode, moreButton);\n};\n\n/**\n * Initialise the more menus.\n *\n * @param {HTMLElement} menu The navbar moremenu.\n */\nexport default menu => {\n isTabListMenu = menu.getAttribute('role') === 'tablist';\n\n // Select the first menu item if there's nothing initially selected.\n const hash = window.location.hash;\n if (!hash) {\n const itemRole = isTabListMenu ? 'tab' : 'menuitem';\n const menuListItem = menu.firstElementChild;\n const roleSelector = `[role=${itemRole}]`;\n const menuItem = menuListItem.querySelector(roleSelector);\n const ariaAttribute = isTabListMenu ? 'aria-selected' : 'aria-current';\n if (!menu.querySelector(`[${ariaAttribute}='true']`)) {\n menuItem.setAttribute(ariaAttribute, 'true');\n menuItem.setAttribute('tabindex', '0');\n }\n }\n\n // Pre-populate the \"more\" dropdown menu with navigation nodes which are set to be displayed in this menu\n // by default at all times.\n if ('children' in menu) {\n const moreButton = menu.querySelector(Selectors.regions.morebutton);\n const menuNodes = Array.from(menu.children);\n menuNodes.forEach((item) => {\n if (!item.classList.contains(Selectors.classes.dropdownmoremenu) &&\n item.dataset.forceintomoremenu === 'true') {\n // Append this node into the more dropdown menu.\n moveIntoMoreDropdown(menu, item, false);\n // After adding the node into the more dropdown menu, make sure that the more dropdown menu button\n // is displayed.\n if (moreButton.classList.contains(Selectors.classes.hidden)) {\n moreButton.classList.remove(Selectors.classes.hidden);\n }\n }\n });\n }\n // Populate the more dropdown menu with additional nodes if necessary, depending on the current screen size.\n autoCollapse(menu);\n menu_navigation(menu);\n\n // When the screen size changes make sure the menu still fits.\n window.addEventListener('resize', () => {\n autoCollapse(menu);\n menu_navigation(menu);\n });\n\n const toggledropdown = e => {\n const innerMenu = e.target.parentNode.querySelector(Selectors.attributes.menu);\n if (innerMenu) {\n innerMenu.classList.toggle('show');\n }\n e.stopPropagation();\n };\n\n // If there are dropdowns in the MoreMenu, add a new\n // event listener to show the contents on click and prevent the\n // moreMenu from closing.\n $('.' + Selectors.classes.dropdownmoremenu).on('show.bs.dropdown', function() {\n const moreDropdown = menu.querySelector(Selectors.regions.moredropdown);\n moreDropdown.querySelectorAll('.dropdown').forEach((dropdown) => {\n dropdown.removeEventListener('click', toggledropdown, true);\n dropdown.addEventListener('click', toggledropdown, true);\n });\n });\n};\n"],"names":["Selectors","moredropdown","morebutton","dropdownitem","dropdownmoremenu","hidden","active","nav","navlink","observed","menu","dropdowntoggle","isTabListMenu","autoCollapse","maxHeight","parentNode","offsetHeight","moreDropdown","querySelector","moreButton","classList","remove","menuHeight","Array","from","children","reverse","forEach","item","contains","moveIntoMoreDropdown","dataset","forceintomoremenu","lastNode","removeChild","moveOutOfMoreDropdown","length","add","navNode","prepend","dropdownToggle","navLink","setAttribute","removeAttribute","append","insertBefore","getAttribute","window","location","hash","itemRole","menuListItem","firstElementChild","roleSelector","menuItem","ariaAttribute","addEventListener","toggledropdown","e","innerMenu","target","toggle","stopPropagation","on","querySelectorAll","dropdown","removeEventListener"],"mappings":"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"} \ No newline at end of file diff --git a/lib/amd/src/dynamic_tabs.js b/lib/amd/src/dynamic_tabs.js index 577a0535e0350..7f1140995dd5e 100644 --- a/lib/amd/src/dynamic_tabs.js +++ b/lib/amd/src/dynamic_tabs.js @@ -46,48 +46,47 @@ SELECTORS.forTabId = tabName => `.dynamictabs [data-bs-toggle="tab"][href="#${ta * Initialises the tabs view on the page (only one tabs view per page is supported) */ export const init = () => { - const tabToggle = $(SELECTORS.tabToggle); + const tabToggles = document.querySelectorAll(SELECTORS.tabToggle); - // Listen to click, warn user if they are navigating away with unsaved form changes. - tabToggle.on('click', (event) => { - if (!isAnyWatchedFormDirty()) { - return; - } + tabToggles.forEach(tabToggle => { + // Listen to click, warn user if they are navigating away with unsaved form changes. + tabToggle.addEventListener('click', (event) => { + if (!isAnyWatchedFormDirty()) { + return; + } - event.preventDefault(); - event.stopPropagation(); - - getStrings([ - {key: 'changesmade', component: 'moodle'}, - {key: 'changesmadereallygoaway', component: 'moodle'}, - {key: 'confirm', component: 'moodle'}, - ]).then(([strChangesMade, strChangesMadeReally, strConfirm]) => - // Reset form dirty state on confirmation, re-trigger the event. - Notification.confirm(strChangesMade, strChangesMadeReally, strConfirm, null, () => { - resetAllFormDirtyStates(); - $(event.target).trigger(event.type); - }) - ).catch(Notification.exception); - }); + event.preventDefault(); + event.stopPropagation(); + + getStrings([ + {key: 'changesmade', component: 'moodle'}, + {key: 'changesmadereallygoaway', component: 'moodle'}, + {key: 'confirm', component: 'moodle'}, + ]).then(([strChangesMade, strChangesMadeReally, strConfirm]) => + // Reset form dirty state on confirmation, re-trigger the event. + Notification.confirm(strChangesMade, strChangesMadeReally, strConfirm, null, () => { + resetAllFormDirtyStates(); + $(event.target).trigger(event.type); + }) + ).catch(Notification.exception); + }); - // This code listens to Bootstrap events 'show.bs.tab' and 'shown.bs.tab' which is triggered using JQuery and - // can not be converted yet to native events. - tabToggle - .on('show.bs.tab', function() { + tabToggle.addEventListener('show.bs.tab', () => { // Clean content from previous tab. const previousTabName = getActiveTabName(); if (previousTabName) { const previousTab = document.querySelector(SELECTORS.forTabName(previousTabName)); previousTab.textContent = ''; } - }) - .on('shown.bs.tab', function() { - const tab = $($(this).attr('href')); - if (tab.length !== 1) { - return; + }); + + tabToggle.addEventListener('shown.bs.tab', () => { + const tabPane = document.getElementById(tabToggle.getAttribute('href').replace(/^#/, '')); + if (tabPane) { + loadTab(tabPane.id); } - loadTab(tab.attr('id')); }); + }); if (!openTabFromHash()) { const tabs = document.querySelector(SELECTORS.allActiveTabs); diff --git a/lib/amd/src/moremenu.js b/lib/amd/src/moremenu.js index 425a947afbe4f..f00625ceb9e2d 100644 --- a/lib/amd/src/moremenu.js +++ b/lib/amd/src/moremenu.js @@ -43,7 +43,7 @@ const Selectors = { }, attributes: { menu: '[role="menu"]', - dropdowntoggle: '[data-toggle="dropdown"]' + dropdowntoggle: '[data-bs-toggle="dropdown"]' } }; diff --git a/lib/form/amd/build/collapsesections.min.js b/lib/form/amd/build/collapsesections.min.js index ecd8ae941eba8..610259e9b186f 100644 --- a/lib/form/amd/build/collapsesections.min.js +++ b/lib/form/amd/build/collapsesections.min.js @@ -1,4 +1,4 @@ -define("core_form/collapsesections",["exports","jquery","core/pending"],(function(_exports,_jquery,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("core_form/collapsesections",["exports","theme_boost/bootstrap/collapse","core/pending"],(function(_exports,_collapse,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Collapse or expand all form sections on clicking the expand all / collapse al link. * @@ -6,6 +6,6 @@ define("core_form/collapsesections",["exports","jquery","core/pending"],(functio * @copyright 2021 Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 4.0 - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_pending=_interopRequireDefault(_pending);const SELECTORS_FORM=".mform",SELECTORS_FORMHEADER=".fheader",SELECTORS_FORMCONTAINER="fieldset > .fcontainer",CLASSES_SHOW="show",CLASSES_COLLAPSED="collapsed",CLASSES_HIDDEN="d-none";_exports.init=collapsesections=>{const pendingPromise=new _pending.default("core_form/collapsesections"),collapsemenu=document.querySelector(collapsesections),formParent=collapsemenu.closest(SELECTORS_FORM),formContainers=formParent.querySelectorAll(SELECTORS_FORMCONTAINER);collapsemenu.addEventListener("keydown",(e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),collapsemenu.click())}));let formcontainercount=0,expandedcount=0;formContainers.forEach((container=>{container.parentElement.classList.contains(CLASSES_HIDDEN)||formcontainercount++,container.classList.contains(CLASSES_SHOW)&&expandedcount++})),formcontainercount===expandedcount&&(collapsemenu.classList.remove(CLASSES_COLLAPSED),collapsemenu.setAttribute("aria-expanded",!0)),collapsemenu.addEventListener("click",(()=>{let action="hide";collapsemenu.classList.contains(CLASSES_COLLAPSED)&&(action="show"),formContainers.forEach((container=>(0,_jquery.default)(container).collapse(action)))}));const collapseElementIds=[...formParent.querySelectorAll(SELECTORS_FORMHEADER)].map(((element,index)=>(element.id=element.id||"collapseElement-".concat(index),element.id)));collapsemenu.setAttribute("aria-controls",collapseElementIds.join(" ")),(0,_jquery.default)(SELECTORS_FORMCONTAINER).on("hidden.bs.collapse",(()=>{[...formContainers].every((container=>!container.classList.contains(CLASSES_SHOW)))&&(collapsemenu.classList.add(CLASSES_COLLAPSED),collapsemenu.setAttribute("aria-expanded",!1))})),(0,_jquery.default)(SELECTORS_FORMCONTAINER).on("shown.bs.collapse",(()=>{[...formContainers].every((container=>container.classList.contains(CLASSES_SHOW)))&&(collapsemenu.classList.remove(CLASSES_COLLAPSED),collapsemenu.setAttribute("aria-expanded",!0))})),pendingPromise.resolve()}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_collapse=_interopRequireDefault(_collapse),_pending=_interopRequireDefault(_pending);const SELECTORS_FORM=".mform",SELECTORS_FORMHEADER=".fheader",SELECTORS_FORMCONTAINER="fieldset > .fcontainer",CLASSES_SHOW="show",CLASSES_COLLAPSED="collapsed",CLASSES_HIDDEN="d-none";_exports.init=collapsesections=>{const pendingPromise=new _pending.default("core_form/collapsesections"),collapsemenu=document.querySelector(collapsesections),formParent=collapsemenu.closest(SELECTORS_FORM),formContainers=formParent.querySelectorAll(SELECTORS_FORMCONTAINER);[...formContainers].map((formContainer=>new _collapse.default(formContainer,{toggle:!1}))),collapsemenu.addEventListener("keydown",(e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),collapsemenu.click())}));let formcontainercount=0,expandedcount=0;formContainers.forEach((container=>{container.parentElement.classList.contains(CLASSES_HIDDEN)||formcontainercount++,container.classList.contains(CLASSES_SHOW)&&expandedcount++})),formcontainercount===expandedcount&&(collapsemenu.classList.remove(CLASSES_COLLAPSED),collapsemenu.setAttribute("aria-expanded",!0)),collapsemenu.addEventListener("click",(()=>{collapsemenu.classList.contains(CLASSES_COLLAPSED)?formContainers.forEach((container=>_collapse.default.getInstance(container).show())):formContainers.forEach((container=>_collapse.default.getInstance(container).hide()))}));const collapseElementIds=[...formParent.querySelectorAll(SELECTORS_FORMHEADER)].map(((element,index)=>(element.id=element.id||"collapseElement-".concat(index),element.id)));collapsemenu.setAttribute("aria-controls",collapseElementIds.join(" "));[...document.querySelectorAll(SELECTORS_FORMCONTAINER)].forEach((collapseTriggerEl=>{collapseTriggerEl.addEventListener("hidden.bs.collapse",(()=>{[...formContainers].every((container=>!container.classList.contains(CLASSES_SHOW)))&&(collapsemenu.classList.add(CLASSES_COLLAPSED),collapsemenu.setAttribute("aria-expanded",!1))})),collapseTriggerEl.addEventListener("shown.bs.collapse",(()=>{[...formContainers].every((container=>container.classList.contains(CLASSES_SHOW)))&&(collapsemenu.classList.remove(CLASSES_COLLAPSED),collapsemenu.setAttribute("aria-expanded",!0))}))})),pendingPromise.resolve()}})); //# sourceMappingURL=collapsesections.min.js.map \ No newline at end of file diff --git a/lib/form/amd/build/collapsesections.min.js.map b/lib/form/amd/build/collapsesections.min.js.map index 5d32da8449c69..66e9f8743afef 100644 --- a/lib/form/amd/build/collapsesections.min.js.map +++ b/lib/form/amd/build/collapsesections.min.js.map @@ -1 +1 @@ -{"version":3,"file":"collapsesections.min.js","sources":["../src/collapsesections.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 * Collapse or expand all form sections on clicking the expand all / collapse al link.\n *\n * @module core_form/collapsesections\n * @copyright 2021 Bas Brands\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n */\n\nimport $ from 'jquery';\nimport Pending from 'core/pending';\n\nconst SELECTORS = {\n FORM: '.mform',\n FORMHEADER: '.fheader',\n FORMCONTAINER: 'fieldset > .fcontainer',\n};\n\nconst CLASSES = {\n SHOW: 'show',\n COLLAPSED: 'collapsed',\n HIDDEN: 'd-none'\n};\n\n/**\n * Initialises the form section collapse / expand action.\n *\n * @param {string} collapsesections the collapse/expand link id.\n */\nexport const init = collapsesections => {\n // All jQuery in this code can be replaced when MDL-71979 is integrated (move to Bootstrap 5).\n const pendingPromise = new Pending('core_form/collapsesections');\n const collapsemenu = document.querySelector(collapsesections);\n\n const formParent = collapsemenu.closest(SELECTORS.FORM);\n const formContainers = formParent.querySelectorAll(SELECTORS.FORMCONTAINER);\n\n collapsemenu.addEventListener('keydown', e => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n collapsemenu.click();\n }\n });\n\n // Override default collapse class if all visible containers are expanded on page load\n let formcontainercount = 0;\n let expandedcount = 0;\n formContainers.forEach(container => {\n const parentFieldset = container.parentElement;\n if (!parentFieldset.classList.contains(CLASSES.HIDDEN)) {\n formcontainercount++;\n }\n if (container.classList.contains(CLASSES.SHOW)) {\n expandedcount++;\n }\n });\n\n if (formcontainercount === expandedcount) {\n collapsemenu.classList.remove(CLASSES.COLLAPSED);\n collapsemenu.setAttribute('aria-expanded', true);\n }\n\n // When the collapse menu is toggled, update each form container to match.\n collapsemenu.addEventListener('click', () => {\n let action = 'hide';\n if (collapsemenu.classList.contains(CLASSES.COLLAPSED)) {\n action = 'show';\n }\n\n formContainers.forEach(container => $(container).collapse(action));\n });\n\n // Ensure collapse menu button adds aria-controls attribute referring to each collapsible element.\n const collapseElements = formParent.querySelectorAll(SELECTORS.FORMHEADER);\n const collapseElementIds = [...collapseElements].map((element, index) => {\n element.id = element.id || `collapseElement-${index}`;\n return element.id;\n });\n collapsemenu.setAttribute('aria-controls', collapseElementIds.join(' '));\n\n // When any form container is toggled, re-calculate collapse menu state.\n $(SELECTORS.FORMCONTAINER).on('hidden.bs.collapse', () => {\n const allCollapsed = [...formContainers].every(container => !container.classList.contains(CLASSES.SHOW));\n if (allCollapsed) {\n collapsemenu.classList.add(CLASSES.COLLAPSED);\n collapsemenu.setAttribute('aria-expanded', false);\n }\n });\n $(SELECTORS.FORMCONTAINER).on('shown.bs.collapse', () => {\n const allExpanded = [...formContainers].every(container => container.classList.contains(CLASSES.SHOW));\n if (allExpanded) {\n collapsemenu.classList.remove(CLASSES.COLLAPSED);\n collapsemenu.setAttribute('aria-expanded', true);\n }\n });\n pendingPromise.resolve();\n};\n"],"names":["SELECTORS","CLASSES","collapsesections","pendingPromise","Pending","collapsemenu","document","querySelector","formParent","closest","formContainers","querySelectorAll","addEventListener","e","key","preventDefault","click","formcontainercount","expandedcount","forEach","container","parentElement","classList","contains","remove","setAttribute","action","collapse","collapseElementIds","map","element","index","id","join","on","every","add","resolve"],"mappings":";;;;;;;;0KA2BMA,eACI,SADJA,qBAEU,WAFVA,wBAGa,yBAGbC,aACI,OADJA,kBAES,YAFTA,eAGM,uBAQQC,yBAEVC,eAAiB,IAAIC,iBAAQ,8BAC7BC,aAAeC,SAASC,cAAcL,kBAEtCM,WAAaH,aAAaI,QAAQT,gBAClCU,eAAiBF,WAAWG,iBAAiBX,yBAEnDK,aAAaO,iBAAiB,WAAWC,IACvB,UAAVA,EAAEC,KAA6B,MAAVD,EAAEC,MACvBD,EAAEE,iBACFV,aAAaW,gBAKjBC,mBAAqB,EACrBC,cAAgB,EACpBR,eAAeS,SAAQC,YACIA,UAAUC,cACbC,UAAUC,SAAStB,iBACnCgB,qBAEAG,UAAUE,UAAUC,SAAStB,eAC7BiB,mBAIJD,qBAAuBC,gBACvBb,aAAaiB,UAAUE,OAAOvB,mBAC9BI,aAAaoB,aAAa,iBAAiB,IAI/CpB,aAAaO,iBAAiB,SAAS,SAC/Bc,OAAS,OACTrB,aAAaiB,UAAUC,SAAStB,qBAChCyB,OAAS,QAGbhB,eAAeS,SAAQC,YAAa,mBAAEA,WAAWO,SAASD,mBAKxDE,mBAAqB,IADFpB,WAAWG,iBAAiBX,uBACJ6B,KAAI,CAACC,QAASC,SAC3DD,QAAQE,GAAKF,QAAQE,8BAAyBD,OACvCD,QAAQE,MAEnB3B,aAAaoB,aAAa,gBAAiBG,mBAAmBK,KAAK,0BAGjEjC,yBAAyBkC,GAAG,sBAAsB,KAC3B,IAAIxB,gBAAgByB,OAAMf,YAAcA,UAAUE,UAAUC,SAAStB,kBAEtFI,aAAaiB,UAAUc,IAAInC,mBAC3BI,aAAaoB,aAAa,iBAAiB,2BAGjDzB,yBAAyBkC,GAAG,qBAAqB,KAC3B,IAAIxB,gBAAgByB,OAAMf,WAAaA,UAAUE,UAAUC,SAAStB,kBAEpFI,aAAaiB,UAAUE,OAAOvB,mBAC9BI,aAAaoB,aAAa,iBAAiB,OAGnDtB,eAAekC"} \ No newline at end of file +{"version":3,"file":"collapsesections.min.js","sources":["../src/collapsesections.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 * Collapse or expand all form sections on clicking the expand all / collapse al link.\n *\n * @module core_form/collapsesections\n * @copyright 2021 Bas Brands\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n */\n\nimport Collapse from 'theme_boost/bootstrap/collapse';\nimport Pending from 'core/pending';\n\nconst SELECTORS = {\n FORM: '.mform',\n FORMHEADER: '.fheader',\n FORMCONTAINER: 'fieldset > .fcontainer',\n};\n\nconst CLASSES = {\n SHOW: 'show',\n COLLAPSED: 'collapsed',\n HIDDEN: 'd-none'\n};\n\n/**\n * Initialises the form section collapse / expand action.\n *\n * @param {string} collapsesections the collapse/expand link id.\n */\nexport const init = collapsesections => {\n const pendingPromise = new Pending('core_form/collapsesections');\n const collapsemenu = document.querySelector(collapsesections);\n\n const formParent = collapsemenu.closest(SELECTORS.FORM);\n const formContainers = formParent.querySelectorAll(SELECTORS.FORMCONTAINER);\n [...formContainers].map(formContainer => new Collapse(formContainer, {toggle: false}));\n\n collapsemenu.addEventListener('keydown', e => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n collapsemenu.click();\n }\n });\n\n // Override default collapse class if all visible containers are expanded on page load\n let formcontainercount = 0;\n let expandedcount = 0;\n formContainers.forEach(container => {\n const parentFieldset = container.parentElement;\n if (!parentFieldset.classList.contains(CLASSES.HIDDEN)) {\n formcontainercount++;\n }\n if (container.classList.contains(CLASSES.SHOW)) {\n expandedcount++;\n }\n });\n\n if (formcontainercount === expandedcount) {\n collapsemenu.classList.remove(CLASSES.COLLAPSED);\n collapsemenu.setAttribute('aria-expanded', true);\n }\n\n // When the collapse menu is toggled, update each form container to match.\n collapsemenu.addEventListener('click', () => {\n if (collapsemenu.classList.contains(CLASSES.COLLAPSED)) {\n formContainers.forEach(container => Collapse.getInstance(container).show());\n } else {\n formContainers.forEach(container => Collapse.getInstance(container).hide());\n }\n });\n\n // Ensure collapse menu button adds aria-controls attribute referring to each collapsible element.\n const collapseElements = formParent.querySelectorAll(SELECTORS.FORMHEADER);\n const collapseElementIds = [...collapseElements].map((element, index) => {\n element.id = element.id || `collapseElement-${index}`;\n return element.id;\n });\n collapsemenu.setAttribute('aria-controls', collapseElementIds.join(' '));\n\n // When any form container is toggled, re-calculate collapse menu state.\n const collapseTriggerList = document.querySelectorAll(SELECTORS.FORMCONTAINER);\n [...collapseTriggerList].forEach(collapseTriggerEl => {\n collapseTriggerEl.addEventListener('hidden.bs.collapse', () => {\n const allCollapsed = [...formContainers].every(container => !container.classList.contains(CLASSES.SHOW));\n if (allCollapsed) {\n collapsemenu.classList.add(CLASSES.COLLAPSED);\n collapsemenu.setAttribute('aria-expanded', false);\n }\n });\n collapseTriggerEl.addEventListener('shown.bs.collapse', () => {\n const allExpanded = [...formContainers].every(container => container.classList.contains(CLASSES.SHOW));\n if (allExpanded) {\n collapsemenu.classList.remove(CLASSES.COLLAPSED);\n collapsemenu.setAttribute('aria-expanded', true);\n }\n });\n });\n pendingPromise.resolve();\n};\n"],"names":["SELECTORS","CLASSES","collapsesections","pendingPromise","Pending","collapsemenu","document","querySelector","formParent","closest","formContainers","querySelectorAll","map","formContainer","Collapse","toggle","addEventListener","e","key","preventDefault","click","formcontainercount","expandedcount","forEach","container","parentElement","classList","contains","remove","setAttribute","getInstance","show","hide","collapseElementIds","element","index","id","join","collapseTriggerEl","every","add","resolve"],"mappings":";;;;;;;;8KA2BMA,eACI,SADJA,qBAEU,WAFVA,wBAGa,yBAGbC,aACI,OADJA,kBAES,YAFTA,eAGM,uBAQQC,yBACVC,eAAiB,IAAIC,iBAAQ,8BAC7BC,aAAeC,SAASC,cAAcL,kBAEtCM,WAAaH,aAAaI,QAAQT,gBAClCU,eAAiBF,WAAWG,iBAAiBX,6BAC/CU,gBAAgBE,KAAIC,eAAiB,IAAIC,kBAASD,cAAe,CAACE,QAAQ,MAE9EV,aAAaW,iBAAiB,WAAWC,IACvB,UAAVA,EAAEC,KAA6B,MAAVD,EAAEC,MACvBD,EAAEE,iBACFd,aAAae,gBAKjBC,mBAAqB,EACrBC,cAAgB,EACpBZ,eAAea,SAAQC,YACIA,UAAUC,cACbC,UAAUC,SAAS1B,iBACnCoB,qBAEAG,UAAUE,UAAUC,SAAS1B,eAC7BqB,mBAIJD,qBAAuBC,gBACvBjB,aAAaqB,UAAUE,OAAO3B,mBAC9BI,aAAawB,aAAa,iBAAiB,IAI/CxB,aAAaW,iBAAiB,SAAS,KAC/BX,aAAaqB,UAAUC,SAAS1B,mBAChCS,eAAea,SAAQC,WAAaV,kBAASgB,YAAYN,WAAWO,SAEpErB,eAAea,SAAQC,WAAaV,kBAASgB,YAAYN,WAAWQ,kBAMtEC,mBAAqB,IADFzB,WAAWG,iBAAiBX,uBACJY,KAAI,CAACsB,QAASC,SAC3DD,QAAQE,GAAKF,QAAQE,8BAAyBD,OACvCD,QAAQE,MAEnB/B,aAAawB,aAAa,gBAAiBI,mBAAmBI,KAAK,UAGvC/B,SAASK,iBAAiBX,0BAC7BuB,SAAQe,oBAC7BA,kBAAkBtB,iBAAiB,sBAAsB,KAChC,IAAIN,gBAAgB6B,OAAMf,YAAcA,UAAUE,UAAUC,SAAS1B,kBAEtFI,aAAaqB,UAAUc,IAAIvC,mBAC3BI,aAAawB,aAAa,iBAAiB,OAGnDS,kBAAkBtB,iBAAiB,qBAAqB,KAChC,IAAIN,gBAAgB6B,OAAMf,WAAaA,UAAUE,UAAUC,SAAS1B,kBAEpFI,aAAaqB,UAAUE,OAAO3B,mBAC9BI,aAAawB,aAAa,iBAAiB,UAIvD1B,eAAesC"} \ No newline at end of file diff --git a/lib/form/amd/src/collapsesections.js b/lib/form/amd/src/collapsesections.js index b2d3127c79dcc..416ce26812f4d 100644 --- a/lib/form/amd/src/collapsesections.js +++ b/lib/form/amd/src/collapsesections.js @@ -22,7 +22,7 @@ * @since 4.0 */ -import $ from 'jquery'; +import Collapse from 'theme_boost/bootstrap/collapse'; import Pending from 'core/pending'; const SELECTORS = { @@ -43,12 +43,12 @@ const CLASSES = { * @param {string} collapsesections the collapse/expand link id. */ export const init = collapsesections => { - // All jQuery in this code can be replaced when MDL-71979 is integrated (move to Bootstrap 5). const pendingPromise = new Pending('core_form/collapsesections'); const collapsemenu = document.querySelector(collapsesections); const formParent = collapsemenu.closest(SELECTORS.FORM); const formContainers = formParent.querySelectorAll(SELECTORS.FORMCONTAINER); + [...formContainers].map(formContainer => new Collapse(formContainer, {toggle: false})); collapsemenu.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { @@ -77,12 +77,11 @@ export const init = collapsesections => { // When the collapse menu is toggled, update each form container to match. collapsemenu.addEventListener('click', () => { - let action = 'hide'; if (collapsemenu.classList.contains(CLASSES.COLLAPSED)) { - action = 'show'; + formContainers.forEach(container => Collapse.getInstance(container).show()); + } else { + formContainers.forEach(container => Collapse.getInstance(container).hide()); } - - formContainers.forEach(container => $(container).collapse(action)); }); // Ensure collapse menu button adds aria-controls attribute referring to each collapsible element. @@ -94,19 +93,22 @@ export const init = collapsesections => { collapsemenu.setAttribute('aria-controls', collapseElementIds.join(' ')); // When any form container is toggled, re-calculate collapse menu state. - $(SELECTORS.FORMCONTAINER).on('hidden.bs.collapse', () => { - const allCollapsed = [...formContainers].every(container => !container.classList.contains(CLASSES.SHOW)); - if (allCollapsed) { - collapsemenu.classList.add(CLASSES.COLLAPSED); - collapsemenu.setAttribute('aria-expanded', false); - } - }); - $(SELECTORS.FORMCONTAINER).on('shown.bs.collapse', () => { - const allExpanded = [...formContainers].every(container => container.classList.contains(CLASSES.SHOW)); - if (allExpanded) { - collapsemenu.classList.remove(CLASSES.COLLAPSED); - collapsemenu.setAttribute('aria-expanded', true); - } + const collapseTriggerList = document.querySelectorAll(SELECTORS.FORMCONTAINER); + [...collapseTriggerList].forEach(collapseTriggerEl => { + collapseTriggerEl.addEventListener('hidden.bs.collapse', () => { + const allCollapsed = [...formContainers].every(container => !container.classList.contains(CLASSES.SHOW)); + if (allCollapsed) { + collapsemenu.classList.add(CLASSES.COLLAPSED); + collapsemenu.setAttribute('aria-expanded', false); + } + }); + collapseTriggerEl.addEventListener('shown.bs.collapse', () => { + const allExpanded = [...formContainers].every(container => container.classList.contains(CLASSES.SHOW)); + if (allExpanded) { + collapsemenu.classList.remove(CLASSES.COLLAPSED); + collapsemenu.setAttribute('aria-expanded', true); + } + }); }); pendingPromise.resolve(); }; diff --git a/lib/templates/help_icon.mustache b/lib/templates/help_icon.mustache index e62c95880e27b..12a7543cf0edb 100644 --- a/lib/templates/help_icon.mustache +++ b/lib/templates/help_icon.mustache @@ -18,8 +18,8 @@ } }} + data-bs-container="body" data-bs-toggle="popover" + data-bs-placement="{{#ltr}}right{{/ltr}}{{^ltr}}left{{/ltr}}" data-bs-content="{{text}} {{completedoclink}}" + data-bs-html="true" tabindex="0" data-bs-trigger="focus" aria-label="{{#str}} help {{/str}}"> {{#pix}}help, core, {{{alt}}}{{/pix}} diff --git a/lib/templates/local/toast/message.mustache b/lib/templates/local/toast/message.mustache index 22ca62453a8fc..10597f2065f29 100644 --- a/lib/templates/local/toast/message.mustache +++ b/lib/templates/local/toast/message.mustache @@ -53,13 +53,14 @@
{{#js}} -require(['jquery', 'theme_boost/bootstrap/toast'], function(jQuery) { +require(['theme_boost/bootstrap/toast'], function(Toast) { // Show the toast. - // Bootstrap toast components are not shown automatically. - jQuery('#toast-{{uniqid}}').toast('show'); + const toastTrigger = document.getElementById('toast-{{uniqid}}'); + new Toast(toastTrigger).show(); - jQuery('#toast-{{uniqid}}').on('hidden.bs.toast', function(e) { - e.target.remove(); + // Remove the toast from the DOM when it is hidden. + toastTrigger.addEventListener('hidden.bs.toast', function() { + toastTrigger.remove(); }); }); {{/js}} diff --git a/question/bank/managecategories/amd/build/newchild.min.js b/question/bank/managecategories/amd/build/newchild.min.js index c62fee98e5ba9..99c65d05be590 100644 --- a/question/bank/managecategories/amd/build/newchild.min.js +++ b/question/bank/managecategories/amd/build/newchild.min.js @@ -1,3 +1,3 @@ -define("qbank_managecategories/newchild",["exports","core/reactive","jquery","qbank_managecategories/categorymanager"],(function(_exports,_reactive,_jquery,_categorymanager){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj};class _default extends _reactive.BaseComponent{create(descriptor){this.name=descriptor.element.id,this.selectors={NEW_CHILD:".qbank_managecategories-newchild",CATEGORY_ID:id=>"#category-".concat(id)},this.classes={DROP_TARGET:"qbank_managecategories-droptarget"},this.ids={CATEGORY:id=>"category-".concat(id)}}stateReady(){this.dragdrop=new _reactive.DragDrop(this)}destroy(){void 0!==this.dragdrop&&(this.dragdrop.unregister(),this.dragdrop=void 0)}static init(target,selectors){return new this({element:document.querySelector(target),selectors:selectors,reactive:_categorymanager.categorymanager})}validateDropData(dropData){return!this.getElement().closest(this.selectors.CATEGORY_ID(dropData.id))}showDropZone(dropData,event){const dropTarget=event.target.closest(this.selectors.NEW_CHILD);dropTarget.classList.add(this.classes.DROP_TARGET),(0,_jquery.default)(dropTarget).tooltip("show")}hideDropZone(dropData,event){const dropTarget=event.target.closest(this.selectors.NEW_CHILD);dropTarget.classList.remove(this.classes.DROP_TARGET),(0,_jquery.default)(dropTarget).tooltip("hide")}drop(dropData,event){const dropTarget=event.target.closest(this.selectors.NEW_CHILD);if(!dropTarget)return;if(!document.getElementById(this.ids.CATEGORY(dropData.id)))return;const targetParentId=dropTarget.dataset.parent;_categorymanager.categorymanager.moveCategory(dropData.id,targetParentId)}getWatchers(){return[{watch:"categories.parent:updated",handler:this.checkNewChild}]}checkNewChild(_ref){let{element:element}=_ref;element.parent===parseInt(this.element.dataset.parent)&&this.remove()}}return _exports.default=_default,_exports.default})); +define("qbank_managecategories/newchild",["exports","core/reactive","qbank_managecategories/categorymanager","theme_boost/bootstrap/tooltip"],(function(_exports,_reactive,_categorymanager,_tooltip){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_tooltip=(obj=_tooltip)&&obj.__esModule?obj:{default:obj};class _default extends _reactive.BaseComponent{create(descriptor){this.name=descriptor.element.id,this.selectors={NEW_CHILD:".qbank_managecategories-newchild",CATEGORY_ID:id=>"#category-".concat(id)},this.classes={DROP_TARGET:"qbank_managecategories-droptarget"},this.ids={CATEGORY:id=>"category-".concat(id)}}stateReady(){this.dragdrop=new _reactive.DragDrop(this)}destroy(){void 0!==this.dragdrop&&(this.dragdrop.unregister(),this.dragdrop=void 0)}static init(target,selectors){return new this({element:document.querySelector(target),selectors:selectors,reactive:_categorymanager.categorymanager})}validateDropData(dropData){return!this.getElement().closest(this.selectors.CATEGORY_ID(dropData.id))}showDropZone(dropData,event){const dropTarget=event.target.closest(this.selectors.NEW_CHILD);dropTarget.classList.add(this.classes.DROP_TARGET),_tooltip.default.getOrCreateInstance(dropTarget).show()}hideDropZone(dropData,event){const dropTarget=event.target.closest(this.selectors.NEW_CHILD);dropTarget.classList.remove(this.classes.DROP_TARGET),_tooltip.default.getOrCreateInstance(dropTarget).hide()}drop(dropData,event){const dropTarget=event.target.closest(this.selectors.NEW_CHILD);if(!dropTarget)return;if(!document.getElementById(this.ids.CATEGORY(dropData.id)))return;const targetParentId=dropTarget.dataset.parent;_categorymanager.categorymanager.moveCategory(dropData.id,targetParentId)}getWatchers(){return[{watch:"categories.parent:updated",handler:this.checkNewChild}]}checkNewChild(_ref){let{element:element}=_ref;element.parent===parseInt(this.element.dataset.parent)&&this.remove()}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=newchild.min.js.map \ No newline at end of file diff --git a/question/bank/managecategories/amd/build/newchild.min.js.map b/question/bank/managecategories/amd/build/newchild.min.js.map index 91a8c9b7996fb..e0735cf5b87d6 100644 --- a/question/bank/managecategories/amd/build/newchild.min.js.map +++ b/question/bank/managecategories/amd/build/newchild.min.js.map @@ -1 +1 @@ -{"version":3,"file":"newchild.min.js","sources":["../src/newchild.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 * The newchild component.\n *\n * This is a drop target for moving a category to an as-yet-nonexistant child list under another category.\n *\n * @module qbank_managecategories/newchild\n * @class qbank_managecategories/newchild\n */\n\nimport {BaseComponent, DragDrop} from 'core/reactive';\nimport $ from 'jquery';\nimport {categorymanager} from 'qbank_managecategories/categorymanager';\n\nexport default class extends BaseComponent {\n create(descriptor) {\n this.name = descriptor.element.id;\n this.selectors = {\n NEW_CHILD: '.qbank_managecategories-newchild',\n CATEGORY_ID: id => `#category-${id}`\n };\n this.classes = {\n DROP_TARGET: 'qbank_managecategories-droptarget',\n };\n this.ids = {\n CATEGORY: id => `category-${id}`,\n };\n }\n\n stateReady() {\n this.dragdrop = new DragDrop(this);\n }\n\n destroy() {\n // The draggable element must be unregistered.\n if (this.dragdrop !== undefined) {\n this.dragdrop.unregister();\n this.dragdrop = undefined;\n }\n }\n\n /**\n * Static method to create a component instance form the mustache template.\n *\n * @param {string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n const targetElement = document.querySelector(target);\n return new this({\n element: targetElement,\n selectors,\n reactive: categorymanager,\n });\n }\n\n /**\n * Cannot drop a category as a new child of its own descendant.\n *\n * @param {Object} dropData\n * @return {boolean}\n */\n validateDropData(dropData) {\n if (this.getElement().closest(this.selectors.CATEGORY_ID(dropData.id))) {\n return false;\n }\n return true;\n }\n\n showDropZone(dropData, event) {\n const dropTarget = event.target.closest(this.selectors.NEW_CHILD);\n dropTarget.classList.add(this.classes.DROP_TARGET);\n $(dropTarget).tooltip('show');\n }\n\n hideDropZone(dropData, event) {\n const dropTarget = event.target.closest(this.selectors.NEW_CHILD);\n dropTarget.classList.remove(this.classes.DROP_TARGET);\n $(dropTarget).tooltip('hide');\n }\n\n drop(dropData, event) {\n const dropTarget = event.target.closest(this.selectors.NEW_CHILD);\n\n if (!dropTarget) {\n return;\n }\n\n const source = document.getElementById(this.ids.CATEGORY(dropData.id));\n\n if (!source) {\n return;\n }\n\n const targetParentId = dropTarget.dataset.parent;\n\n // Insert the category as the first child of the new parent.\n categorymanager.moveCategory(dropData.id, targetParentId);\n }\n\n /**\n * Watch for categories moving to a new parent.\n *\n * @return {Array} A list of watchers.\n */\n getWatchers() {\n return [\n // Watch for any category having its parent changed.\n {watch: `categories.parent:updated`, handler: this.checkNewChild},\n ];\n }\n\n /**\n * If an element now has this category as the parent, remove this new child target.\n *\n * @param {Object} args\n * @param {Element} args.element\n */\n checkNewChild({element}) {\n if (element.parent === parseInt(this.element.dataset.parent)) {\n this.remove();\n }\n }\n}\n"],"names":["BaseComponent","create","descriptor","name","element","id","selectors","NEW_CHILD","CATEGORY_ID","classes","DROP_TARGET","ids","CATEGORY","stateReady","dragdrop","DragDrop","this","destroy","undefined","unregister","target","document","querySelector","reactive","categorymanager","validateDropData","dropData","getElement","closest","showDropZone","event","dropTarget","classList","add","tooltip","hideDropZone","remove","drop","getElementById","targetParentId","dataset","parent","moveCategory","getWatchers","watch","handler","checkNewChild","parseInt"],"mappings":"qVA4B6BA,wBACzBC,OAAOC,iBACEC,KAAOD,WAAWE,QAAQC,QAC1BC,UAAY,CACbC,UAAW,mCACXC,YAAaH,wBAAmBA,UAE/BI,QAAU,CACXC,YAAa,0CAEZC,IAAM,CACPC,SAAUP,uBAAkBA,KAIpCQ,kBACSC,SAAW,IAAIC,mBAASC,MAGjCC,eAE0BC,IAAlBF,KAAKF,gBACAA,SAASK,kBACTL,cAAWI,eAWZE,OAAQd,kBAET,IAAIU,KAAK,CACZZ,QAFkBiB,SAASC,cAAcF,QAGzCd,UAAAA,UACAiB,SAAUC,mCAUlBC,iBAAiBC,iBACTV,KAAKW,aAAaC,QAAQZ,KAAKV,UAAUE,YAAYkB,SAASrB,KAMtEwB,aAAaH,SAAUI,aACbC,WAAaD,MAAMV,OAAOQ,QAAQZ,KAAKV,UAAUC,WACvDwB,WAAWC,UAAUC,IAAIjB,KAAKP,QAAQC,iCACpCqB,YAAYG,QAAQ,QAG1BC,aAAaT,SAAUI,aACbC,WAAaD,MAAMV,OAAOQ,QAAQZ,KAAKV,UAAUC,WACvDwB,WAAWC,UAAUI,OAAOpB,KAAKP,QAAQC,iCACvCqB,YAAYG,QAAQ,QAG1BG,KAAKX,SAAUI,aACLC,WAAaD,MAAMV,OAAOQ,QAAQZ,KAAKV,UAAUC,eAElDwB,sBAIUV,SAASiB,eAAetB,KAAKL,IAAIC,SAASc,SAASrB,kBAM5DkC,eAAiBR,WAAWS,QAAQC,wCAG1BC,aAAahB,SAASrB,GAAIkC,gBAQ9CI,oBACW,CAEH,CAACC,kCAAoCC,QAAS7B,KAAK8B,gBAU3DA,wBAAc1C,QAACA,cACPA,QAAQqC,SAAWM,SAAS/B,KAAKZ,QAAQoC,QAAQC,cAC5CL"} \ No newline at end of file +{"version":3,"file":"newchild.min.js","sources":["../src/newchild.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 * The newchild component.\n *\n * This is a drop target for moving a category to an as-yet-nonexistant child list under another category.\n *\n * @module qbank_managecategories/newchild\n * @class qbank_managecategories/newchild\n */\n\nimport {BaseComponent, DragDrop} from 'core/reactive';\nimport {categorymanager} from 'qbank_managecategories/categorymanager';\nimport Tooltip from 'theme_boost/bootstrap/tooltip';\n\nexport default class extends BaseComponent {\n create(descriptor) {\n this.name = descriptor.element.id;\n this.selectors = {\n NEW_CHILD: '.qbank_managecategories-newchild',\n CATEGORY_ID: id => `#category-${id}`\n };\n this.classes = {\n DROP_TARGET: 'qbank_managecategories-droptarget',\n };\n this.ids = {\n CATEGORY: id => `category-${id}`,\n };\n }\n\n stateReady() {\n this.dragdrop = new DragDrop(this);\n }\n\n destroy() {\n // The draggable element must be unregistered.\n if (this.dragdrop !== undefined) {\n this.dragdrop.unregister();\n this.dragdrop = undefined;\n }\n }\n\n /**\n * Static method to create a component instance form the mustache template.\n *\n * @param {string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n const targetElement = document.querySelector(target);\n return new this({\n element: targetElement,\n selectors,\n reactive: categorymanager,\n });\n }\n\n /**\n * Cannot drop a category as a new child of its own descendant.\n *\n * @param {Object} dropData\n * @return {boolean}\n */\n validateDropData(dropData) {\n if (this.getElement().closest(this.selectors.CATEGORY_ID(dropData.id))) {\n return false;\n }\n return true;\n }\n\n showDropZone(dropData, event) {\n const dropTarget = event.target.closest(this.selectors.NEW_CHILD);\n dropTarget.classList.add(this.classes.DROP_TARGET);\n Tooltip.getOrCreateInstance(dropTarget).show();\n }\n\n hideDropZone(dropData, event) {\n const dropTarget = event.target.closest(this.selectors.NEW_CHILD);\n dropTarget.classList.remove(this.classes.DROP_TARGET);\n Tooltip.getOrCreateInstance(dropTarget).hide();\n }\n\n drop(dropData, event) {\n const dropTarget = event.target.closest(this.selectors.NEW_CHILD);\n\n if (!dropTarget) {\n return;\n }\n\n const source = document.getElementById(this.ids.CATEGORY(dropData.id));\n\n if (!source) {\n return;\n }\n\n const targetParentId = dropTarget.dataset.parent;\n\n // Insert the category as the first child of the new parent.\n categorymanager.moveCategory(dropData.id, targetParentId);\n }\n\n /**\n * Watch for categories moving to a new parent.\n *\n * @return {Array} A list of watchers.\n */\n getWatchers() {\n return [\n // Watch for any category having its parent changed.\n {watch: `categories.parent:updated`, handler: this.checkNewChild},\n ];\n }\n\n /**\n * If an element now has this category as the parent, remove this new child target.\n *\n * @param {Object} args\n * @param {Element} args.element\n */\n checkNewChild({element}) {\n if (element.parent === parseInt(this.element.dataset.parent)) {\n this.remove();\n }\n }\n}\n"],"names":["BaseComponent","create","descriptor","name","element","id","selectors","NEW_CHILD","CATEGORY_ID","classes","DROP_TARGET","ids","CATEGORY","stateReady","dragdrop","DragDrop","this","destroy","undefined","unregister","target","document","querySelector","reactive","categorymanager","validateDropData","dropData","getElement","closest","showDropZone","event","dropTarget","classList","add","getOrCreateInstance","show","hideDropZone","remove","hide","drop","getElementById","targetParentId","dataset","parent","moveCategory","getWatchers","watch","handler","checkNewChild","parseInt"],"mappings":"+WA4B6BA,wBACzBC,OAAOC,iBACEC,KAAOD,WAAWE,QAAQC,QAC1BC,UAAY,CACbC,UAAW,mCACXC,YAAaH,wBAAmBA,UAE/BI,QAAU,CACXC,YAAa,0CAEZC,IAAM,CACPC,SAAUP,uBAAkBA,KAIpCQ,kBACSC,SAAW,IAAIC,mBAASC,MAGjCC,eAE0BC,IAAlBF,KAAKF,gBACAA,SAASK,kBACTL,cAAWI,eAWZE,OAAQd,kBAET,IAAIU,KAAK,CACZZ,QAFkBiB,SAASC,cAAcF,QAGzCd,UAAAA,UACAiB,SAAUC,mCAUlBC,iBAAiBC,iBACTV,KAAKW,aAAaC,QAAQZ,KAAKV,UAAUE,YAAYkB,SAASrB,KAMtEwB,aAAaH,SAAUI,aACbC,WAAaD,MAAMV,OAAOQ,QAAQZ,KAAKV,UAAUC,WACvDwB,WAAWC,UAAUC,IAAIjB,KAAKP,QAAQC,8BAC9BwB,oBAAoBH,YAAYI,OAG5CC,aAAaV,SAAUI,aACbC,WAAaD,MAAMV,OAAOQ,QAAQZ,KAAKV,UAAUC,WACvDwB,WAAWC,UAAUK,OAAOrB,KAAKP,QAAQC,8BACjCwB,oBAAoBH,YAAYO,OAG5CC,KAAKb,SAAUI,aACLC,WAAaD,MAAMV,OAAOQ,QAAQZ,KAAKV,UAAUC,eAElDwB,sBAIUV,SAASmB,eAAexB,KAAKL,IAAIC,SAASc,SAASrB,kBAM5DoC,eAAiBV,WAAWW,QAAQC,wCAG1BC,aAAalB,SAASrB,GAAIoC,gBAQ9CI,oBACW,CAEH,CAACC,kCAAoCC,QAAS/B,KAAKgC,gBAU3DA,wBAAc5C,QAACA,cACPA,QAAQuC,SAAWM,SAASjC,KAAKZ,QAAQsC,QAAQC,cAC5CN"} \ No newline at end of file diff --git a/question/bank/managecategories/amd/src/newchild.js b/question/bank/managecategories/amd/src/newchild.js index 35cd934d6c7ac..1d6668b14db1e 100644 --- a/question/bank/managecategories/amd/src/newchild.js +++ b/question/bank/managecategories/amd/src/newchild.js @@ -23,8 +23,8 @@ */ import {BaseComponent, DragDrop} from 'core/reactive'; -import $ from 'jquery'; import {categorymanager} from 'qbank_managecategories/categorymanager'; +import Tooltip from 'theme_boost/bootstrap/tooltip'; export default class extends BaseComponent { create(descriptor) { @@ -85,13 +85,13 @@ export default class extends BaseComponent { showDropZone(dropData, event) { const dropTarget = event.target.closest(this.selectors.NEW_CHILD); dropTarget.classList.add(this.classes.DROP_TARGET); - $(dropTarget).tooltip('show'); + Tooltip.getOrCreateInstance(dropTarget).show(); } hideDropZone(dropData, event) { const dropTarget = event.target.closest(this.selectors.NEW_CHILD); dropTarget.classList.remove(this.classes.DROP_TARGET); - $(dropTarget).tooltip('hide'); + Tooltip.getOrCreateInstance(dropTarget).hide(); } drop(dropData, event) { diff --git a/question/type/multianswer/amd/build/feedback.min.js b/question/type/multianswer/amd/build/feedback.min.js index 4798e1a004c13..f6fbd7d941017 100644 --- a/question/type/multianswer/amd/build/feedback.min.js +++ b/question/type/multianswer/amd/build/feedback.min.js @@ -1,10 +1,10 @@ -define("qtype_multianswer/feedback",["exports","theme_boost/popover","jquery"],(function(_exports,_popover,_jquery){var obj; +define("qtype_multianswer/feedback",["exports","theme_boost/popover","theme_boost/bootstrap/popover"],(function(_exports,_popover,_popover2){var obj; /** * Backward compatibility file for the old popover.js * * @module qtype_multianswer/feedback * @copyright 2023 Jun Pataleta * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj};const SELECTORS_FEEDBACK_TRIGGER='.feedbacktrigger[data-bs-toggle="popover"]';let feedbackInitialised=!1;var _default={initPopovers:()=>{feedbackInitialised||((0,_jquery.default)(SELECTORS_FEEDBACK_TRIGGER).popover(),document.addEventListener("click",(e=>{e.target.closest(SELECTORS_FEEDBACK_TRIGGER)&&e.preventDefault()})),feedbackInitialised=!0)}};return _exports.default=_default,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_popover2=(obj=_popover2)&&obj.__esModule?obj:{default:obj};const SELECTORS_FEEDBACK_TRIGGER='.feedbacktrigger[data-bs-toggle="popover"]';let feedbackInitialised=!1;var _default={initPopovers:()=>{if(!feedbackInitialised){[...document.querySelectorAll(SELECTORS_FEEDBACK_TRIGGER)].map((trigger=>new _popover2.default(trigger))),document.addEventListener("click",(e=>{e.target.closest(SELECTORS_FEEDBACK_TRIGGER)&&e.preventDefault()})),feedbackInitialised=!0}}};return _exports.default=_default,_exports.default})); //# sourceMappingURL=feedback.min.js.map \ No newline at end of file diff --git a/question/type/multianswer/amd/build/feedback.min.js.map b/question/type/multianswer/amd/build/feedback.min.js.map index b6c80caa605cb..e9ff7c23a6ffe 100644 --- a/question/type/multianswer/amd/build/feedback.min.js.map +++ b/question/type/multianswer/amd/build/feedback.min.js.map @@ -1 +1 @@ -{"version":3,"file":"feedback.min.js","sources":["../src/feedback.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 * Backward compatibility file for the old popover.js\n *\n * @module qtype_multianswer/feedback\n * @copyright 2023 Jun Pataleta \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport 'theme_boost/popover';\nimport $ from 'jquery';\n\n/** @property {object} Contains the list of selectors for this module. */\nconst SELECTORS = {\n FEEDBACK_TRIGGER: '.feedbacktrigger[data-bs-toggle=\"popover\"]',\n};\n\n/** @property {boolean} Flag to indicate whether the feedback popovers have been already initialised. */\nlet feedbackInitialised = false;\n\n/**\n * Function to initialise the feedback popovers.\n */\nconst initPopovers = () => {\n if (!feedbackInitialised) {\n $(SELECTORS.FEEDBACK_TRIGGER).popover();\n\n document.addEventListener('click', (e) => {\n if (e.target.closest(SELECTORS.FEEDBACK_TRIGGER)) {\n e.preventDefault();\n }\n });\n feedbackInitialised = true;\n }\n};\n\nexport default {\n initPopovers: initPopovers,\n};\n"],"names":["SELECTORS","feedbackInitialised","initPopovers","popover","document","addEventListener","e","target","closest","preventDefault"],"mappings":";;;;;;;mJA2BMA,2BACgB,iDAIlBC,qBAAsB,eAkBX,CACXC,aAdiB,KACZD,0CACCD,4BAA4BG,UAE9BC,SAASC,iBAAiB,SAAUC,IAC5BA,EAAEC,OAAOC,QAAQR,6BACjBM,EAAEG,oBAGVR,qBAAsB"} \ No newline at end of file +{"version":3,"file":"feedback.min.js","sources":["../src/feedback.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 * Backward compatibility file for the old popover.js\n *\n * @module qtype_multianswer/feedback\n * @copyright 2023 Jun Pataleta \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport 'theme_boost/popover';\nimport Popover from 'theme_boost/bootstrap/popover';\n\n/** @property {object} Contains the list of selectors for this module. */\nconst SELECTORS = {\n FEEDBACK_TRIGGER: '.feedbacktrigger[data-bs-toggle=\"popover\"]',\n};\n\n/** @property {boolean} Flag to indicate whether the feedback popovers have been already initialised. */\nlet feedbackInitialised = false;\n\n/**\n * Function to initialise the feedback popovers.\n */\nconst initPopovers = () => {\n if (!feedbackInitialised) {\n const popoverTriggers = document.querySelectorAll(SELECTORS.FEEDBACK_TRIGGER);\n [...popoverTriggers].map((trigger) => new Popover(trigger));\n\n document.addEventListener('click', (e) => {\n if (e.target.closest(SELECTORS.FEEDBACK_TRIGGER)) {\n e.preventDefault();\n }\n });\n feedbackInitialised = true;\n }\n};\n\nexport default {\n initPopovers: initPopovers,\n};\n"],"names":["SELECTORS","feedbackInitialised","initPopovers","document","querySelectorAll","map","trigger","Popover","addEventListener","e","target","closest","preventDefault"],"mappings":";;;;;;;uJA2BMA,2BACgB,iDAIlBC,qBAAsB,eAmBX,CACXC,aAfiB,SACZD,oBAAqB,KACEE,SAASC,iBAAiBJ,6BAC7BK,KAAKC,SAAY,IAAIC,kBAAQD,WAElDH,SAASK,iBAAiB,SAAUC,IAC5BA,EAAEC,OAAOC,QAAQX,6BACjBS,EAAEG,oBAGVX,qBAAsB"} \ No newline at end of file diff --git a/question/type/multianswer/amd/src/feedback.js b/question/type/multianswer/amd/src/feedback.js index a79f12b527a2a..8f90e1c921a8f 100644 --- a/question/type/multianswer/amd/src/feedback.js +++ b/question/type/multianswer/amd/src/feedback.js @@ -22,7 +22,7 @@ */ import 'theme_boost/popover'; -import $ from 'jquery'; +import Popover from 'theme_boost/bootstrap/popover'; /** @property {object} Contains the list of selectors for this module. */ const SELECTORS = { @@ -37,7 +37,8 @@ let feedbackInitialised = false; */ const initPopovers = () => { if (!feedbackInitialised) { - $(SELECTORS.FEEDBACK_TRIGGER).popover(); + const popoverTriggers = document.querySelectorAll(SELECTORS.FEEDBACK_TRIGGER); + [...popoverTriggers].map((trigger) => new Popover(trigger)); document.addEventListener('click', (e) => { if (e.target.closest(SELECTORS.FEEDBACK_TRIGGER)) { diff --git a/theme/boost/amd/build/aria.min.js b/theme/boost/amd/build/aria.min.js index 8611192026202..6cb7145847d9b 100644 --- a/theme/boost/amd/build/aria.min.js +++ b/theme/boost/amd/build/aria.min.js @@ -1,10 +1,10 @@ -define("theme_boost/aria",["exports","jquery","core/pending","core/local/aria/focuslock"],(function(_exports,_jquery,_pending,FocusLockManager){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("theme_boost/aria",["exports","jquery","theme_boost/bootstrap/tab","core/pending","core/local/aria/focuslock"],(function(_exports,_jquery,_tab,_pending,FocusLockManager){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Enhancements to Bootstrap components for accessibility. * * @module theme_boost/aria * @copyright 2018 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_pending=_interopRequireDefault(_pending),FocusLockManager=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(FocusLockManager);const dropdownFix=()=>{let focusEnd=!1;const setFocusEnd=function(){let end=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];focusEnd=end},shiftFocus=function(element){let focusCheck=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;const pendingPromise=new _pending.default("core/aria:delayed-focus");setTimeout((()=>{focusCheck&&!focusCheck()||element.focus(),pendingPromise.resolve()}),50)},handleMenuButton=e=>{const trigger=e.key;let fixFocus=!1;if(" "!==trigger&&"Enter"!==trigger||(fixFocus=!0,e.preventDefault(),e.target.click()),"ArrowUp"!==trigger&&"ArrowDown"!==trigger||(fixFocus=!0),!fixFocus)return;const menu=e.target.parentElement.querySelector('[role="menu"]');let menuItems=!1,foundMenuItem=!1;menu&&(menuItems=menu.querySelectorAll('[role="menuitem"]')),menuItems&&menuItems.length>0&&("ArrowUp"===trigger?setFocusEnd():setFocusEnd(!1),foundMenuItem=(()=>{const result=focusEnd;return focusEnd=!1,result})()?menuItems[menuItems.length-1]:menuItems[0]),foundMenuItem&&shiftFocus(foundMenuItem)};document.addEventListener("keypress",(e=>{if(e.target.matches('[role="menu"] [role="menuitem"]')){const menu=e.target.closest('[role="menu"]');if(!menu)return;const menuItems=menu.querySelectorAll('[role="menuitem"]');if(!menuItems)return;const trigger=e.key.toLowerCase();for(let i=0;i{if(e.target.matches('[data-bs-toggle="dropdown"]')&&handleMenuButton(e),e.target.matches('[role="menu"] [role="menuitem"]')){const trigger=e.key;let next=!1;const menu=e.target.closest('[role="menu"]');if(!menu)return;const menuItems=menu.querySelectorAll('[role="menuitem"]');if(!menuItems)return;if("ArrowDown"==trigger){for(let i=0;i{const dialog=e.target.querySelector('.dropdown-menu[role="dialog"]');dialog&&setTimeout((()=>{FocusLockManager.trapFocus(dialog)}))})),(0,_jquery.default)(document).on("hidden.bs.dropdown",(e=>{var _e$clickEvent;e.target.querySelector('.dropdown-menu[role="dialog"]')&&FocusLockManager.untrapFocus();const trigger=e.target.querySelector('[data-bs-toggle="dropdown"]'),focused=(null===(_e$clickEvent=e.clickEvent)||void 0===_e$clickEvent?void 0:_e$clickEvent.target)||(document.activeElement!==document.body?document.activeElement:null);trigger&&focused&&e.target.contains(focused)&&shiftFocus(trigger,(()=>document.activeElement===document.body||e.target.contains(document.activeElement)))}))},rovingFocus=(elements,e,vertical,updateTabIndex)=>{const rtl=window.right_to_left(),arrowNext=vertical?"ArrowDown":rtl?"ArrowLeft":"ArrowRight",arrowPrevious=vertical?"ArrowUp":rtl?"ArrowRight":"ArrowLeft";if(![arrowNext,arrowPrevious,"Home","End"].includes(e.key))return;const focusElement=index=>{elements[index].focus(),updateTabIndex&&elements.forEach(((element,i)=>element.setAttribute("tabindex",i===index?"0":"-1")))},currentIndex=Array.prototype.indexOf.call(elements,e.target);let nextIndex;switch(e.key){case arrowNext:e.preventDefault(),nextIndex=currentIndex+1=0?currentIndex-1:elements.length-1,focusElement(nextIndex);break;case"Home":e.preventDefault(),focusElement(0);break;case"End":e.preventDefault(),focusElement(elements.length-1)}};_exports.init=()=>{dropdownFix(),(()=>{(0,_jquery.default)(document).on("show.bs.dropdown",(e=>{if(e.relatedTarget.matches('[role="combobox"]')){const combobox=e.relatedTarget,listbox=document.querySelector("#".concat(combobox.getAttribute("aria-controls"),'[role="listbox"]'));if(listbox){const selectedOption=listbox.querySelector('[role="option"][aria-selected="true"]');setTimeout((()=>{if(selectedOption)selectedOption.classList.add("active"),combobox.setAttribute("aria-activedescendant",selectedOption.id);else{const firstOption=listbox.querySelector('[role="option"]');firstOption.setAttribute("aria-selected","true"),firstOption.classList.add("active"),combobox.setAttribute("aria-activedescendant",firstOption.id)}}),0)}}})),(0,_jquery.default)(document).on("hidden.bs.dropdown",(e=>{if(e.relatedTarget.matches('[role="combobox"]')){const combobox=e.relatedTarget,listbox=document.querySelector("#".concat(combobox.getAttribute("aria-controls"),'[role="listbox"]'));combobox.removeAttribute("aria-activedescendant"),listbox&&setTimeout((()=>{listbox.querySelectorAll('.active[role="option"]').forEach((option=>{option.classList.remove("active")}))}),0)}})),document.addEventListener("keydown",(e=>{if(e.target.matches('[role="combobox"][aria-controls]:not([aria-haspopup=dialog])')){const combobox=e.target,trigger=e.key;let next=null;const listbox=document.querySelector("#".concat(combobox.getAttribute("aria-controls"),'[role="listbox"]')),options=listbox.querySelectorAll('[role="option"]'),activeOption=listbox.querySelector('.active[role="option"]'),editable=combobox.hasAttribute("aria-autocomplete");if(options&&(activeOption||editable)){if("ArrowDown"==trigger){for(let i=0;i{const option=e.target.closest('[role="listbox"] [role="option"]');if(option){const listbox=option.closest('[role="listbox"]'),combobox=document.querySelector('[role="combobox"][aria-controls="'.concat(listbox.id,'"]'));combobox&&selectOption(combobox,option)}})),document.addEventListener("change",(e=>{if(e.target.matches('input[type="hidden"][id]')){const combobox=document.querySelector('[role="combobox"][data-input-element="'.concat(e.target.id,'"]')),option=e.target.parentElement.querySelector('[role="option"][data-value="'.concat(e.target.value,'"]'));combobox&&option&&selectOption(combobox,option)}}));const selectOption=(combobox,option)=>{const oldSelectedOption=option.closest('[role="listbox"]').querySelector('[role="option"][aria-selected="true"]');if(oldSelectedOption!=option&&(oldSelectedOption&&oldSelectedOption.removeAttribute("aria-selected"),option.setAttribute("aria-selected","true")),combobox.hasAttribute("value"))combobox.value=option.dataset.shortText||option.textContent.replace(/[\n\r]+|[\s]{2,}/g," ").trim();else{const selectedOptionContainer=combobox.querySelector("[data-selected-option]");selectedOptionContainer?selectedOptionContainer.textContent=option.dataset.shortText||option.textContent:combobox.textContent=option.dataset.shortText||option.textContent}if(combobox.dataset.inputElement){const inputElement=document.getElementById(combobox.dataset.inputElement);inputElement&&inputElement.value!=option.dataset.value&&(inputElement.value=option.dataset.value,inputElement.dispatchEvent(new Event("change",{bubbles:!0})))}}})(),window.addEventListener("load",(()=>{const alerts=document.querySelectorAll('[data-aria-autofocus="true"][role="alert"]');Array.prototype.forEach.call(alerts,(autofocusElement=>{autofocusElement.innerHTML+=" ",autofocusElement.removeAttribute("data-aria-autofocus")}))})),document.addEventListener("keydown",(e=>{if(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Home","End"].includes(e.key)&&e.target.matches('[role="tablist"] [role="tab"]')){const tabList=e.target.closest('[role="tablist"]'),tabs=Array.prototype.filter.call(tabList.querySelectorAll('[role="tab"]'),(tab=>!!tab.offsetHeight)),vertical="vertical"==tabList.getAttribute("aria-orientation");rovingFocus(tabs,e,vertical,!1)}})),document.addEventListener("click",(e=>{if(e.target.matches('[role="tablist"] [data-bs-toggle="tab"], [role="tablist"] [data-bs-toggle="pill"]')){const tabs=e.target.closest('[role="tablist"]').querySelectorAll('[data-bs-toggle="tab"], [data-bs-toggle="pill"]');e.preventDefault(),(0,_jquery.default)(e.target).tab("show"),tabs.forEach((tab=>{tab.tabIndex=-1})),e.target.tabIndex=0}})),document.addEventListener("keydown",(e=>{e.target.matches('[data-bs-toggle="collapse"]')&&" "===e.key&&(e.preventDefault(),e.target.click())})),document.addEventListener("keydown",(e=>{if(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Home","End"].includes(e.key)&&e.target.matches('[role="toolbar"] button')){const buttons=e.target.closest('[role="toolbar"]').querySelectorAll("button");rovingFocus(buttons,e,!1,!0)}}))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_tab=_interopRequireDefault(_tab),_pending=_interopRequireDefault(_pending),FocusLockManager=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(FocusLockManager);const dropdownFix=()=>{let focusEnd=!1;const setFocusEnd=function(){let end=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];focusEnd=end},shiftFocus=function(element){let focusCheck=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;const pendingPromise=new _pending.default("core/aria:delayed-focus");setTimeout((()=>{focusCheck&&!focusCheck()||element.focus(),pendingPromise.resolve()}),50)},handleMenuButton=e=>{const trigger=e.key;let fixFocus=!1;if(" "!==trigger&&"Enter"!==trigger||(fixFocus=!0,e.preventDefault(),e.target.click()),"ArrowUp"!==trigger&&"ArrowDown"!==trigger||(fixFocus=!0),!fixFocus)return;const menu=e.target.parentElement.querySelector('[role="menu"]');let menuItems=!1,foundMenuItem=!1;menu&&(menuItems=menu.querySelectorAll('[role="menuitem"]')),menuItems&&menuItems.length>0&&("ArrowUp"===trigger?setFocusEnd():setFocusEnd(!1),foundMenuItem=(()=>{const result=focusEnd;return focusEnd=!1,result})()?menuItems[menuItems.length-1]:menuItems[0]),foundMenuItem&&shiftFocus(foundMenuItem)};document.addEventListener("keypress",(e=>{if(e.target.matches('[role="menu"] [role="menuitem"]')){const menu=e.target.closest('[role="menu"]');if(!menu)return;const menuItems=menu.querySelectorAll('[role="menuitem"]');if(!menuItems)return;const trigger=e.key.toLowerCase();for(let i=0;i{if(e.target.matches('[data-bs-toggle="dropdown"]')&&handleMenuButton(e),e.target.matches('[role="menu"] [role="menuitem"]')){const trigger=e.key;let next=!1;const menu=e.target.closest('[role="menu"]');if(!menu)return;const menuItems=menu.querySelectorAll('[role="menuitem"]');if(!menuItems)return;if("ArrowDown"==trigger){for(let i=0;i{const dialog=e.target.querySelector('.dropdown-menu[role="dialog"]');dialog&&setTimeout((()=>{FocusLockManager.trapFocus(dialog)}))})),(0,_jquery.default)(document).on("hidden.bs.dropdown",(e=>{var _e$clickEvent;e.target.querySelector('.dropdown-menu[role="dialog"]')&&FocusLockManager.untrapFocus();const trigger=e.target.querySelector('[data-bs-toggle="dropdown"]'),focused=(null===(_e$clickEvent=e.clickEvent)||void 0===_e$clickEvent?void 0:_e$clickEvent.target)||(document.activeElement!==document.body?document.activeElement:null);trigger&&focused&&e.target.contains(focused)&&shiftFocus(trigger,(()=>document.activeElement===document.body||e.target.contains(document.activeElement)))}))},rovingFocus=(elements,e,vertical,updateTabIndex)=>{const rtl=window.right_to_left(),arrowNext=vertical?"ArrowDown":rtl?"ArrowLeft":"ArrowRight",arrowPrevious=vertical?"ArrowUp":rtl?"ArrowRight":"ArrowLeft";if(![arrowNext,arrowPrevious,"Home","End"].includes(e.key))return;const focusElement=index=>{elements[index].focus(),updateTabIndex&&elements.forEach(((element,i)=>element.setAttribute("tabindex",i===index?"0":"-1")))},currentIndex=Array.prototype.indexOf.call(elements,e.target);let nextIndex;switch(e.key){case arrowNext:e.preventDefault(),nextIndex=currentIndex+1=0?currentIndex-1:elements.length-1,focusElement(nextIndex);break;case"Home":e.preventDefault(),focusElement(0);break;case"End":e.preventDefault(),focusElement(elements.length-1)}};_exports.init=()=>{dropdownFix(),(()=>{(0,_jquery.default)(document).on("show.bs.dropdown",(e=>{if(e.relatedTarget.matches('[role="combobox"]')){const combobox=e.relatedTarget,listbox=document.querySelector("#".concat(combobox.getAttribute("aria-controls"),'[role="listbox"]'));if(listbox){const selectedOption=listbox.querySelector('[role="option"][aria-selected="true"]');setTimeout((()=>{if(selectedOption)selectedOption.classList.add("active"),combobox.setAttribute("aria-activedescendant",selectedOption.id);else{const firstOption=listbox.querySelector('[role="option"]');firstOption.setAttribute("aria-selected","true"),firstOption.classList.add("active"),combobox.setAttribute("aria-activedescendant",firstOption.id)}}),0)}}})),(0,_jquery.default)(document).on("hidden.bs.dropdown",(e=>{if(e.relatedTarget.matches('[role="combobox"]')){const combobox=e.relatedTarget,listbox=document.querySelector("#".concat(combobox.getAttribute("aria-controls"),'[role="listbox"]'));combobox.removeAttribute("aria-activedescendant"),listbox&&setTimeout((()=>{listbox.querySelectorAll('.active[role="option"]').forEach((option=>{option.classList.remove("active")}))}),0)}})),document.addEventListener("keydown",(e=>{if(e.target.matches('[role="combobox"][aria-controls]:not([aria-haspopup=dialog])')){const combobox=e.target,trigger=e.key;let next=null;const listbox=document.querySelector("#".concat(combobox.getAttribute("aria-controls"),'[role="listbox"]')),options=listbox.querySelectorAll('[role="option"]'),activeOption=listbox.querySelector('.active[role="option"]'),editable=combobox.hasAttribute("aria-autocomplete");if(options&&(activeOption||editable)){if("ArrowDown"==trigger){for(let i=0;i{const option=e.target.closest('[role="listbox"] [role="option"]');if(option){const listbox=option.closest('[role="listbox"]'),combobox=document.querySelector('[role="combobox"][aria-controls="'.concat(listbox.id,'"]'));combobox&&selectOption(combobox,option)}})),document.addEventListener("change",(e=>{if(e.target.matches('input[type="hidden"][id]')){const combobox=document.querySelector('[role="combobox"][data-input-element="'.concat(e.target.id,'"]')),option=e.target.parentElement.querySelector('[role="option"][data-value="'.concat(e.target.value,'"]'));combobox&&option&&selectOption(combobox,option)}}));const selectOption=(combobox,option)=>{const oldSelectedOption=option.closest('[role="listbox"]').querySelector('[role="option"][aria-selected="true"]');if(oldSelectedOption!=option&&(oldSelectedOption&&oldSelectedOption.removeAttribute("aria-selected"),option.setAttribute("aria-selected","true")),combobox.hasAttribute("value"))combobox.value=option.dataset.shortText||option.textContent.replace(/[\n\r]+|[\s]{2,}/g," ").trim();else{const selectedOptionContainer=combobox.querySelector("[data-selected-option]");selectedOptionContainer?selectedOptionContainer.textContent=option.dataset.shortText||option.textContent:combobox.textContent=option.dataset.shortText||option.textContent}if(combobox.dataset.inputElement){const inputElement=document.getElementById(combobox.dataset.inputElement);inputElement&&inputElement.value!=option.dataset.value&&(inputElement.value=option.dataset.value,inputElement.dispatchEvent(new Event("change",{bubbles:!0})))}}})(),window.addEventListener("load",(()=>{const alerts=document.querySelectorAll('[data-aria-autofocus="true"][role="alert"]');Array.prototype.forEach.call(alerts,(autofocusElement=>{autofocusElement.innerHTML+=" ",autofocusElement.removeAttribute("data-aria-autofocus")}))})),document.addEventListener("keydown",(e=>{if(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Home","End"].includes(e.key)&&e.target.matches('[role="tablist"] [role="tab"]')){const tabList=e.target.closest('[role="tablist"]'),tabs=Array.prototype.filter.call(tabList.querySelectorAll('[role="tab"]'),(tab=>!!tab.offsetHeight)),vertical="vertical"==tabList.getAttribute("aria-orientation");rovingFocus(tabs,e,vertical,!1)}})),document.addEventListener("click",(e=>{if(e.target.matches('[role="tablist"] [data-bs-toggle="tab"], [role="tablist"] [data-bs-toggle="pill"]')){const tabs=e.target.closest('[role="tablist"]').querySelectorAll('[data-bs-toggle="tab"], [data-bs-toggle="pill"]');e.preventDefault(),_tab.default.getOrCreateInstance(e.target).show(),tabs.forEach((tab=>{tab.tabIndex=-1})),e.target.tabIndex=0}})),document.addEventListener("keydown",(e=>{e.target.matches('[data-bs-toggle="collapse"]')&&" "===e.key&&(e.preventDefault(),e.target.click())})),document.addEventListener("keydown",(e=>{if(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Home","End"].includes(e.key)&&e.target.matches('[role="toolbar"] button')){const buttons=e.target.closest('[role="toolbar"]').querySelectorAll("button");rovingFocus(buttons,e,!1,!0)}}))}})); //# sourceMappingURL=aria.min.js.map \ No newline at end of file diff --git a/theme/boost/amd/build/aria.min.js.map b/theme/boost/amd/build/aria.min.js.map index a5b6e6998054a..3c7ffedebacbb 100644 --- a/theme/boost/amd/build/aria.min.js.map +++ b/theme/boost/amd/build/aria.min.js.map @@ -1 +1 @@ -{"version":3,"file":"aria.min.js","sources":["../src/aria.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 * Enhancements to Bootstrap components for accessibility.\n *\n * @module theme_boost/aria\n * @copyright 2018 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Pending from 'core/pending';\nimport * as FocusLockManager from 'core/local/aria/focuslock';\n\n/**\n * Drop downs from bootstrap don't support keyboard accessibility by default.\n */\nconst dropdownFix = () => {\n let focusEnd = false;\n const setFocusEnd = (end = true) => {\n focusEnd = end;\n };\n const getFocusEnd = () => {\n const result = focusEnd;\n focusEnd = false;\n return result;\n };\n\n // Special handling for navigation keys when menu is open.\n const shiftFocus = (element, focusCheck = null) => {\n const pendingPromise = new Pending('core/aria:delayed-focus');\n setTimeout(() => {\n if (!focusCheck || focusCheck()) {\n element.focus();\n }\n\n pendingPromise.resolve();\n }, 50);\n };\n\n // Event handling for the dropdown menu button.\n const handleMenuButton = e => {\n const trigger = e.key;\n let fixFocus = false;\n\n // Space key or Enter key opens the menu.\n if (trigger === ' ' || trigger === 'Enter') {\n fixFocus = true;\n // Cancel random scroll.\n e.preventDefault();\n // Open the menu instead.\n e.target.click();\n }\n\n // Up and Down keys also open the menu.\n if (trigger === 'ArrowUp' || trigger === 'ArrowDown') {\n fixFocus = true;\n }\n\n if (!fixFocus) {\n // No need to fix the focus. Return early.\n return;\n }\n\n // Fix the focus on the menu items when the menu is opened.\n const menu = e.target.parentElement.querySelector('[role=\"menu\"]');\n let menuItems = false;\n let foundMenuItem = false;\n\n if (menu) {\n menuItems = menu.querySelectorAll('[role=\"menuitem\"]');\n }\n if (menuItems && menuItems.length > 0) {\n // Up key opens the menu at the end.\n if (trigger === 'ArrowUp') {\n setFocusEnd();\n } else {\n setFocusEnd(false);\n }\n\n if (getFocusEnd()) {\n foundMenuItem = menuItems[menuItems.length - 1];\n } else {\n // The first menu entry, pretty reasonable.\n foundMenuItem = menuItems[0];\n }\n }\n\n if (foundMenuItem) {\n shiftFocus(foundMenuItem);\n }\n };\n\n // Search for menu items by finding the first item that has\n // text starting with the typed character (case insensitive).\n document.addEventListener('keypress', e => {\n if (e.target.matches('[role=\"menu\"] [role=\"menuitem\"]')) {\n const menu = e.target.closest('[role=\"menu\"]');\n if (!menu) {\n return;\n }\n const menuItems = menu.querySelectorAll('[role=\"menuitem\"]');\n if (!menuItems) {\n return;\n }\n\n const trigger = e.key.toLowerCase();\n\n for (let i = 0; i < menuItems.length; i++) {\n const item = menuItems[i];\n const itemText = item.text.trim().toLowerCase();\n if (itemText.indexOf(trigger) == 0) {\n shiftFocus(item);\n break;\n }\n }\n }\n });\n\n // Keyboard navigation for arrow keys, home and end keys.\n document.addEventListener('keydown', e => {\n\n // We only want to set focus when users access the dropdown via keyboard as per\n // guidelines defined in w3 aria practices 1.1 menu-button.\n if (e.target.matches('[data-bs-toggle=\"dropdown\"]')) {\n handleMenuButton(e);\n }\n\n if (e.target.matches('[role=\"menu\"] [role=\"menuitem\"]')) {\n const trigger = e.key;\n let next = false;\n const menu = e.target.closest('[role=\"menu\"]');\n\n if (!menu) {\n return;\n }\n const menuItems = menu.querySelectorAll('[role=\"menuitem\"]');\n if (!menuItems) {\n return;\n }\n // Down key.\n if (trigger == 'ArrowDown') {\n for (let i = 0; i < menuItems.length - 1; i++) {\n if (menuItems[i] == e.target) {\n next = menuItems[i + 1];\n break;\n }\n }\n if (!next) {\n // Wrap to first item.\n next = menuItems[0];\n }\n } else if (trigger == 'ArrowUp') {\n // Up key.\n for (let i = 1; i < menuItems.length; i++) {\n if (menuItems[i] == e.target) {\n next = menuItems[i - 1];\n break;\n }\n }\n if (!next) {\n // Wrap to last item.\n next = menuItems[menuItems.length - 1];\n }\n } else if (trigger == 'Home') {\n // Home key.\n next = menuItems[0];\n\n } else if (trigger == 'End') {\n // End key.\n next = menuItems[menuItems.length - 1];\n }\n\n // Variable next is set if we do want to act on the keypress.\n if (next) {\n e.preventDefault();\n shiftFocus(next);\n }\n return;\n }\n });\n\n // Trap focus if the dropdown is a dialog.\n $(document).on('shown.bs.dropdown', e => {\n const dialog = e.target.querySelector('.dropdown-menu[role=\"dialog\"]');\n if (dialog) {\n // Use setTimeout to make sure the dialog is positioned correctly to prevent random scrolling.\n setTimeout(() => {\n FocusLockManager.trapFocus(dialog);\n });\n }\n });\n\n // Untrap focus when the dialog dropdown is closed.\n $(document).on('hidden.bs.dropdown', e => {\n const dialog = e.target.querySelector('.dropdown-menu[role=\"dialog\"]');\n if (dialog) {\n FocusLockManager.untrapFocus();\n }\n\n // We need to focus on the menu trigger.\n const trigger = e.target.querySelector('[data-bs-toggle=\"dropdown\"]');\n // If it's a click event, then no element is focused because the clicked element is inside a closed dropdown.\n const focused = e.clickEvent?.target || (document.activeElement !== document.body ? document.activeElement : null);\n if (trigger && focused && e.target.contains(focused)) {\n shiftFocus(trigger, () => {\n if (document.activeElement === document.body) {\n // If the focus is currently on the body, then we can safely assume that the focus needs to be updated.\n return true;\n }\n\n // If the focus is on a child of the clicked element still, then update the focus.\n return e.target.contains(document.activeElement);\n });\n }\n });\n};\n\n/**\n * A lot of Bootstrap's out of the box features don't work if dropdown items are not focusable.\n */\nconst comboboxFix = () => {\n $(document).on('show.bs.dropdown', e => {\n if (e.relatedTarget.matches('[role=\"combobox\"]')) {\n const combobox = e.relatedTarget;\n const listbox = document.querySelector(`#${combobox.getAttribute('aria-controls')}[role=\"listbox\"]`);\n\n if (listbox) {\n const selectedOption = listbox.querySelector('[role=\"option\"][aria-selected=\"true\"]');\n\n // To make sure ArrowDown doesn't move the active option afterwards.\n setTimeout(() => {\n if (selectedOption) {\n selectedOption.classList.add('active');\n combobox.setAttribute('aria-activedescendant', selectedOption.id);\n } else {\n const firstOption = listbox.querySelector('[role=\"option\"]');\n firstOption.setAttribute('aria-selected', 'true');\n firstOption.classList.add('active');\n combobox.setAttribute('aria-activedescendant', firstOption.id);\n }\n }, 0);\n }\n }\n });\n\n $(document).on('hidden.bs.dropdown', e => {\n if (e.relatedTarget.matches('[role=\"combobox\"]')) {\n const combobox = e.relatedTarget;\n const listbox = document.querySelector(`#${combobox.getAttribute('aria-controls')}[role=\"listbox\"]`);\n\n combobox.removeAttribute('aria-activedescendant');\n\n if (listbox) {\n setTimeout(() => {\n // Undo all previously highlighted options.\n listbox.querySelectorAll('.active[role=\"option\"]').forEach(option => {\n option.classList.remove('active');\n });\n }, 0);\n }\n }\n });\n\n // Handling keyboard events for both navigating through and selecting options.\n document.addEventListener('keydown', e => {\n if (e.target.matches('[role=\"combobox\"][aria-controls]:not([aria-haspopup=dialog])')) {\n const combobox = e.target;\n const trigger = e.key;\n let next = null;\n const listbox = document.querySelector(`#${combobox.getAttribute('aria-controls')}[role=\"listbox\"]`);\n const options = listbox.querySelectorAll('[role=\"option\"]');\n const activeOption = listbox.querySelector('.active[role=\"option\"]');\n const editable = combobox.hasAttribute('aria-autocomplete');\n\n // Under the special case that the dropdown menu is being shown as a result of the key press (like when the user\n // presses ArrowDown or Enter or ... to open the dropdown menu), activeOption is not set yet.\n // It's because of a race condition with show.bs.dropdown event handler.\n if (options && (activeOption || editable)) {\n if (trigger == 'ArrowDown') {\n for (let i = 0; i < options.length - 1; i++) {\n if (options[i] == activeOption) {\n next = options[i + 1];\n break;\n }\n }\n if (editable && !next) {\n next = options[0];\n }\n } if (trigger == 'ArrowUp') {\n for (let i = 1; i < options.length; i++) {\n if (options[i] == activeOption) {\n next = options[i - 1];\n break;\n }\n }\n if (editable && !next) {\n next = options[options.length - 1];\n }\n } else if (trigger == 'Home' && !editable) {\n next = options[0];\n } else if (trigger == 'End' && !editable) {\n next = options[options.length - 1];\n } else if ((trigger == ' ' && !editable) || trigger == 'Enter') {\n e.preventDefault();\n selectOption(combobox, activeOption);\n } else if (!editable) {\n // Search for options by finding the first option that has\n // text starting with the typed character (case insensitive).\n for (let i = 0; i < options.length; i++) {\n const option = options[i];\n const optionText = option.textContent.trim().toLowerCase();\n const keyPressed = e.key.toLowerCase();\n if (optionText.indexOf(keyPressed) == 0) {\n next = option;\n break;\n }\n }\n }\n\n // Variable next is set if we do want to act on the keypress.\n if (next) {\n e.preventDefault();\n if (activeOption) {\n activeOption.classList.remove('active');\n }\n next.classList.add('active');\n combobox.setAttribute('aria-activedescendant', next.id);\n next.scrollIntoView({block: 'nearest'});\n }\n }\n }\n });\n\n document.addEventListener('click', e => {\n const option = e.target.closest('[role=\"listbox\"] [role=\"option\"]');\n if (option) {\n const listbox = option.closest('[role=\"listbox\"]');\n const combobox = document.querySelector(`[role=\"combobox\"][aria-controls=\"${listbox.id}\"]`);\n if (combobox) {\n selectOption(combobox, option);\n }\n }\n });\n\n // In case some code somewhere else changes the value of the combobox.\n document.addEventListener('change', e => {\n if (e.target.matches('input[type=\"hidden\"][id]')) {\n const combobox = document.querySelector(`[role=\"combobox\"][data-input-element=\"${e.target.id}\"]`);\n const option = e.target.parentElement.querySelector(`[role=\"option\"][data-value=\"${e.target.value}\"]`);\n\n if (combobox && option) {\n selectOption(combobox, option);\n }\n }\n });\n\n const selectOption = (combobox, option) => {\n const listbox = option.closest('[role=\"listbox\"]');\n const oldSelectedOption = listbox.querySelector('[role=\"option\"][aria-selected=\"true\"]');\n\n if (oldSelectedOption != option) {\n if (oldSelectedOption) {\n oldSelectedOption.removeAttribute('aria-selected');\n }\n option.setAttribute('aria-selected', 'true');\n }\n\n if (combobox.hasAttribute('value')) {\n combobox.value = option.dataset.shortText || option.textContent.replace(/[\\n\\r]+|[\\s]{2,}/g, ' ').trim();\n } else {\n const selectedOptionContainer = combobox.querySelector('[data-selected-option]');\n if (selectedOptionContainer) {\n selectedOptionContainer.textContent = option.dataset.shortText || option.textContent;\n } else {\n combobox.textContent = option.dataset.shortText || option.textContent;\n }\n }\n\n if (combobox.dataset.inputElement) {\n const inputElement = document.getElementById(combobox.dataset.inputElement);\n if (inputElement && (inputElement.value != option.dataset.value)) {\n inputElement.value = option.dataset.value;\n inputElement.dispatchEvent(new Event('change', {bubbles: true}));\n }\n }\n };\n};\n\n/**\n * After page load, focus on any element with special autofocus attribute.\n */\nconst autoFocus = () => {\n window.addEventListener(\"load\", () => {\n const alerts = document.querySelectorAll('[data-aria-autofocus=\"true\"][role=\"alert\"]');\n Array.prototype.forEach.call(alerts, autofocusElement => {\n // According to the specification an role=\"alert\" region is only read out on change to the content\n // of that region.\n autofocusElement.innerHTML += ' ';\n autofocusElement.removeAttribute('data-aria-autofocus');\n });\n });\n};\n\n/**\n * Changes the focus to the correct element based on the key that is pressed.\n * @param {NodeList} elements A NodeList of focusable elements to navigate between.\n * @param {KeyboardEvent} e The keyboard event that triggers the roving focus.\n * @param {boolean} vertical Whether the navigation is vertical.\n * @param {boolean} updateTabIndex Whether to update the tabIndex of the elements.\n */\nconst rovingFocus = (elements, e, vertical, updateTabIndex) => {\n const rtl = window.right_to_left();\n const arrowNext = vertical ? 'ArrowDown' : (rtl ? 'ArrowLeft' : 'ArrowRight');\n const arrowPrevious = vertical ? 'ArrowUp' : (rtl ? 'ArrowRight' : 'ArrowLeft');\n const keys = [arrowNext, arrowPrevious, 'Home', 'End'];\n\n if (!keys.includes(e.key)) {\n return;\n }\n\n const focusElement = index => {\n elements[index].focus();\n if (updateTabIndex) {\n elements.forEach((element, i) => element.setAttribute('tabindex', i === index ? '0' : '-1'));\n }\n };\n\n const currentIndex = Array.prototype.indexOf.call(elements, e.target);\n let nextIndex;\n\n switch (e.key) {\n case arrowNext:\n e.preventDefault();\n nextIndex = (currentIndex + 1 < elements.length) ? currentIndex + 1 : 0;\n focusElement(nextIndex);\n break;\n case arrowPrevious:\n e.preventDefault();\n nextIndex = (currentIndex - 1 >= 0) ? currentIndex - 1 : elements.length - 1;\n focusElement(nextIndex);\n break;\n case 'Home':\n e.preventDefault();\n focusElement(0);\n break;\n case 'End':\n e.preventDefault();\n focusElement(elements.length - 1);\n }\n};\n\n/**\n * Fix accessibility issues regarding tab elements focus and their tab order in Bootstrap navs.\n */\nconst tabElementFix = () => {\n document.addEventListener('keydown', e => {\n if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) {\n if (e.target.matches('[role=\"tablist\"] [role=\"tab\"]')) {\n const tabList = e.target.closest('[role=\"tablist\"]');\n const tabs = Array.prototype.filter.call(\n tabList.querySelectorAll('[role=\"tab\"]'),\n tab => !!tab.offsetHeight\n ); // We only work with the visible tabs.\n const vertical = tabList.getAttribute('aria-orientation') == 'vertical';\n\n rovingFocus(tabs, e, vertical, false);\n }\n }\n });\n\n document.addEventListener('click', e => {\n if (e.target.matches('[role=\"tablist\"] [data-bs-toggle=\"tab\"], [role=\"tablist\"] [data-bs-toggle=\"pill\"]')) {\n const tabs = e.target.closest('[role=\"tablist\"]').querySelectorAll('[data-bs-toggle=\"tab\"], [data-bs-toggle=\"pill\"]');\n e.preventDefault();\n $(e.target).tab('show');\n tabs.forEach(tab => {\n tab.tabIndex = -1;\n });\n e.target.tabIndex = 0;\n }\n });\n};\n\n/**\n * Fix keyboard interaction with Bootstrap Collapse elements.\n *\n * @see {@link https://www.w3.org/TR/wai-aria-practices-1.1/#disclosure|WAI-ARIA Authoring Practices 1.1 - Disclosure (Show/Hide)}\n */\nconst collapseFix = () => {\n document.addEventListener('keydown', e => {\n if (e.target.matches('[data-bs-toggle=\"collapse\"]')) {\n // Pressing space should toggle expand/collapse.\n if (e.key === ' ') {\n e.preventDefault();\n e.target.click();\n }\n }\n });\n};\n\n/**\n * Fix accessibility issues\n */\nconst toolbarFix = () => {\n document.addEventListener('keydown', e => {\n if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) {\n if (e.target.matches('[role=\"toolbar\"] button')) {\n const buttons = e.target.closest('[role=\"toolbar\"]').querySelectorAll('button');\n rovingFocus(buttons, e, false, true);\n }\n }\n });\n};\n\nexport const init = () => {\n dropdownFix();\n comboboxFix();\n autoFocus();\n tabElementFix();\n collapseFix();\n toolbarFix();\n};\n"],"names":["dropdownFix","focusEnd","setFocusEnd","end","shiftFocus","element","focusCheck","pendingPromise","Pending","setTimeout","focus","resolve","handleMenuButton","e","trigger","key","fixFocus","preventDefault","target","click","menu","parentElement","querySelector","menuItems","foundMenuItem","querySelectorAll","length","result","getFocusEnd","document","addEventListener","matches","closest","toLowerCase","i","item","text","trim","indexOf","next","on","dialog","FocusLockManager","trapFocus","untrapFocus","focused","clickEvent","activeElement","body","contains","rovingFocus","elements","vertical","updateTabIndex","rtl","window","right_to_left","arrowNext","arrowPrevious","includes","focusElement","index","forEach","setAttribute","currentIndex","Array","prototype","call","nextIndex","relatedTarget","combobox","listbox","getAttribute","selectedOption","classList","add","id","firstOption","removeAttribute","option","remove","options","activeOption","editable","hasAttribute","selectOption","optionText","textContent","keyPressed","scrollIntoView","block","value","oldSelectedOption","dataset","shortText","replace","selectedOptionContainer","inputElement","getElementById","dispatchEvent","Event","bubbles","comboboxFix","alerts","autofocusElement","innerHTML","tabList","tabs","filter","tab","offsetHeight","tabIndex","buttons"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"aria.min.js","sources":["../src/aria.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 * Enhancements to Bootstrap components for accessibility.\n *\n * @module theme_boost/aria\n * @copyright 2018 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Tab from 'theme_boost/bootstrap/tab';\nimport Pending from 'core/pending';\nimport * as FocusLockManager from 'core/local/aria/focuslock';\n\n/**\n * Drop downs from bootstrap don't support keyboard accessibility by default.\n */\nconst dropdownFix = () => {\n let focusEnd = false;\n const setFocusEnd = (end = true) => {\n focusEnd = end;\n };\n const getFocusEnd = () => {\n const result = focusEnd;\n focusEnd = false;\n return result;\n };\n\n // Special handling for navigation keys when menu is open.\n const shiftFocus = (element, focusCheck = null) => {\n const pendingPromise = new Pending('core/aria:delayed-focus');\n setTimeout(() => {\n if (!focusCheck || focusCheck()) {\n element.focus();\n }\n\n pendingPromise.resolve();\n }, 50);\n };\n\n // Event handling for the dropdown menu button.\n const handleMenuButton = e => {\n const trigger = e.key;\n let fixFocus = false;\n\n // Space key or Enter key opens the menu.\n if (trigger === ' ' || trigger === 'Enter') {\n fixFocus = true;\n // Cancel random scroll.\n e.preventDefault();\n // Open the menu instead.\n e.target.click();\n }\n\n // Up and Down keys also open the menu.\n if (trigger === 'ArrowUp' || trigger === 'ArrowDown') {\n fixFocus = true;\n }\n\n if (!fixFocus) {\n // No need to fix the focus. Return early.\n return;\n }\n\n // Fix the focus on the menu items when the menu is opened.\n const menu = e.target.parentElement.querySelector('[role=\"menu\"]');\n let menuItems = false;\n let foundMenuItem = false;\n\n if (menu) {\n menuItems = menu.querySelectorAll('[role=\"menuitem\"]');\n }\n if (menuItems && menuItems.length > 0) {\n // Up key opens the menu at the end.\n if (trigger === 'ArrowUp') {\n setFocusEnd();\n } else {\n setFocusEnd(false);\n }\n\n if (getFocusEnd()) {\n foundMenuItem = menuItems[menuItems.length - 1];\n } else {\n // The first menu entry, pretty reasonable.\n foundMenuItem = menuItems[0];\n }\n }\n\n if (foundMenuItem) {\n shiftFocus(foundMenuItem);\n }\n };\n\n // Search for menu items by finding the first item that has\n // text starting with the typed character (case insensitive).\n document.addEventListener('keypress', e => {\n if (e.target.matches('[role=\"menu\"] [role=\"menuitem\"]')) {\n const menu = e.target.closest('[role=\"menu\"]');\n if (!menu) {\n return;\n }\n const menuItems = menu.querySelectorAll('[role=\"menuitem\"]');\n if (!menuItems) {\n return;\n }\n\n const trigger = e.key.toLowerCase();\n\n for (let i = 0; i < menuItems.length; i++) {\n const item = menuItems[i];\n const itemText = item.text.trim().toLowerCase();\n if (itemText.indexOf(trigger) == 0) {\n shiftFocus(item);\n break;\n }\n }\n }\n });\n\n // Keyboard navigation for arrow keys, home and end keys.\n document.addEventListener('keydown', e => {\n\n // We only want to set focus when users access the dropdown via keyboard as per\n // guidelines defined in w3 aria practices 1.1 menu-button.\n if (e.target.matches('[data-bs-toggle=\"dropdown\"]')) {\n handleMenuButton(e);\n }\n\n if (e.target.matches('[role=\"menu\"] [role=\"menuitem\"]')) {\n const trigger = e.key;\n let next = false;\n const menu = e.target.closest('[role=\"menu\"]');\n\n if (!menu) {\n return;\n }\n const menuItems = menu.querySelectorAll('[role=\"menuitem\"]');\n if (!menuItems) {\n return;\n }\n // Down key.\n if (trigger == 'ArrowDown') {\n for (let i = 0; i < menuItems.length - 1; i++) {\n if (menuItems[i] == e.target) {\n next = menuItems[i + 1];\n break;\n }\n }\n if (!next) {\n // Wrap to first item.\n next = menuItems[0];\n }\n } else if (trigger == 'ArrowUp') {\n // Up key.\n for (let i = 1; i < menuItems.length; i++) {\n if (menuItems[i] == e.target) {\n next = menuItems[i - 1];\n break;\n }\n }\n if (!next) {\n // Wrap to last item.\n next = menuItems[menuItems.length - 1];\n }\n } else if (trigger == 'Home') {\n // Home key.\n next = menuItems[0];\n\n } else if (trigger == 'End') {\n // End key.\n next = menuItems[menuItems.length - 1];\n }\n\n // Variable next is set if we do want to act on the keypress.\n if (next) {\n e.preventDefault();\n shiftFocus(next);\n }\n return;\n }\n });\n\n // Trap focus if the dropdown is a dialog.\n $(document).on('shown.bs.dropdown', e => {\n const dialog = e.target.querySelector('.dropdown-menu[role=\"dialog\"]');\n if (dialog) {\n // Use setTimeout to make sure the dialog is positioned correctly to prevent random scrolling.\n setTimeout(() => {\n FocusLockManager.trapFocus(dialog);\n });\n }\n });\n\n // Untrap focus when the dialog dropdown is closed.\n $(document).on('hidden.bs.dropdown', e => {\n const dialog = e.target.querySelector('.dropdown-menu[role=\"dialog\"]');\n if (dialog) {\n FocusLockManager.untrapFocus();\n }\n\n // We need to focus on the menu trigger.\n const trigger = e.target.querySelector('[data-bs-toggle=\"dropdown\"]');\n // If it's a click event, then no element is focused because the clicked element is inside a closed dropdown.\n const focused = e.clickEvent?.target || (document.activeElement !== document.body ? document.activeElement : null);\n if (trigger && focused && e.target.contains(focused)) {\n shiftFocus(trigger, () => {\n if (document.activeElement === document.body) {\n // If the focus is currently on the body, then we can safely assume that the focus needs to be updated.\n return true;\n }\n\n // If the focus is on a child of the clicked element still, then update the focus.\n return e.target.contains(document.activeElement);\n });\n }\n });\n};\n\n/**\n * A lot of Bootstrap's out of the box features don't work if dropdown items are not focusable.\n */\nconst comboboxFix = () => {\n $(document).on('show.bs.dropdown', e => {\n if (e.relatedTarget.matches('[role=\"combobox\"]')) {\n const combobox = e.relatedTarget;\n const listbox = document.querySelector(`#${combobox.getAttribute('aria-controls')}[role=\"listbox\"]`);\n\n if (listbox) {\n const selectedOption = listbox.querySelector('[role=\"option\"][aria-selected=\"true\"]');\n\n // To make sure ArrowDown doesn't move the active option afterwards.\n setTimeout(() => {\n if (selectedOption) {\n selectedOption.classList.add('active');\n combobox.setAttribute('aria-activedescendant', selectedOption.id);\n } else {\n const firstOption = listbox.querySelector('[role=\"option\"]');\n firstOption.setAttribute('aria-selected', 'true');\n firstOption.classList.add('active');\n combobox.setAttribute('aria-activedescendant', firstOption.id);\n }\n }, 0);\n }\n }\n });\n\n $(document).on('hidden.bs.dropdown', e => {\n if (e.relatedTarget.matches('[role=\"combobox\"]')) {\n const combobox = e.relatedTarget;\n const listbox = document.querySelector(`#${combobox.getAttribute('aria-controls')}[role=\"listbox\"]`);\n\n combobox.removeAttribute('aria-activedescendant');\n\n if (listbox) {\n setTimeout(() => {\n // Undo all previously highlighted options.\n listbox.querySelectorAll('.active[role=\"option\"]').forEach(option => {\n option.classList.remove('active');\n });\n }, 0);\n }\n }\n });\n\n // Handling keyboard events for both navigating through and selecting options.\n document.addEventListener('keydown', e => {\n if (e.target.matches('[role=\"combobox\"][aria-controls]:not([aria-haspopup=dialog])')) {\n const combobox = e.target;\n const trigger = e.key;\n let next = null;\n const listbox = document.querySelector(`#${combobox.getAttribute('aria-controls')}[role=\"listbox\"]`);\n const options = listbox.querySelectorAll('[role=\"option\"]');\n const activeOption = listbox.querySelector('.active[role=\"option\"]');\n const editable = combobox.hasAttribute('aria-autocomplete');\n\n // Under the special case that the dropdown menu is being shown as a result of the key press (like when the user\n // presses ArrowDown or Enter or ... to open the dropdown menu), activeOption is not set yet.\n // It's because of a race condition with show.bs.dropdown event handler.\n if (options && (activeOption || editable)) {\n if (trigger == 'ArrowDown') {\n for (let i = 0; i < options.length - 1; i++) {\n if (options[i] == activeOption) {\n next = options[i + 1];\n break;\n }\n }\n if (editable && !next) {\n next = options[0];\n }\n } if (trigger == 'ArrowUp') {\n for (let i = 1; i < options.length; i++) {\n if (options[i] == activeOption) {\n next = options[i - 1];\n break;\n }\n }\n if (editable && !next) {\n next = options[options.length - 1];\n }\n } else if (trigger == 'Home' && !editable) {\n next = options[0];\n } else if (trigger == 'End' && !editable) {\n next = options[options.length - 1];\n } else if ((trigger == ' ' && !editable) || trigger == 'Enter') {\n e.preventDefault();\n selectOption(combobox, activeOption);\n } else if (!editable) {\n // Search for options by finding the first option that has\n // text starting with the typed character (case insensitive).\n for (let i = 0; i < options.length; i++) {\n const option = options[i];\n const optionText = option.textContent.trim().toLowerCase();\n const keyPressed = e.key.toLowerCase();\n if (optionText.indexOf(keyPressed) == 0) {\n next = option;\n break;\n }\n }\n }\n\n // Variable next is set if we do want to act on the keypress.\n if (next) {\n e.preventDefault();\n if (activeOption) {\n activeOption.classList.remove('active');\n }\n next.classList.add('active');\n combobox.setAttribute('aria-activedescendant', next.id);\n next.scrollIntoView({block: 'nearest'});\n }\n }\n }\n });\n\n document.addEventListener('click', e => {\n const option = e.target.closest('[role=\"listbox\"] [role=\"option\"]');\n if (option) {\n const listbox = option.closest('[role=\"listbox\"]');\n const combobox = document.querySelector(`[role=\"combobox\"][aria-controls=\"${listbox.id}\"]`);\n if (combobox) {\n selectOption(combobox, option);\n }\n }\n });\n\n // In case some code somewhere else changes the value of the combobox.\n document.addEventListener('change', e => {\n if (e.target.matches('input[type=\"hidden\"][id]')) {\n const combobox = document.querySelector(`[role=\"combobox\"][data-input-element=\"${e.target.id}\"]`);\n const option = e.target.parentElement.querySelector(`[role=\"option\"][data-value=\"${e.target.value}\"]`);\n\n if (combobox && option) {\n selectOption(combobox, option);\n }\n }\n });\n\n const selectOption = (combobox, option) => {\n const listbox = option.closest('[role=\"listbox\"]');\n const oldSelectedOption = listbox.querySelector('[role=\"option\"][aria-selected=\"true\"]');\n\n if (oldSelectedOption != option) {\n if (oldSelectedOption) {\n oldSelectedOption.removeAttribute('aria-selected');\n }\n option.setAttribute('aria-selected', 'true');\n }\n\n if (combobox.hasAttribute('value')) {\n combobox.value = option.dataset.shortText || option.textContent.replace(/[\\n\\r]+|[\\s]{2,}/g, ' ').trim();\n } else {\n const selectedOptionContainer = combobox.querySelector('[data-selected-option]');\n if (selectedOptionContainer) {\n selectedOptionContainer.textContent = option.dataset.shortText || option.textContent;\n } else {\n combobox.textContent = option.dataset.shortText || option.textContent;\n }\n }\n\n if (combobox.dataset.inputElement) {\n const inputElement = document.getElementById(combobox.dataset.inputElement);\n if (inputElement && (inputElement.value != option.dataset.value)) {\n inputElement.value = option.dataset.value;\n inputElement.dispatchEvent(new Event('change', {bubbles: true}));\n }\n }\n };\n};\n\n/**\n * After page load, focus on any element with special autofocus attribute.\n */\nconst autoFocus = () => {\n window.addEventListener(\"load\", () => {\n const alerts = document.querySelectorAll('[data-aria-autofocus=\"true\"][role=\"alert\"]');\n Array.prototype.forEach.call(alerts, autofocusElement => {\n // According to the specification an role=\"alert\" region is only read out on change to the content\n // of that region.\n autofocusElement.innerHTML += ' ';\n autofocusElement.removeAttribute('data-aria-autofocus');\n });\n });\n};\n\n/**\n * Changes the focus to the correct element based on the key that is pressed.\n * @param {NodeList} elements A NodeList of focusable elements to navigate between.\n * @param {KeyboardEvent} e The keyboard event that triggers the roving focus.\n * @param {boolean} vertical Whether the navigation is vertical.\n * @param {boolean} updateTabIndex Whether to update the tabIndex of the elements.\n */\nconst rovingFocus = (elements, e, vertical, updateTabIndex) => {\n const rtl = window.right_to_left();\n const arrowNext = vertical ? 'ArrowDown' : (rtl ? 'ArrowLeft' : 'ArrowRight');\n const arrowPrevious = vertical ? 'ArrowUp' : (rtl ? 'ArrowRight' : 'ArrowLeft');\n const keys = [arrowNext, arrowPrevious, 'Home', 'End'];\n\n if (!keys.includes(e.key)) {\n return;\n }\n\n const focusElement = index => {\n elements[index].focus();\n if (updateTabIndex) {\n elements.forEach((element, i) => element.setAttribute('tabindex', i === index ? '0' : '-1'));\n }\n };\n\n const currentIndex = Array.prototype.indexOf.call(elements, e.target);\n let nextIndex;\n\n switch (e.key) {\n case arrowNext:\n e.preventDefault();\n nextIndex = (currentIndex + 1 < elements.length) ? currentIndex + 1 : 0;\n focusElement(nextIndex);\n break;\n case arrowPrevious:\n e.preventDefault();\n nextIndex = (currentIndex - 1 >= 0) ? currentIndex - 1 : elements.length - 1;\n focusElement(nextIndex);\n break;\n case 'Home':\n e.preventDefault();\n focusElement(0);\n break;\n case 'End':\n e.preventDefault();\n focusElement(elements.length - 1);\n }\n};\n\n/**\n * Fix accessibility issues regarding tab elements focus and their tab order in Bootstrap navs.\n */\nconst tabElementFix = () => {\n document.addEventListener('keydown', e => {\n if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) {\n if (e.target.matches('[role=\"tablist\"] [role=\"tab\"]')) {\n const tabList = e.target.closest('[role=\"tablist\"]');\n const tabs = Array.prototype.filter.call(\n tabList.querySelectorAll('[role=\"tab\"]'),\n tab => !!tab.offsetHeight\n ); // We only work with the visible tabs.\n const vertical = tabList.getAttribute('aria-orientation') == 'vertical';\n\n rovingFocus(tabs, e, vertical, false);\n }\n }\n });\n\n document.addEventListener('click', e => {\n if (e.target.matches('[role=\"tablist\"] [data-bs-toggle=\"tab\"], [role=\"tablist\"] [data-bs-toggle=\"pill\"]')) {\n const tabs = e.target.closest('[role=\"tablist\"]').querySelectorAll('[data-bs-toggle=\"tab\"], [data-bs-toggle=\"pill\"]');\n e.preventDefault();\n Tab.getOrCreateInstance(e.target).show();\n tabs.forEach(tab => {\n tab.tabIndex = -1;\n });\n e.target.tabIndex = 0;\n }\n });\n};\n\n/**\n * Fix keyboard interaction with Bootstrap Collapse elements.\n *\n * @see {@link https://www.w3.org/TR/wai-aria-practices-1.1/#disclosure|WAI-ARIA Authoring Practices 1.1 - Disclosure (Show/Hide)}\n */\nconst collapseFix = () => {\n document.addEventListener('keydown', e => {\n if (e.target.matches('[data-bs-toggle=\"collapse\"]')) {\n // Pressing space should toggle expand/collapse.\n if (e.key === ' ') {\n e.preventDefault();\n e.target.click();\n }\n }\n });\n};\n\n/**\n * Fix accessibility issues\n */\nconst toolbarFix = () => {\n document.addEventListener('keydown', e => {\n if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) {\n if (e.target.matches('[role=\"toolbar\"] button')) {\n const buttons = e.target.closest('[role=\"toolbar\"]').querySelectorAll('button');\n rovingFocus(buttons, e, false, true);\n }\n }\n });\n};\n\nexport const init = () => {\n dropdownFix();\n comboboxFix();\n autoFocus();\n tabElementFix();\n collapseFix();\n toolbarFix();\n};\n"],"names":["dropdownFix","focusEnd","setFocusEnd","end","shiftFocus","element","focusCheck","pendingPromise","Pending","setTimeout","focus","resolve","handleMenuButton","e","trigger","key","fixFocus","preventDefault","target","click","menu","parentElement","querySelector","menuItems","foundMenuItem","querySelectorAll","length","result","getFocusEnd","document","addEventListener","matches","closest","toLowerCase","i","item","text","trim","indexOf","next","on","dialog","FocusLockManager","trapFocus","untrapFocus","focused","clickEvent","activeElement","body","contains","rovingFocus","elements","vertical","updateTabIndex","rtl","window","right_to_left","arrowNext","arrowPrevious","includes","focusElement","index","forEach","setAttribute","currentIndex","Array","prototype","call","nextIndex","relatedTarget","combobox","listbox","getAttribute","selectedOption","classList","add","id","firstOption","removeAttribute","option","remove","options","activeOption","editable","hasAttribute","selectOption","optionText","textContent","keyPressed","scrollIntoView","block","value","oldSelectedOption","dataset","shortText","replace","selectedOptionContainer","inputElement","getElementById","dispatchEvent","Event","bubbles","comboboxFix","alerts","autofocusElement","innerHTML","tabList","tabs","filter","tab","offsetHeight","getOrCreateInstance","show","tabIndex","buttons"],"mappings":"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"} \ No newline at end of file diff --git a/theme/boost/amd/build/drawers.min.js b/theme/boost/amd/build/drawers.min.js index 4fe5b1fa3f84e..26b9174869ac7 100644 --- a/theme/boost/amd/build/drawers.min.js +++ b/theme/boost/amd/build/drawers.min.js @@ -1,3 +1,3 @@ -define("theme_boost/drawers",["exports","core/modal_backdrop","core/templates","core/aria","core/event_dispatcher","core/utils","core/pagehelpers","core/pending","core_user/repository","jquery"],(function(_exports,_modal_backdrop,_templates,Aria,_event_dispatcher,_utils,_pagehelpers,_pending,_repository,_jquery){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}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,_modal_backdrop=_interopRequireDefault(_modal_backdrop),_templates=_interopRequireDefault(_templates),Aria=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Aria),_pending=_interopRequireDefault(_pending),_jquery=_interopRequireDefault(_jquery);let backdropPromise=null;const drawerMap=new Map,SELECTORS_BUTTONS='[data-toggler="drawers"]',SELECTORS_CLOSEBTN='[data-toggler="drawers"][data-action="closedrawer"]',SELECTORS_OPENBTN='[data-toggler="drawers"][data-action="opendrawer"]',SELECTORS_TOGGLEBTN='[data-toggler="drawers"][data-action="toggle"]',SELECTORS_DRAWERS='[data-region="fixed-drawer"]',SELECTORS_DRAWERCONTENT=".drawercontent",SELECTORS_PAGECONTENT="#page-content",SELECTORS_HEADERCONTENT=".drawerheadercontent",CLASSES_SCROLLED="scrolled",CLASSES_SHOW="show",CLASSES_NOTINITIALISED="not-initialized",getDrawerZIndex=()=>{const drawer=document.querySelector(SELECTORS_DRAWERS);return drawer?parseInt(window.getComputedStyle(drawer).zIndex,10):null},getBackdrop=()=>(backdropPromise||(backdropPromise=_templates.default.render("core/modal_backdrop",{}).then((html=>new _modal_backdrop.default(html))).then((modalBackdrop=>(getDrawerZIndex()&&modalBackdrop.setZIndex(getDrawerZIndex()-1),modalBackdrop.getAttachmentPoint().get(0).addEventListener("click",(e=>{e.preventDefault(),Drawers.closeAllDrawers()})),modalBackdrop))).catch()),backdropPromise),getDrawerOpenButton=drawerId=>{let openButton=document.querySelector("".concat(SELECTORS_OPENBTN,'[data-target="').concat(drawerId,'"]'));return openButton||(openButton=document.querySelector("".concat(SELECTORS_TOGGLEBTN,'[data-target="').concat(drawerId,'"]'))),openButton},disableDrawerTooltips=drawerNode=>{[drawerNode.querySelector(SELECTORS_CLOSEBTN),getDrawerOpenButton(drawerNode.id)].forEach((button=>{button&&disableButtonTooltip(button)}))},disableButtonTooltip=(button,enableOnBlur)=>{button.hasAttribute("data-original-title")?((0,_jquery.default)(button).tooltip("disable"),button.setAttribute("title",button.dataset.originalTitle)):(button.dataset.disabledToggle=button.dataset.toggle,button.removeAttribute("data-toggle")),enableOnBlur&&(button.dataset.restoreTooltipOnBlur=!0)},enableButtonTooltip=button=>{button.hasAttribute("data-original-title")?((0,_jquery.default)(button).tooltip("enable"),button.removeAttribute("title")):button.dataset.disabledToggle&&(button.dataset.toggle=button.dataset.disabledToggle,(0,_jquery.default)(button).tooltip()),delete button.dataset.restoreTooltipOnBlur};class Drawers{constructor(drawerNode){_defineProperty(this,"drawerNode",null),_defineProperty(this,"boundingRect",null),void 0===drawerNode.dataset.behatFakeDrawer&&(this.drawerNode=drawerNode,(0,_pagehelpers.isSmall)()&&this.closeDrawer({focusOnOpenButton:!1,updatePreferences:!1}),this.drawerNode.classList.contains(CLASSES_SHOW)?this.openDrawer({focusOnCloseButton:!1,setUserPref:!1}):1==this.drawerNode.dataset.forceopen?(0,_pagehelpers.isSmall)()||this.openDrawer({focusOnCloseButton:!1,setUserPref:!1}):Aria.hide(this.drawerNode),(0,_pagehelpers.isSmall)()&&disableDrawerTooltips(this.drawerNode),(drawerNode=>{const content=drawerNode.querySelector(SELECTORS_DRAWERCONTENT);content&&content.addEventListener("scroll",(()=>{drawerNode.classList.toggle(CLASSES_SCROLLED,0!=content.scrollTop)}))})(this.drawerNode),drawerMap.set(drawerNode,this),drawerNode.classList.remove(CLASSES_NOTINITIALISED))}get isOpen(){return this.drawerNode.classList.contains(CLASSES_SHOW)}get closeOnResize(){return!!parseInt(this.drawerNode.dataset.closeOnResize)}static getDrawerInstanceForNode(drawerNode){return drawerMap.has(drawerNode)||new Drawers(drawerNode),drawerMap.get(drawerNode)}dispatchEvent(eventname){let cancelable=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return(0,_event_dispatcher.dispatchEvent)(eventname,{drawerInstance:this},this.drawerNode,{cancelable:cancelable})}openDrawer(){var _this$drawerNode$quer,_this$drawerNode$quer2;let{focusOnCloseButton:focusOnCloseButton=!0,setUserPref:setUserPref=!0}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const pendingPromise=new _pending.default("theme_boost/drawers:open");if(this.dispatchEvent(Drawers.eventTypes.drawerShow,!0).defaultPrevented)return;null===(_this$drawerNode$quer=this.drawerNode.querySelector(SELECTORS_CLOSEBTN))||void 0===_this$drawerNode$quer||_this$drawerNode$quer.classList.toggle("hidden",!0),null===(_this$drawerNode$quer2=this.drawerNode.querySelector(SELECTORS_HEADERCONTENT))||void 0===_this$drawerNode$quer2||_this$drawerNode$quer2.classList.toggle("hidden",!0);let openButton=getDrawerOpenButton(this.drawerNode.id);var _jQuery;openButton&&openButton.hasAttribute("data-original-title")&&(null===(_jQuery=(0,_jquery.default)(openButton))||void 0===_jQuery||_jQuery.tooltip("hide"));Aria.unhide(this.drawerNode),this.drawerNode.classList.add(CLASSES_SHOW);const preference=this.drawerNode.dataset.preference;preference&&!(0,_pagehelpers.isSmall)()&&1!=this.drawerNode.dataset.forceopen&&setUserPref&&(0,_repository.setUserPreference)(preference,!0);const state=this.drawerNode.dataset.state;if(state){document.getElementById("page").classList.add(state)}this.boundingRect=this.drawerNode.getBoundingClientRect(),(0,_pagehelpers.isSmall)()&&getBackdrop().then((backdrop=>{backdrop.show();return document.getElementById("page").style.overflow="hidden",backdrop})).catch();const closeButton=this.drawerNode.querySelector(SELECTORS_CLOSEBTN),headerContent=this.drawerNode.querySelector(SELECTORS_HEADERCONTENT);focusOnCloseButton&&closeButton&&disableButtonTooltip(closeButton,!0),setTimeout((()=>{closeButton.classList.toggle("hidden",!1),headerContent.classList.toggle("hidden",!1),focusOnCloseButton&&closeButton.focus(),pendingPromise.resolve()}),300),this.dispatchEvent(Drawers.eventTypes.drawerShown)}closeDrawer(){let{focusOnOpenButton:focusOnOpenButton=!0,updatePreferences:updatePreferences=!0}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const pendingPromise=new _pending.default("theme_boost/drawers:close");if(this.dispatchEvent(Drawers.eventTypes.drawerHide,!0).defaultPrevented)return;const closeButton=this.drawerNode.querySelector(SELECTORS_CLOSEBTN);null==closeButton||closeButton.classList.toggle("hidden",!0);const headerContent=this.drawerNode.querySelector(SELECTORS_HEADERCONTENT);var _jQuery2;(null==headerContent||headerContent.classList.toggle("hidden",!0),closeButton.hasAttribute("data-original-title"))&&(null===(_jQuery2=(0,_jquery.default)(closeButton))||void 0===_jQuery2||_jQuery2.tooltip("hide"));const preference=this.drawerNode.dataset.preference;preference&&updatePreferences&&!(0,_pagehelpers.isSmall)()&&(0,_repository.setUserPreference)(preference,!1);const state=this.drawerNode.dataset.state;if(state){document.getElementById("page").classList.remove(state)}Aria.hide(this.drawerNode),this.drawerNode.classList.remove(CLASSES_SHOW),getBackdrop().then((backdrop=>{if(backdrop.hide(),(0,_pagehelpers.isSmall)()){document.getElementById("page").style.overflow="visible"}return backdrop})).catch();let openButton=getDrawerOpenButton(this.drawerNode.id);openButton&&disableButtonTooltip(openButton,!0),setTimeout((()=>{openButton&&focusOnOpenButton&&openButton.focus(),pendingPromise.resolve()}),300),this.dispatchEvent(Drawers.eventTypes.drawerHidden)}toggleVisibility(){this.drawerNode.classList.contains(CLASSES_SHOW)?this.closeDrawer():this.openDrawer()}displace(scrollPosition){var _this$drawerNode$data;let displace=scrollPosition,openButton=getDrawerOpenButton(this.drawerNode.id);if(0===scrollPosition)return this.drawerNode.style.transform="",void(openButton&&(openButton.style.transform=""));const state=null===(_this$drawerNode$data=this.drawerNode.dataset)||void 0===_this$drawerNode$data?void 0:_this$drawerNode$data.state,drawrWidth=this.drawerNode.offsetWidth;let scrollThreshold=drawrWidth,direction=-1;"show-drawer-right"===state&&(direction=1,scrollThreshold=20),Math.abs(scrollPosition)>scrollThreshold&&(displace=Math.sign(scrollPosition)*(drawrWidth+20)),displace*=direction;const transform="translateX(".concat(displace,"px)");openButton&&(openButton.style.transform=transform),this.drawerNode.style.transform=transform}preventOverlap(currentFocus){var _this$drawerNode$data2;if(!this.isOpen||"show-drawer-left"===(null===(_this$drawerNode$data2=this.drawerNode.dataset)||void 0===_this$drawerNode$data2?void 0:_this$drawerNode$data2.state))return;const drawrWidth=this.drawerNode.offsetWidth,element=currentFocus.getBoundingClientRect();let overlapping=element.right+20>this.boundingRect.left&&element.left-20currentBoundingRect.top&&element.top{drawerInstance.closeDrawer()}))}static closeOtherDrawers(comparisonInstance){drawerMap.forEach((drawerInstance=>{drawerInstance!==comparisonInstance&&drawerInstance.closeDrawer()}))}static preventCoveringFocusedElement(){const currentFocus=document.activeElement,pagecontent=document.querySelector(SELECTORS_PAGECONTENT);currentFocus&&null!=pagecontent&&pagecontent.contains(currentFocus)?drawerMap.forEach((drawerInstance=>{drawerInstance.preventOverlap(currentFocus)})):Drawers.displaceDrawers(window.scrollX)}static displaceDrawers(displace){drawerMap.forEach((drawerInstance=>{drawerInstance.displace(displace)}))}}_exports.default=Drawers,_defineProperty(Drawers,"eventTypes",{drawerShow:"theme_boost/drawers:show",drawerShown:"theme_boost/drawers:shown",drawerHide:"theme_boost/drawers:hide",drawerHidden:"theme_boost/drawers:hidden"});const setLastUsedToggle=toggleButton=>{toggleButton.dataset.target&&(document.querySelectorAll("".concat(SELECTORS_BUTTONS,'[data-target="').concat(toggleButton.dataset.target,'"]')).forEach((btn=>{btn.dataset.lastused=!1})),toggleButton.dataset.lastused=!0)};(()=>{document.addEventListener("click",(e=>{const toggleButton=e.target.closest(SELECTORS_TOGGLEBTN);if(toggleButton&&toggleButton.dataset.target){e.preventDefault();const targetDrawer=document.getElementById(toggleButton.dataset.target),drawerInstance=Drawers.getDrawerInstanceForNode(targetDrawer);setLastUsedToggle(toggleButton),drawerInstance.toggleVisibility()}const openDrawerButton=e.target.closest(SELECTORS_OPENBTN);if(openDrawerButton&&openDrawerButton.dataset.target){e.preventDefault();const targetDrawer=document.getElementById(openDrawerButton.dataset.target),drawerInstance=Drawers.getDrawerInstanceForNode(targetDrawer);setLastUsedToggle(toggleButton),drawerInstance.openDrawer()}const closeDrawerButton=e.target.closest(SELECTORS_CLOSEBTN);if(closeDrawerButton&&closeDrawerButton.dataset.target){e.preventDefault();const targetDrawer=document.getElementById(closeDrawerButton.dataset.target);Drawers.getDrawerInstanceForNode(targetDrawer).closeDrawer(),(target=>{const lastUsedButton=document.querySelector("".concat(SELECTORS_BUTTONS,'[data-target="').concat(target,'"][data-lastused="true"'));lastUsedButton&&lastUsedButton.focus()})(closeDrawerButton.dataset.target)}})),document.addEventListener(Drawers.eventTypes.drawerShow,(e=>{(0,_pagehelpers.isLarge)()||Drawers.closeOtherDrawers(e.detail.drawerInstance)}));const btnSelector="".concat(SELECTORS_TOGGLEBTN,", ").concat(SELECTORS_OPENBTN,", ").concat(SELECTORS_CLOSEBTN);document.addEventListener("focusout",(e=>{const button=e.target.closest(btnSelector);void 0!==(null==button?void 0:button.dataset.restoreTooltipOnBlur)&&enableButtonTooltip(button)}));document.addEventListener("scroll",(()=>{const currentFocus=document.activeElement,drawerContentElements=document.querySelectorAll(SELECTORS_DRAWERCONTENT);if(Array.from(drawerContentElements).some((drawer=>drawer.contains(currentFocus))))return;const body=document.querySelector("body");window.scrollY>=window.innerHeight?body.classList.add(CLASSES_SCROLLED):body.classList.remove(CLASSES_SCROLLED),Drawers.displaceDrawers(window.scrollX)}));const preventOverlap=(0,_utils.debounce)(Drawers.preventCoveringFocusedElement,100);document.addEventListener("focusin",preventOverlap),document.addEventListener("focusout",preventOverlap),window.addEventListener("resize",(0,_utils.debounce)((()=>{if((0,_pagehelpers.isSmall)()){let anyOpen=!1;drawerMap.forEach((drawerInstance=>{if(disableDrawerTooltips(drawerInstance.drawerNode),drawerInstance.isOpen){const currentFocus=document.activeElement,drawerContent=drawerInstance.drawerNode.querySelector(SELECTORS_DRAWERCONTENT);drawerInstance.closeOnResize&&(!drawerContent||!drawerContent.contains(currentFocus))?drawerInstance.closeDrawer():anyOpen=!0}})),anyOpen&&getBackdrop().then((backdrop=>backdrop.show())).catch()}else drawerMap.forEach((drawerInstance=>{var drawerNode;[(drawerNode=drawerInstance.drawerNode).querySelector(SELECTORS_CLOSEBTN),getDrawerOpenButton(drawerNode.id)].forEach((button=>{button&&enableButtonTooltip(button)}))})),getBackdrop().then((backdrop=>backdrop.hide())).catch()}),400,{pending:!0}))})();return document.querySelectorAll(SELECTORS_DRAWERS).forEach((drawerNode=>Drawers.getDrawerInstanceForNode(drawerNode))),_exports.default})); +define("theme_boost/drawers",["exports","core/modal_backdrop","core/templates","core/aria","core/event_dispatcher","core/utils","core/pagehelpers","core/pending","core_user/repository","./bootstrap/tooltip"],(function(_exports,_modal_backdrop,_templates,Aria,_event_dispatcher,_utils,_pagehelpers,_pending,_repository,_tooltip){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}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,_modal_backdrop=_interopRequireDefault(_modal_backdrop),_templates=_interopRequireDefault(_templates),Aria=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Aria),_pending=_interopRequireDefault(_pending),_tooltip=_interopRequireDefault(_tooltip);let backdropPromise=null;const drawerMap=new Map,SELECTORS_BUTTONS='[data-toggler="drawers"]',SELECTORS_CLOSEBTN='[data-toggler="drawers"][data-action="closedrawer"]',SELECTORS_OPENBTN='[data-toggler="drawers"][data-action="opendrawer"]',SELECTORS_TOGGLEBTN='[data-toggler="drawers"][data-action="toggle"]',SELECTORS_DRAWERS='[data-region="fixed-drawer"]',SELECTORS_DRAWERCONTENT=".drawercontent",SELECTORS_PAGECONTENT="#page-content",SELECTORS_HEADERCONTENT=".drawerheadercontent",CLASSES_SCROLLED="scrolled",CLASSES_SHOW="show",CLASSES_NOTINITIALISED="not-initialized",getDrawerZIndex=()=>{const drawer=document.querySelector(SELECTORS_DRAWERS);return drawer?parseInt(window.getComputedStyle(drawer).zIndex,10):null},getBackdrop=()=>(backdropPromise||(backdropPromise=_templates.default.render("core/modal_backdrop",{}).then((html=>new _modal_backdrop.default(html))).then((modalBackdrop=>(getDrawerZIndex()&&modalBackdrop.setZIndex(getDrawerZIndex()-1),modalBackdrop.getAttachmentPoint().get(0).addEventListener("click",(e=>{e.preventDefault(),Drawers.closeAllDrawers()})),modalBackdrop))).catch()),backdropPromise),getDrawerOpenButton=drawerId=>{let openButton=document.querySelector("".concat(SELECTORS_OPENBTN,'[data-target="').concat(drawerId,'"]'));return openButton||(openButton=document.querySelector("".concat(SELECTORS_TOGGLEBTN,'[data-target="').concat(drawerId,'"]'))),openButton},disableDrawerTooltips=drawerNode=>{[drawerNode.querySelector(SELECTORS_CLOSEBTN),getDrawerOpenButton(drawerNode.id)].forEach((button=>{button&&disableButtonTooltip(button)}))},disableButtonTooltip=(button,enableOnBlur)=>{button.hasAttribute("data-original-title")?(_tooltip.default.getInstance(button).disable(),button.setAttribute("title",button.dataset.originalTitle)):(button.dataset.disabledToggle=button.dataset.toggle,button.removeAttribute("data-bs-toggle")),enableOnBlur&&(button.dataset.restoreTooltipOnBlur=!0)},enableButtonTooltip=button=>{button.hasAttribute("data-bs-original-title")?(_tooltip.default.getInstance(button).enable(),button.removeAttribute("title")):button.dataset.disabledToggle&&(button.dataset.toggle=button.dataset.disabledToggle,new _tooltip.default(button)),delete button.dataset.restoreTooltipOnBlur};class Drawers{constructor(drawerNode){_defineProperty(this,"drawerNode",null),_defineProperty(this,"boundingRect",null),void 0===drawerNode.dataset.behatFakeDrawer&&(this.drawerNode=drawerNode,(0,_pagehelpers.isSmall)()&&this.closeDrawer({focusOnOpenButton:!1,updatePreferences:!1}),this.drawerNode.classList.contains(CLASSES_SHOW)?this.openDrawer({focusOnCloseButton:!1,setUserPref:!1}):1==this.drawerNode.dataset.forceopen?(0,_pagehelpers.isSmall)()||this.openDrawer({focusOnCloseButton:!1,setUserPref:!1}):Aria.hide(this.drawerNode),(0,_pagehelpers.isSmall)()&&disableDrawerTooltips(this.drawerNode),(drawerNode=>{const content=drawerNode.querySelector(SELECTORS_DRAWERCONTENT);content&&content.addEventListener("scroll",(()=>{drawerNode.classList.toggle(CLASSES_SCROLLED,0!=content.scrollTop)}))})(this.drawerNode),drawerMap.set(drawerNode,this),drawerNode.classList.remove(CLASSES_NOTINITIALISED))}get isOpen(){return this.drawerNode.classList.contains(CLASSES_SHOW)}get closeOnResize(){return!!parseInt(this.drawerNode.dataset.closeOnResize)}static getDrawerInstanceForNode(drawerNode){return drawerMap.has(drawerNode)||new Drawers(drawerNode),drawerMap.get(drawerNode)}dispatchEvent(eventname){let cancelable=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return(0,_event_dispatcher.dispatchEvent)(eventname,{drawerInstance:this},this.drawerNode,{cancelable:cancelable})}openDrawer(){var _this$drawerNode$quer,_this$drawerNode$quer2;let{focusOnCloseButton:focusOnCloseButton=!0,setUserPref:setUserPref=!0}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const pendingPromise=new _pending.default("theme_boost/drawers:open");if(this.dispatchEvent(Drawers.eventTypes.drawerShow,!0).defaultPrevented)return;null===(_this$drawerNode$quer=this.drawerNode.querySelector(SELECTORS_CLOSEBTN))||void 0===_this$drawerNode$quer||_this$drawerNode$quer.classList.toggle("hidden",!0),null===(_this$drawerNode$quer2=this.drawerNode.querySelector(SELECTORS_HEADERCONTENT))||void 0===_this$drawerNode$quer2||_this$drawerNode$quer2.classList.toggle("hidden",!0);let openButton=getDrawerOpenButton(this.drawerNode.id);var _Tooltip$getInstance;openButton&&openButton.hasAttribute("data-original-title")&&(null===(_Tooltip$getInstance=_tooltip.default.getInstance(openButton))||void 0===_Tooltip$getInstance||_Tooltip$getInstance.hide());Aria.unhide(this.drawerNode),this.drawerNode.classList.add(CLASSES_SHOW);const preference=this.drawerNode.dataset.preference;preference&&!(0,_pagehelpers.isSmall)()&&1!=this.drawerNode.dataset.forceopen&&setUserPref&&(0,_repository.setUserPreference)(preference,!0);const state=this.drawerNode.dataset.state;if(state){document.getElementById("page").classList.add(state)}this.boundingRect=this.drawerNode.getBoundingClientRect(),(0,_pagehelpers.isSmall)()&&getBackdrop().then((backdrop=>{backdrop.show();return document.getElementById("page").style.overflow="hidden",backdrop})).catch();const closeButton=this.drawerNode.querySelector(SELECTORS_CLOSEBTN),headerContent=this.drawerNode.querySelector(SELECTORS_HEADERCONTENT);focusOnCloseButton&&closeButton&&disableButtonTooltip(closeButton,!0),setTimeout((()=>{closeButton.classList.toggle("hidden",!1),headerContent.classList.toggle("hidden",!1),focusOnCloseButton&&closeButton.focus(),pendingPromise.resolve()}),300),this.dispatchEvent(Drawers.eventTypes.drawerShown)}closeDrawer(){let{focusOnOpenButton:focusOnOpenButton=!0,updatePreferences:updatePreferences=!0}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const pendingPromise=new _pending.default("theme_boost/drawers:close");if(this.dispatchEvent(Drawers.eventTypes.drawerHide,!0).defaultPrevented)return;const closeButton=this.drawerNode.querySelector(SELECTORS_CLOSEBTN);null==closeButton||closeButton.classList.toggle("hidden",!0);const headerContent=this.drawerNode.querySelector(SELECTORS_HEADERCONTENT);var _Tooltip$getInstance2;(null==headerContent||headerContent.classList.toggle("hidden",!0),closeButton.hasAttribute("data-original-title"))&&(null===(_Tooltip$getInstance2=_tooltip.default.getInstance(closeButton))||void 0===_Tooltip$getInstance2||_Tooltip$getInstance2.hide());const preference=this.drawerNode.dataset.preference;preference&&updatePreferences&&!(0,_pagehelpers.isSmall)()&&(0,_repository.setUserPreference)(preference,!1);const state=this.drawerNode.dataset.state;if(state){document.getElementById("page").classList.remove(state)}Aria.hide(this.drawerNode),this.drawerNode.classList.remove(CLASSES_SHOW),getBackdrop().then((backdrop=>{if(backdrop.hide(),(0,_pagehelpers.isSmall)()){document.getElementById("page").style.overflow="visible"}return backdrop})).catch();let openButton=getDrawerOpenButton(this.drawerNode.id);openButton&&disableButtonTooltip(openButton,!0),setTimeout((()=>{openButton&&focusOnOpenButton&&openButton.focus(),pendingPromise.resolve()}),300),this.dispatchEvent(Drawers.eventTypes.drawerHidden)}toggleVisibility(){this.drawerNode.classList.contains(CLASSES_SHOW)?this.closeDrawer():this.openDrawer()}displace(scrollPosition){var _this$drawerNode$data;let displace=scrollPosition,openButton=getDrawerOpenButton(this.drawerNode.id);if(0===scrollPosition)return this.drawerNode.style.transform="",void(openButton&&(openButton.style.transform=""));const state=null===(_this$drawerNode$data=this.drawerNode.dataset)||void 0===_this$drawerNode$data?void 0:_this$drawerNode$data.state,drawrWidth=this.drawerNode.offsetWidth;let scrollThreshold=drawrWidth,direction=-1;"show-drawer-right"===state&&(direction=1,scrollThreshold=20),Math.abs(scrollPosition)>scrollThreshold&&(displace=Math.sign(scrollPosition)*(drawrWidth+20)),displace*=direction;const transform="translateX(".concat(displace,"px)");openButton&&(openButton.style.transform=transform),this.drawerNode.style.transform=transform}preventOverlap(currentFocus){var _this$drawerNode$data2;if(!this.isOpen||"show-drawer-left"===(null===(_this$drawerNode$data2=this.drawerNode.dataset)||void 0===_this$drawerNode$data2?void 0:_this$drawerNode$data2.state))return;const drawrWidth=this.drawerNode.offsetWidth,element=currentFocus.getBoundingClientRect();let overlapping=element.right+20>this.boundingRect.left&&element.left-20currentBoundingRect.top&&element.top{drawerInstance.closeDrawer()}))}static closeOtherDrawers(comparisonInstance){drawerMap.forEach((drawerInstance=>{drawerInstance!==comparisonInstance&&drawerInstance.closeDrawer()}))}static preventCoveringFocusedElement(){const currentFocus=document.activeElement,pagecontent=document.querySelector(SELECTORS_PAGECONTENT);currentFocus&&null!=pagecontent&&pagecontent.contains(currentFocus)?drawerMap.forEach((drawerInstance=>{drawerInstance.preventOverlap(currentFocus)})):Drawers.displaceDrawers(window.scrollX)}static displaceDrawers(displace){drawerMap.forEach((drawerInstance=>{drawerInstance.displace(displace)}))}}_exports.default=Drawers,_defineProperty(Drawers,"eventTypes",{drawerShow:"theme_boost/drawers:show",drawerShown:"theme_boost/drawers:shown",drawerHide:"theme_boost/drawers:hide",drawerHidden:"theme_boost/drawers:hidden"});const setLastUsedToggle=toggleButton=>{toggleButton.dataset.target&&(document.querySelectorAll("".concat(SELECTORS_BUTTONS,'[data-target="').concat(toggleButton.dataset.target,'"]')).forEach((btn=>{btn.dataset.lastused=!1})),toggleButton.dataset.lastused=!0)};(()=>{document.addEventListener("click",(e=>{const toggleButton=e.target.closest(SELECTORS_TOGGLEBTN);if(toggleButton&&toggleButton.dataset.target){e.preventDefault();const targetDrawer=document.getElementById(toggleButton.dataset.target),drawerInstance=Drawers.getDrawerInstanceForNode(targetDrawer);setLastUsedToggle(toggleButton),drawerInstance.toggleVisibility()}const openDrawerButton=e.target.closest(SELECTORS_OPENBTN);if(openDrawerButton&&openDrawerButton.dataset.target){e.preventDefault();const targetDrawer=document.getElementById(openDrawerButton.dataset.target),drawerInstance=Drawers.getDrawerInstanceForNode(targetDrawer);setLastUsedToggle(toggleButton),drawerInstance.openDrawer()}const closeDrawerButton=e.target.closest(SELECTORS_CLOSEBTN);if(closeDrawerButton&&closeDrawerButton.dataset.target){e.preventDefault();const targetDrawer=document.getElementById(closeDrawerButton.dataset.target);Drawers.getDrawerInstanceForNode(targetDrawer).closeDrawer(),(target=>{const lastUsedButton=document.querySelector("".concat(SELECTORS_BUTTONS,'[data-target="').concat(target,'"][data-lastused="true"'));lastUsedButton&&lastUsedButton.focus()})(closeDrawerButton.dataset.target)}})),document.addEventListener(Drawers.eventTypes.drawerShow,(e=>{(0,_pagehelpers.isLarge)()||Drawers.closeOtherDrawers(e.detail.drawerInstance)}));const btnSelector="".concat(SELECTORS_TOGGLEBTN,", ").concat(SELECTORS_OPENBTN,", ").concat(SELECTORS_CLOSEBTN);document.addEventListener("focusout",(e=>{const button=e.target.closest(btnSelector);void 0!==(null==button?void 0:button.dataset.restoreTooltipOnBlur)&&enableButtonTooltip(button)}));document.addEventListener("scroll",(()=>{const currentFocus=document.activeElement,drawerContentElements=document.querySelectorAll(SELECTORS_DRAWERCONTENT);if(Array.from(drawerContentElements).some((drawer=>drawer.contains(currentFocus))))return;const body=document.querySelector("body");window.scrollY>=window.innerHeight?body.classList.add(CLASSES_SCROLLED):body.classList.remove(CLASSES_SCROLLED),Drawers.displaceDrawers(window.scrollX)}));const preventOverlap=(0,_utils.debounce)(Drawers.preventCoveringFocusedElement,100);document.addEventListener("focusin",preventOverlap),document.addEventListener("focusout",preventOverlap),window.addEventListener("resize",(0,_utils.debounce)((()=>{if((0,_pagehelpers.isSmall)()){let anyOpen=!1;drawerMap.forEach((drawerInstance=>{if(disableDrawerTooltips(drawerInstance.drawerNode),drawerInstance.isOpen){const currentFocus=document.activeElement,drawerContent=drawerInstance.drawerNode.querySelector(SELECTORS_DRAWERCONTENT);drawerInstance.closeOnResize&&(!drawerContent||!drawerContent.contains(currentFocus))?drawerInstance.closeDrawer():anyOpen=!0}})),anyOpen&&getBackdrop().then((backdrop=>backdrop.show())).catch()}else drawerMap.forEach((drawerInstance=>{var drawerNode;[(drawerNode=drawerInstance.drawerNode).querySelector(SELECTORS_CLOSEBTN),getDrawerOpenButton(drawerNode.id)].forEach((button=>{button&&enableButtonTooltip(button)}))})),getBackdrop().then((backdrop=>backdrop.hide())).catch()}),400,{pending:!0}))})();return document.querySelectorAll(SELECTORS_DRAWERS).forEach((drawerNode=>Drawers.getDrawerInstanceForNode(drawerNode))),_exports.default})); //# sourceMappingURL=drawers.min.js.map \ No newline at end of file diff --git a/theme/boost/amd/build/drawers.min.js.map b/theme/boost/amd/build/drawers.min.js.map index 8fa90d3168a76..51531a00c6104 100644 --- a/theme/boost/amd/build/drawers.min.js.map +++ b/theme/boost/amd/build/drawers.min.js.map @@ -1 +1 @@ -{"version":3,"file":"drawers.min.js","sources":["../src/drawers.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 * Toggling the visibility of the secondary navigation on mobile.\n *\n * @module theme_boost/drawers\n * @copyright 2021 Bas Brands\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport ModalBackdrop from 'core/modal_backdrop';\nimport Templates from 'core/templates';\nimport * as Aria from 'core/aria';\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport {debounce} from 'core/utils';\nimport {isSmall, isLarge} from 'core/pagehelpers';\nimport Pending from 'core/pending';\nimport {setUserPreference} from 'core_user/repository';\n// The jQuery module is only used for interacting with Boostrap 4. It can we removed when MDL-71979 is integrated.\nimport jQuery from 'jquery';\n\nlet backdropPromise = null;\n\nconst drawerMap = new Map();\n\nconst SELECTORS = {\n BUTTONS: '[data-toggler=\"drawers\"]',\n CLOSEBTN: '[data-toggler=\"drawers\"][data-action=\"closedrawer\"]',\n OPENBTN: '[data-toggler=\"drawers\"][data-action=\"opendrawer\"]',\n TOGGLEBTN: '[data-toggler=\"drawers\"][data-action=\"toggle\"]',\n DRAWERS: '[data-region=\"fixed-drawer\"]',\n DRAWERCONTENT: '.drawercontent',\n PAGECONTENT: '#page-content',\n HEADERCONTENT: '.drawerheadercontent',\n};\n\nconst CLASSES = {\n SCROLLED: 'scrolled',\n SHOW: 'show',\n NOTINITIALISED: 'not-initialized',\n};\n\n/**\n * Pixel thresshold to auto-hide drawers.\n *\n * @type {Number}\n */\nconst THRESHOLD = 20;\n\n/**\n * Try to get the drawer z-index from the page content.\n *\n * @returns {Number|null} the z-index of the drawer.\n * @private\n */\nconst getDrawerZIndex = () => {\n const drawer = document.querySelector(SELECTORS.DRAWERS);\n if (!drawer) {\n return null;\n }\n return parseInt(window.getComputedStyle(drawer).zIndex, 10);\n};\n\n/**\n * Add a backdrop to the page.\n *\n * @returns {Promise} rendering of modal backdrop.\n * @private\n */\nconst getBackdrop = () => {\n if (!backdropPromise) {\n backdropPromise = Templates.render('core/modal_backdrop', {})\n .then(html => new ModalBackdrop(html))\n .then(modalBackdrop => {\n const drawerZindex = getDrawerZIndex();\n if (drawerZindex) {\n modalBackdrop.setZIndex(getDrawerZIndex() - 1);\n }\n modalBackdrop.getAttachmentPoint().get(0).addEventListener('click', e => {\n e.preventDefault();\n Drawers.closeAllDrawers();\n });\n return modalBackdrop;\n })\n .catch();\n }\n return backdropPromise;\n};\n\n/**\n * Get the button element to open a specific drawer.\n *\n * @param {String} drawerId the drawer element Id\n * @return {HTMLElement|undefined} the open button element\n * @private\n */\nconst getDrawerOpenButton = (drawerId) => {\n let openButton = document.querySelector(`${SELECTORS.OPENBTN}[data-target=\"${drawerId}\"]`);\n if (!openButton) {\n openButton = document.querySelector(`${SELECTORS.TOGGLEBTN}[data-target=\"${drawerId}\"]`);\n }\n return openButton;\n};\n\n/**\n * Disable drawer tooltips.\n *\n * @param {HTMLElement} drawerNode the drawer main node\n * @private\n */\nconst disableDrawerTooltips = (drawerNode) => {\n const buttons = [\n drawerNode.querySelector(SELECTORS.CLOSEBTN),\n getDrawerOpenButton(drawerNode.id),\n ];\n buttons.forEach(button => {\n if (!button) {\n return;\n }\n disableButtonTooltip(button);\n });\n};\n\n/**\n * Disable the button tooltips.\n *\n * @param {HTMLElement} button the button element\n * @param {boolean} enableOnBlur if the tooltip must be re-enabled on blur.\n * @private\n */\nconst disableButtonTooltip = (button, enableOnBlur) => {\n if (button.hasAttribute('data-original-title')) {\n // The jQuery is still used in Boostrap 4. It can we removed when MDL-71979 is integrated.\n jQuery(button).tooltip('disable');\n button.setAttribute('title', button.dataset.originalTitle);\n } else {\n button.dataset.disabledToggle = button.dataset.toggle;\n button.removeAttribute('data-toggle');\n }\n if (enableOnBlur) {\n button.dataset.restoreTooltipOnBlur = true;\n }\n};\n\n/**\n * Enable drawer tooltips.\n *\n * @param {HTMLElement} drawerNode the drawer main node\n * @private\n */\nconst enableDrawerTooltips = (drawerNode) => {\n const buttons = [\n drawerNode.querySelector(SELECTORS.CLOSEBTN),\n getDrawerOpenButton(drawerNode.id),\n ];\n buttons.forEach(button => {\n if (!button) {\n return;\n }\n enableButtonTooltip(button);\n });\n};\n\n/**\n * Enable the button tooltips.\n *\n * @param {HTMLElement} button the button element\n * @private\n */\nconst enableButtonTooltip = (button) => {\n // The jQuery is still used in Boostrap 4. It can we removed when MDL-71979 is integrated.\n if (button.hasAttribute('data-original-title')) {\n jQuery(button).tooltip('enable');\n button.removeAttribute('title');\n } else if (button.dataset.disabledToggle) {\n button.dataset.toggle = button.dataset.disabledToggle;\n jQuery(button).tooltip();\n }\n delete button.dataset.restoreTooltipOnBlur;\n};\n\n/**\n * Add scroll listeners to a drawer element.\n *\n * @param {HTMLElement} drawerNode the drawer main node\n * @private\n */\nconst addInnerScrollListener = (drawerNode) => {\n const content = drawerNode.querySelector(SELECTORS.DRAWERCONTENT);\n if (!content) {\n return;\n }\n content.addEventListener(\"scroll\", () => {\n drawerNode.classList.toggle(\n CLASSES.SCROLLED,\n content.scrollTop != 0\n );\n });\n};\n\n/**\n * The Drawers class is used to control on-screen drawer elements.\n *\n * It handles opening, and closing of drawer elements, as well as more detailed behaviours such as closing a drawer when\n * another drawer is opened, and supports closing a drawer when the screen is resized.\n *\n * Drawers are instantiated on page load, and can also be toggled lazily when toggling any drawer toggle, open button,\n * or close button.\n *\n * A range of show and hide events are also dispatched as detailed in the class\n * {@link module:theme_boost/drawers#eventTypes eventTypes} object.\n *\n * @example Standard usage\n *\n * // The module just needs to be included to add drawer support.\n * import 'theme_boost/drawers';\n *\n * @example Manually open or close any drawer\n *\n * import Drawers from 'theme_boost/drawers';\n *\n * const myDrawer = Drawers.getDrawerInstanceForNode(document.querySelector('.myDrawerNode');\n * myDrawer.closeDrawer();\n *\n * @example Listen to the before show event and cancel it\n *\n * import Drawers from 'theme_boost/drawers';\n *\n * document.addEventListener(Drawers.eventTypes.drawerShow, e => {\n * // The drawer which will be shown.\n * window.console.log(e.target);\n *\n * // The instance of the Drawers class for this drawer.\n * window.console.log(e.detail.drawerInstance);\n *\n * // Prevent this drawer from being shown.\n * e.preventDefault();\n * });\n *\n * @example Listen to the shown event\n *\n * document.addEventListener(Drawers.eventTypes.drawerShown, e => {\n * // The drawer which was shown.\n * window.console.log(e.target);\n *\n * // The instance of the Drawers class for this drawer.\n * window.console.log(e.detail.drawerInstance);\n * });\n */\nexport default class Drawers {\n /**\n * The underlying HTMLElement which is controlled.\n */\n drawerNode = null;\n\n /**\n * The drawer page bounding box dimensions.\n * @var {DOMRect} boundingRect\n */\n boundingRect = null;\n\n constructor(drawerNode) {\n // Some behat tests may use fake drawer divs to test components in drawers.\n if (drawerNode.dataset.behatFakeDrawer !== undefined) {\n return;\n }\n\n this.drawerNode = drawerNode;\n\n if (isSmall()) {\n this.closeDrawer({focusOnOpenButton: false, updatePreferences: false});\n }\n\n if (this.drawerNode.classList.contains(CLASSES.SHOW)) {\n this.openDrawer({focusOnCloseButton: false, setUserPref: false});\n } else if (this.drawerNode.dataset.forceopen == 1) {\n if (!isSmall()) {\n this.openDrawer({focusOnCloseButton: false, setUserPref: false});\n }\n } else {\n Aria.hide(this.drawerNode);\n }\n\n // Disable tooltips in small screens.\n if (isSmall()) {\n disableDrawerTooltips(this.drawerNode);\n }\n\n addInnerScrollListener(this.drawerNode);\n\n drawerMap.set(drawerNode, this);\n\n drawerNode.classList.remove(CLASSES.NOTINITIALISED);\n }\n\n /**\n * Whether the drawer is open.\n *\n * @returns {boolean}\n */\n get isOpen() {\n return this.drawerNode.classList.contains(CLASSES.SHOW);\n }\n\n /**\n * Whether the drawer should close when the window is resized\n *\n * @returns {boolean}\n */\n get closeOnResize() {\n return !!parseInt(this.drawerNode.dataset.closeOnResize);\n }\n\n /**\n * The list of event types.\n *\n * @static\n * @property {String} drawerShow See {@link event:theme_boost/drawers:show}\n * @property {String} drawerShown See {@link event:theme_boost/drawers:shown}\n * @property {String} drawerHide See {@link event:theme_boost/drawers:hide}\n * @property {String} drawerHidden See {@link event:theme_boost/drawers:hidden}\n */\n static eventTypes = {\n /**\n * An event triggered before a drawer is shown.\n *\n * @event theme_boost/drawers:show\n * @type {CustomEvent}\n * @property {HTMLElement} target The drawer that will be opened.\n */\n drawerShow: 'theme_boost/drawers:show',\n\n /**\n * An event triggered after a drawer is shown.\n *\n * @event theme_boost/drawers:shown\n * @type {CustomEvent}\n * @property {HTMLElement} target The drawer that was be opened.\n */\n drawerShown: 'theme_boost/drawers:shown',\n\n /**\n * An event triggered before a drawer is hidden.\n *\n * @event theme_boost/drawers:hide\n * @type {CustomEvent}\n * @property {HTMLElement} target The drawer that will be hidden.\n */\n drawerHide: 'theme_boost/drawers:hide',\n\n /**\n * An event triggered after a drawer is hidden.\n *\n * @event theme_boost/drawers:hidden\n * @type {CustomEvent}\n * @property {HTMLElement} target The drawer that was be hidden.\n */\n drawerHidden: 'theme_boost/drawers:hidden',\n };\n\n\n /**\n * Get the drawer instance for the specified node\n *\n * @param {HTMLElement} drawerNode\n * @returns {module:theme_boost/drawers}\n */\n static getDrawerInstanceForNode(drawerNode) {\n if (!drawerMap.has(drawerNode)) {\n new Drawers(drawerNode);\n }\n\n return drawerMap.get(drawerNode);\n }\n\n /**\n * Dispatch a drawer event.\n *\n * @param {string} eventname the event name\n * @param {boolean} cancelable if the event is cancelable\n * @returns {CustomEvent} the resulting custom event\n */\n dispatchEvent(eventname, cancelable = false) {\n return dispatchEvent(\n eventname,\n {\n drawerInstance: this,\n },\n this.drawerNode,\n {\n cancelable,\n }\n );\n }\n\n /**\n * Open the drawer.\n *\n * By default, openDrawer sets the page focus to the close drawer button. However, when a drawer is open at page\n * load, this represents an accessibility problem as the initial focus changes without any user interaction. The\n * focusOnCloseButton parameter can be set to false to prevent this behaviour.\n *\n * @param {object} args\n * @param {boolean} [args.focusOnCloseButton=true] Whether to alter page focus when opening the drawer\n * @param {boolean} [args.setUserPref=true] Whether to store the opened drawer state as a user preference\n */\n openDrawer({focusOnCloseButton = true, setUserPref = true} = {}) {\n\n const pendingPromise = new Pending('theme_boost/drawers:open');\n const showEvent = this.dispatchEvent(Drawers.eventTypes.drawerShow, true);\n if (showEvent.defaultPrevented) {\n return;\n }\n\n // Hide close button and header content while the drawer is showing to prevent glitchy effects.\n this.drawerNode.querySelector(SELECTORS.CLOSEBTN)?.classList.toggle('hidden', true);\n this.drawerNode.querySelector(SELECTORS.HEADERCONTENT)?.classList.toggle('hidden', true);\n\n\n // Remove open tooltip if still visible.\n let openButton = getDrawerOpenButton(this.drawerNode.id);\n if (openButton && openButton.hasAttribute('data-original-title')) {\n // The jQuery is still used in Boostrap 4. It can we removed when MDL-71979 is integrated.\n jQuery(openButton)?.tooltip('hide');\n }\n\n Aria.unhide(this.drawerNode);\n this.drawerNode.classList.add(CLASSES.SHOW);\n\n const preference = this.drawerNode.dataset.preference;\n if (preference && !isSmall() && (this.drawerNode.dataset.forceopen != 1) && setUserPref) {\n setUserPreference(preference, true);\n }\n\n const state = this.drawerNode.dataset.state;\n if (state) {\n const page = document.getElementById('page');\n page.classList.add(state);\n }\n\n this.boundingRect = this.drawerNode.getBoundingClientRect();\n\n if (isSmall()) {\n getBackdrop().then(backdrop => {\n backdrop.show();\n\n const pageWrapper = document.getElementById('page');\n pageWrapper.style.overflow = 'hidden';\n return backdrop;\n })\n .catch();\n }\n\n // Show close button and header content once the drawer is fully opened.\n const closeButton = this.drawerNode.querySelector(SELECTORS.CLOSEBTN);\n const headerContent = this.drawerNode.querySelector(SELECTORS.HEADERCONTENT);\n if (focusOnCloseButton && closeButton) {\n disableButtonTooltip(closeButton, true);\n }\n setTimeout(() => {\n closeButton.classList.toggle('hidden', false);\n headerContent.classList.toggle('hidden', false);\n if (focusOnCloseButton) {\n closeButton.focus();\n }\n pendingPromise.resolve();\n }, 300);\n\n this.dispatchEvent(Drawers.eventTypes.drawerShown);\n }\n\n /**\n * Close the drawer.\n *\n * @param {object} args\n * @param {boolean} [args.focusOnOpenButton=true] Whether to alter page focus when opening the drawer\n * @param {boolean} [args.updatePreferences=true] Whether to update the user prewference\n */\n closeDrawer({focusOnOpenButton = true, updatePreferences = true} = {}) {\n\n const pendingPromise = new Pending('theme_boost/drawers:close');\n\n const hideEvent = this.dispatchEvent(Drawers.eventTypes.drawerHide, true);\n if (hideEvent.defaultPrevented) {\n return;\n }\n\n // Hide close button and header content while the drawer is hiding to prevent glitchy effects.\n const closeButton = this.drawerNode.querySelector(SELECTORS.CLOSEBTN);\n closeButton?.classList.toggle('hidden', true);\n const headerContent = this.drawerNode.querySelector(SELECTORS.HEADERCONTENT);\n headerContent?.classList.toggle('hidden', true);\n // Remove the close button tooltip if visible.\n if (closeButton.hasAttribute('data-original-title')) {\n // The jQuery is still used in Boostrap 4. It can we removed when MDL-71979 is integrated.\n jQuery(closeButton)?.tooltip('hide');\n }\n\n const preference = this.drawerNode.dataset.preference;\n if (preference && updatePreferences && !isSmall()) {\n setUserPreference(preference, false);\n }\n\n const state = this.drawerNode.dataset.state;\n if (state) {\n const page = document.getElementById('page');\n page.classList.remove(state);\n }\n\n Aria.hide(this.drawerNode);\n this.drawerNode.classList.remove(CLASSES.SHOW);\n\n getBackdrop().then(backdrop => {\n backdrop.hide();\n\n if (isSmall()) {\n const pageWrapper = document.getElementById('page');\n pageWrapper.style.overflow = 'visible';\n }\n return backdrop;\n })\n .catch();\n\n // Move focus to the open drawer (or toggler) button once the drawer is hidden.\n let openButton = getDrawerOpenButton(this.drawerNode.id);\n if (openButton) {\n disableButtonTooltip(openButton, true);\n }\n setTimeout(() => {\n if (openButton && focusOnOpenButton) {\n openButton.focus();\n }\n pendingPromise.resolve();\n }, 300);\n\n this.dispatchEvent(Drawers.eventTypes.drawerHidden);\n }\n\n /**\n * Toggle visibility of the drawer.\n */\n toggleVisibility() {\n if (this.drawerNode.classList.contains(CLASSES.SHOW)) {\n this.closeDrawer();\n } else {\n this.openDrawer();\n }\n }\n\n /**\n * Displaces the drawer outsite the page.\n *\n * @param {Number} scrollPosition the page current scroll position\n */\n displace(scrollPosition) {\n let displace = scrollPosition;\n let openButton = getDrawerOpenButton(this.drawerNode.id);\n if (scrollPosition === 0) {\n this.drawerNode.style.transform = '';\n if (openButton) {\n openButton.style.transform = '';\n }\n return;\n }\n const state = this.drawerNode.dataset?.state;\n const drawrWidth = this.drawerNode.offsetWidth;\n let scrollThreshold = drawrWidth;\n let direction = -1;\n if (state === 'show-drawer-right') {\n direction = 1;\n scrollThreshold = THRESHOLD;\n }\n // LTR scroll is positive while RTL scroll is negative.\n if (Math.abs(scrollPosition) > scrollThreshold) {\n displace = Math.sign(scrollPosition) * (drawrWidth + THRESHOLD);\n }\n displace *= direction;\n const transform = `translateX(${displace}px)`;\n if (openButton) {\n openButton.style.transform = transform;\n }\n this.drawerNode.style.transform = transform;\n }\n\n /**\n * Prevent drawer from overlapping an element.\n *\n * @param {HTMLElement} currentFocus\n */\n preventOverlap(currentFocus) {\n // Start position drawer (aka. left drawer) will never overlap with the page content.\n if (!this.isOpen || this.drawerNode.dataset?.state === 'show-drawer-left') {\n return;\n }\n const drawrWidth = this.drawerNode.offsetWidth;\n const element = currentFocus.getBoundingClientRect();\n\n // The this.boundingRect is calculated only once and it is reliable\n // for horizontal overlapping (which is the most common). However,\n // it is not reliable for vertical overlapping because the drawer\n // height can be changed by other elements like sticky footer.\n // To prevent recalculating the boundingRect on every\n // focusin event, we use horizontal overlapping as first fast check.\n let overlapping = (\n (element.right + THRESHOLD) > this.boundingRect.left &&\n (element.left - THRESHOLD) < this.boundingRect.right\n );\n if (overlapping) {\n const currentBoundingRect = this.drawerNode.getBoundingClientRect();\n overlapping = (\n (element.bottom) > currentBoundingRect.top &&\n (element.top) < currentBoundingRect.bottom\n );\n }\n\n if (overlapping) {\n // Force drawer to displace out of the page.\n let displaceOut = drawrWidth + 1;\n if (window.right_to_left()) {\n displaceOut *= -1;\n }\n this.displace(displaceOut);\n } else {\n // Reset drawer displacement.\n this.displace(window.scrollX);\n }\n }\n\n /**\n * Close all drawers.\n */\n static closeAllDrawers() {\n drawerMap.forEach(drawerInstance => {\n drawerInstance.closeDrawer();\n });\n }\n\n /**\n * Close all drawers except for the specified drawer.\n *\n * @param {module:theme_boost/drawers} comparisonInstance\n */\n static closeOtherDrawers(comparisonInstance) {\n drawerMap.forEach(drawerInstance => {\n if (drawerInstance === comparisonInstance) {\n return;\n }\n\n drawerInstance.closeDrawer();\n });\n }\n\n /**\n * Prevent drawers from covering the focused element.\n */\n static preventCoveringFocusedElement() {\n const currentFocus = document.activeElement;\n // Focus on page layout elements should be ignored.\n const pagecontent = document.querySelector(SELECTORS.PAGECONTENT);\n if (!currentFocus || !pagecontent?.contains(currentFocus)) {\n Drawers.displaceDrawers(window.scrollX);\n return;\n }\n drawerMap.forEach(drawerInstance => {\n drawerInstance.preventOverlap(currentFocus);\n });\n }\n\n /**\n * Prevent drawer from covering the content when the page content covers the full page.\n *\n * @param {Number} displace\n */\n static displaceDrawers(displace) {\n drawerMap.forEach(drawerInstance => {\n drawerInstance.displace(displace);\n });\n }\n}\n\n/**\n * Set the last used attribute for the last used toggle button for a drawer.\n *\n * @param {object} toggleButton The clicked button.\n */\nconst setLastUsedToggle = (toggleButton) => {\n if (toggleButton.dataset.target) {\n document.querySelectorAll(`${SELECTORS.BUTTONS}[data-target=\"${toggleButton.dataset.target}\"]`)\n .forEach(btn => {\n btn.dataset.lastused = false;\n });\n toggleButton.dataset.lastused = true;\n }\n};\n\n/**\n * Set the focus to the last used button to open this drawer.\n * @param {string} target The drawer target.\n */\nconst focusLastUsedToggle = (target) => {\n const lastUsedButton = document.querySelector(`${SELECTORS.BUTTONS}[data-target=\"${target}\"][data-lastused=\"true\"`);\n if (lastUsedButton) {\n lastUsedButton.focus();\n }\n};\n\n/**\n * Register the event listeners for the drawer.\n *\n * @private\n */\nconst registerListeners = () => {\n // Listen for show/hide events.\n document.addEventListener('click', e => {\n const toggleButton = e.target.closest(SELECTORS.TOGGLEBTN);\n if (toggleButton && toggleButton.dataset.target) {\n e.preventDefault();\n const targetDrawer = document.getElementById(toggleButton.dataset.target);\n const drawerInstance = Drawers.getDrawerInstanceForNode(targetDrawer);\n setLastUsedToggle(toggleButton);\n\n drawerInstance.toggleVisibility();\n }\n\n const openDrawerButton = e.target.closest(SELECTORS.OPENBTN);\n if (openDrawerButton && openDrawerButton.dataset.target) {\n e.preventDefault();\n const targetDrawer = document.getElementById(openDrawerButton.dataset.target);\n const drawerInstance = Drawers.getDrawerInstanceForNode(targetDrawer);\n setLastUsedToggle(toggleButton);\n\n drawerInstance.openDrawer();\n }\n\n const closeDrawerButton = e.target.closest(SELECTORS.CLOSEBTN);\n if (closeDrawerButton && closeDrawerButton.dataset.target) {\n e.preventDefault();\n const targetDrawer = document.getElementById(closeDrawerButton.dataset.target);\n const drawerInstance = Drawers.getDrawerInstanceForNode(targetDrawer);\n\n drawerInstance.closeDrawer();\n focusLastUsedToggle(closeDrawerButton.dataset.target);\n }\n });\n\n // Close drawer when another drawer opens.\n document.addEventListener(Drawers.eventTypes.drawerShow, e => {\n if (isLarge()) {\n return;\n }\n Drawers.closeOtherDrawers(e.detail.drawerInstance);\n });\n\n // Tooglers and openers blur listeners.\n const btnSelector = `${SELECTORS.TOGGLEBTN}, ${SELECTORS.OPENBTN}, ${SELECTORS.CLOSEBTN}`;\n document.addEventListener('focusout', (e) => {\n const button = e.target.closest(btnSelector);\n if (button?.dataset.restoreTooltipOnBlur !== undefined) {\n enableButtonTooltip(button);\n }\n });\n\n const closeOnResizeListener = () => {\n if (isSmall()) {\n let anyOpen = false;\n drawerMap.forEach(drawerInstance => {\n disableDrawerTooltips(drawerInstance.drawerNode);\n if (drawerInstance.isOpen) {\n const currentFocus = document.activeElement;\n const drawerContent = drawerInstance.drawerNode.querySelector(SELECTORS.DRAWERCONTENT);\n const shouldClose = drawerInstance.closeOnResize && (!drawerContent || !drawerContent.contains(currentFocus));\n if (shouldClose) {\n drawerInstance.closeDrawer();\n } else {\n anyOpen = true;\n }\n }\n });\n\n if (anyOpen) {\n getBackdrop().then(backdrop => backdrop.show()).catch();\n }\n } else {\n drawerMap.forEach(drawerInstance => {\n enableDrawerTooltips(drawerInstance.drawerNode);\n });\n getBackdrop().then(backdrop => backdrop.hide()).catch();\n }\n };\n\n document.addEventListener('scroll', () => {\n const currentFocus = document.activeElement;\n const drawerContentElements = document.querySelectorAll(SELECTORS.DRAWERCONTENT);\n // Check if the current focus is within any drawer content.\n if (Array.from(drawerContentElements).some(drawer => drawer.contains(currentFocus))) {\n return;\n }\n const body = document.querySelector('body');\n if (window.scrollY >= window.innerHeight) {\n body.classList.add(CLASSES.SCROLLED);\n } else {\n body.classList.remove(CLASSES.SCROLLED);\n }\n // Horizontal scroll listener to displace the drawers to prevent covering\n // any possible sticky content.\n Drawers.displaceDrawers(window.scrollX);\n });\n\n const preventOverlap = debounce(Drawers.preventCoveringFocusedElement, 100);\n document.addEventListener('focusin', preventOverlap);\n document.addEventListener('focusout', preventOverlap);\n\n window.addEventListener('resize', debounce(closeOnResizeListener, 400, {pending: true}));\n};\n\nregisterListeners();\n\nconst drawers = document.querySelectorAll(SELECTORS.DRAWERS);\ndrawers.forEach(drawerNode => Drawers.getDrawerInstanceForNode(drawerNode));\n"],"names":["backdropPromise","drawerMap","Map","SELECTORS","CLASSES","getDrawerZIndex","drawer","document","querySelector","parseInt","window","getComputedStyle","zIndex","getBackdrop","Templates","render","then","html","ModalBackdrop","modalBackdrop","setZIndex","getAttachmentPoint","get","addEventListener","e","preventDefault","Drawers","closeAllDrawers","catch","getDrawerOpenButton","drawerId","openButton","disableDrawerTooltips","drawerNode","id","forEach","button","disableButtonTooltip","enableOnBlur","hasAttribute","tooltip","setAttribute","dataset","originalTitle","disabledToggle","toggle","removeAttribute","restoreTooltipOnBlur","enableButtonTooltip","constructor","undefined","behatFakeDrawer","closeDrawer","focusOnOpenButton","updatePreferences","this","classList","contains","openDrawer","focusOnCloseButton","setUserPref","forceopen","Aria","hide","content","scrollTop","addInnerScrollListener","set","remove","isOpen","closeOnResize","has","dispatchEvent","eventname","cancelable","drawerInstance","pendingPromise","Pending","eventTypes","drawerShow","defaultPrevented","unhide","add","preference","state","getElementById","boundingRect","getBoundingClientRect","backdrop","show","style","overflow","closeButton","headerContent","setTimeout","focus","resolve","drawerShown","drawerHide","drawerHidden","toggleVisibility","displace","scrollPosition","transform","_this$drawerNode$data","drawrWidth","offsetWidth","scrollThreshold","direction","Math","abs","sign","preventOverlap","currentFocus","element","overlapping","right","left","currentBoundingRect","bottom","top","displaceOut","right_to_left","scrollX","comparisonInstance","activeElement","pagecontent","displaceDrawers","setLastUsedToggle","toggleButton","target","querySelectorAll","btn","lastused","closest","targetDrawer","getDrawerInstanceForNode","openDrawerButton","closeDrawerButton","lastUsedButton","focusLastUsedToggle","closeOtherDrawers","detail","btnSelector","drawerContentElements","Array","from","some","body","scrollY","innerHeight","preventCoveringFocusedElement","anyOpen","drawerContent","pending","registerListeners"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"drawers.min.js","sources":["../src/drawers.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 * Toggling the visibility of the secondary navigation on mobile.\n *\n * @module theme_boost/drawers\n * @copyright 2021 Bas Brands\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport ModalBackdrop from 'core/modal_backdrop';\nimport Templates from 'core/templates';\nimport * as Aria from 'core/aria';\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport {debounce} from 'core/utils';\nimport {isSmall, isLarge} from 'core/pagehelpers';\nimport Pending from 'core/pending';\nimport {setUserPreference} from 'core_user/repository';\nimport Tooltip from './bootstrap/tooltip';\n\nlet backdropPromise = null;\n\nconst drawerMap = new Map();\n\nconst SELECTORS = {\n BUTTONS: '[data-toggler=\"drawers\"]',\n CLOSEBTN: '[data-toggler=\"drawers\"][data-action=\"closedrawer\"]',\n OPENBTN: '[data-toggler=\"drawers\"][data-action=\"opendrawer\"]',\n TOGGLEBTN: '[data-toggler=\"drawers\"][data-action=\"toggle\"]',\n DRAWERS: '[data-region=\"fixed-drawer\"]',\n DRAWERCONTENT: '.drawercontent',\n PAGECONTENT: '#page-content',\n HEADERCONTENT: '.drawerheadercontent',\n};\n\nconst CLASSES = {\n SCROLLED: 'scrolled',\n SHOW: 'show',\n NOTINITIALISED: 'not-initialized',\n};\n\n/**\n * Pixel thresshold to auto-hide drawers.\n *\n * @type {Number}\n */\nconst THRESHOLD = 20;\n\n/**\n * Try to get the drawer z-index from the page content.\n *\n * @returns {Number|null} the z-index of the drawer.\n * @private\n */\nconst getDrawerZIndex = () => {\n const drawer = document.querySelector(SELECTORS.DRAWERS);\n if (!drawer) {\n return null;\n }\n return parseInt(window.getComputedStyle(drawer).zIndex, 10);\n};\n\n/**\n * Add a backdrop to the page.\n *\n * @returns {Promise} rendering of modal backdrop.\n * @private\n */\nconst getBackdrop = () => {\n if (!backdropPromise) {\n backdropPromise = Templates.render('core/modal_backdrop', {})\n .then(html => new ModalBackdrop(html))\n .then(modalBackdrop => {\n const drawerZindex = getDrawerZIndex();\n if (drawerZindex) {\n modalBackdrop.setZIndex(getDrawerZIndex() - 1);\n }\n modalBackdrop.getAttachmentPoint().get(0).addEventListener('click', e => {\n e.preventDefault();\n Drawers.closeAllDrawers();\n });\n return modalBackdrop;\n })\n .catch();\n }\n return backdropPromise;\n};\n\n/**\n * Get the button element to open a specific drawer.\n *\n * @param {String} drawerId the drawer element Id\n * @return {HTMLElement|undefined} the open button element\n * @private\n */\nconst getDrawerOpenButton = (drawerId) => {\n let openButton = document.querySelector(`${SELECTORS.OPENBTN}[data-target=\"${drawerId}\"]`);\n if (!openButton) {\n openButton = document.querySelector(`${SELECTORS.TOGGLEBTN}[data-target=\"${drawerId}\"]`);\n }\n return openButton;\n};\n\n/**\n * Disable drawer tooltips.\n *\n * @param {HTMLElement} drawerNode the drawer main node\n * @private\n */\nconst disableDrawerTooltips = (drawerNode) => {\n const buttons = [\n drawerNode.querySelector(SELECTORS.CLOSEBTN),\n getDrawerOpenButton(drawerNode.id),\n ];\n buttons.forEach(button => {\n if (!button) {\n return;\n }\n disableButtonTooltip(button);\n });\n};\n\n/**\n * Disable the button tooltips.\n *\n * @param {HTMLElement} button the button element\n * @param {boolean} enableOnBlur if the tooltip must be re-enabled on blur.\n * @private\n */\nconst disableButtonTooltip = (button, enableOnBlur) => {\n if (button.hasAttribute('data-original-title')) {\n Tooltip.getInstance(button).disable();\n button.setAttribute('title', button.dataset.originalTitle);\n } else {\n button.dataset.disabledToggle = button.dataset.toggle;\n button.removeAttribute('data-bs-toggle');\n }\n if (enableOnBlur) {\n button.dataset.restoreTooltipOnBlur = true;\n }\n};\n\n/**\n * Enable drawer tooltips.\n *\n * @param {HTMLElement} drawerNode the drawer main node\n * @private\n */\nconst enableDrawerTooltips = (drawerNode) => {\n const buttons = [\n drawerNode.querySelector(SELECTORS.CLOSEBTN),\n getDrawerOpenButton(drawerNode.id),\n ];\n buttons.forEach(button => {\n if (!button) {\n return;\n }\n enableButtonTooltip(button);\n });\n};\n\n/**\n * Enable the button tooltips.\n *\n * @param {HTMLElement} button the button element\n * @private\n */\nconst enableButtonTooltip = (button) => {\n if (button.hasAttribute('data-bs-original-title')) {\n Tooltip.getInstance(button).enable();\n button.removeAttribute('title');\n } else if (button.dataset.disabledToggle) {\n button.dataset.toggle = button.dataset.disabledToggle;\n new Tooltip(button);\n }\n delete button.dataset.restoreTooltipOnBlur;\n};\n\n/**\n * Add scroll listeners to a drawer element.\n *\n * @param {HTMLElement} drawerNode the drawer main node\n * @private\n */\nconst addInnerScrollListener = (drawerNode) => {\n const content = drawerNode.querySelector(SELECTORS.DRAWERCONTENT);\n if (!content) {\n return;\n }\n content.addEventListener(\"scroll\", () => {\n drawerNode.classList.toggle(\n CLASSES.SCROLLED,\n content.scrollTop != 0\n );\n });\n};\n\n/**\n * The Drawers class is used to control on-screen drawer elements.\n *\n * It handles opening, and closing of drawer elements, as well as more detailed behaviours such as closing a drawer when\n * another drawer is opened, and supports closing a drawer when the screen is resized.\n *\n * Drawers are instantiated on page load, and can also be toggled lazily when toggling any drawer toggle, open button,\n * or close button.\n *\n * A range of show and hide events are also dispatched as detailed in the class\n * {@link module:theme_boost/drawers#eventTypes eventTypes} object.\n *\n * @example Standard usage\n *\n * // The module just needs to be included to add drawer support.\n * import 'theme_boost/drawers';\n *\n * @example Manually open or close any drawer\n *\n * import Drawers from 'theme_boost/drawers';\n *\n * const myDrawer = Drawers.getDrawerInstanceForNode(document.querySelector('.myDrawerNode');\n * myDrawer.closeDrawer();\n *\n * @example Listen to the before show event and cancel it\n *\n * import Drawers from 'theme_boost/drawers';\n *\n * document.addEventListener(Drawers.eventTypes.drawerShow, e => {\n * // The drawer which will be shown.\n * window.console.log(e.target);\n *\n * // The instance of the Drawers class for this drawer.\n * window.console.log(e.detail.drawerInstance);\n *\n * // Prevent this drawer from being shown.\n * e.preventDefault();\n * });\n *\n * @example Listen to the shown event\n *\n * document.addEventListener(Drawers.eventTypes.drawerShown, e => {\n * // The drawer which was shown.\n * window.console.log(e.target);\n *\n * // The instance of the Drawers class for this drawer.\n * window.console.log(e.detail.drawerInstance);\n * });\n */\nexport default class Drawers {\n /**\n * The underlying HTMLElement which is controlled.\n */\n drawerNode = null;\n\n /**\n * The drawer page bounding box dimensions.\n * @var {DOMRect} boundingRect\n */\n boundingRect = null;\n\n constructor(drawerNode) {\n // Some behat tests may use fake drawer divs to test components in drawers.\n if (drawerNode.dataset.behatFakeDrawer !== undefined) {\n return;\n }\n\n this.drawerNode = drawerNode;\n\n if (isSmall()) {\n this.closeDrawer({focusOnOpenButton: false, updatePreferences: false});\n }\n\n if (this.drawerNode.classList.contains(CLASSES.SHOW)) {\n this.openDrawer({focusOnCloseButton: false, setUserPref: false});\n } else if (this.drawerNode.dataset.forceopen == 1) {\n if (!isSmall()) {\n this.openDrawer({focusOnCloseButton: false, setUserPref: false});\n }\n } else {\n Aria.hide(this.drawerNode);\n }\n\n // Disable tooltips in small screens.\n if (isSmall()) {\n disableDrawerTooltips(this.drawerNode);\n }\n\n addInnerScrollListener(this.drawerNode);\n\n drawerMap.set(drawerNode, this);\n\n drawerNode.classList.remove(CLASSES.NOTINITIALISED);\n }\n\n /**\n * Whether the drawer is open.\n *\n * @returns {boolean}\n */\n get isOpen() {\n return this.drawerNode.classList.contains(CLASSES.SHOW);\n }\n\n /**\n * Whether the drawer should close when the window is resized\n *\n * @returns {boolean}\n */\n get closeOnResize() {\n return !!parseInt(this.drawerNode.dataset.closeOnResize);\n }\n\n /**\n * The list of event types.\n *\n * @static\n * @property {String} drawerShow See {@link event:theme_boost/drawers:show}\n * @property {String} drawerShown See {@link event:theme_boost/drawers:shown}\n * @property {String} drawerHide See {@link event:theme_boost/drawers:hide}\n * @property {String} drawerHidden See {@link event:theme_boost/drawers:hidden}\n */\n static eventTypes = {\n /**\n * An event triggered before a drawer is shown.\n *\n * @event theme_boost/drawers:show\n * @type {CustomEvent}\n * @property {HTMLElement} target The drawer that will be opened.\n */\n drawerShow: 'theme_boost/drawers:show',\n\n /**\n * An event triggered after a drawer is shown.\n *\n * @event theme_boost/drawers:shown\n * @type {CustomEvent}\n * @property {HTMLElement} target The drawer that was be opened.\n */\n drawerShown: 'theme_boost/drawers:shown',\n\n /**\n * An event triggered before a drawer is hidden.\n *\n * @event theme_boost/drawers:hide\n * @type {CustomEvent}\n * @property {HTMLElement} target The drawer that will be hidden.\n */\n drawerHide: 'theme_boost/drawers:hide',\n\n /**\n * An event triggered after a drawer is hidden.\n *\n * @event theme_boost/drawers:hidden\n * @type {CustomEvent}\n * @property {HTMLElement} target The drawer that was be hidden.\n */\n drawerHidden: 'theme_boost/drawers:hidden',\n };\n\n\n /**\n * Get the drawer instance for the specified node\n *\n * @param {HTMLElement} drawerNode\n * @returns {module:theme_boost/drawers}\n */\n static getDrawerInstanceForNode(drawerNode) {\n if (!drawerMap.has(drawerNode)) {\n new Drawers(drawerNode);\n }\n\n return drawerMap.get(drawerNode);\n }\n\n /**\n * Dispatch a drawer event.\n *\n * @param {string} eventname the event name\n * @param {boolean} cancelable if the event is cancelable\n * @returns {CustomEvent} the resulting custom event\n */\n dispatchEvent(eventname, cancelable = false) {\n return dispatchEvent(\n eventname,\n {\n drawerInstance: this,\n },\n this.drawerNode,\n {\n cancelable,\n }\n );\n }\n\n /**\n * Open the drawer.\n *\n * By default, openDrawer sets the page focus to the close drawer button. However, when a drawer is open at page\n * load, this represents an accessibility problem as the initial focus changes without any user interaction. The\n * focusOnCloseButton parameter can be set to false to prevent this behaviour.\n *\n * @param {object} args\n * @param {boolean} [args.focusOnCloseButton=true] Whether to alter page focus when opening the drawer\n * @param {boolean} [args.setUserPref=true] Whether to store the opened drawer state as a user preference\n */\n openDrawer({focusOnCloseButton = true, setUserPref = true} = {}) {\n\n const pendingPromise = new Pending('theme_boost/drawers:open');\n const showEvent = this.dispatchEvent(Drawers.eventTypes.drawerShow, true);\n if (showEvent.defaultPrevented) {\n return;\n }\n\n // Hide close button and header content while the drawer is showing to prevent glitchy effects.\n this.drawerNode.querySelector(SELECTORS.CLOSEBTN)?.classList.toggle('hidden', true);\n this.drawerNode.querySelector(SELECTORS.HEADERCONTENT)?.classList.toggle('hidden', true);\n\n\n // Remove open tooltip if still visible.\n let openButton = getDrawerOpenButton(this.drawerNode.id);\n if (openButton && openButton.hasAttribute('data-original-title')) {\n Tooltip.getInstance(openButton)?.hide();\n }\n\n Aria.unhide(this.drawerNode);\n this.drawerNode.classList.add(CLASSES.SHOW);\n\n const preference = this.drawerNode.dataset.preference;\n if (preference && !isSmall() && (this.drawerNode.dataset.forceopen != 1) && setUserPref) {\n setUserPreference(preference, true);\n }\n\n const state = this.drawerNode.dataset.state;\n if (state) {\n const page = document.getElementById('page');\n page.classList.add(state);\n }\n\n this.boundingRect = this.drawerNode.getBoundingClientRect();\n\n if (isSmall()) {\n getBackdrop().then(backdrop => {\n backdrop.show();\n\n const pageWrapper = document.getElementById('page');\n pageWrapper.style.overflow = 'hidden';\n return backdrop;\n })\n .catch();\n }\n\n // Show close button and header content once the drawer is fully opened.\n const closeButton = this.drawerNode.querySelector(SELECTORS.CLOSEBTN);\n const headerContent = this.drawerNode.querySelector(SELECTORS.HEADERCONTENT);\n if (focusOnCloseButton && closeButton) {\n disableButtonTooltip(closeButton, true);\n }\n setTimeout(() => {\n closeButton.classList.toggle('hidden', false);\n headerContent.classList.toggle('hidden', false);\n if (focusOnCloseButton) {\n closeButton.focus();\n }\n pendingPromise.resolve();\n }, 300);\n\n this.dispatchEvent(Drawers.eventTypes.drawerShown);\n }\n\n /**\n * Close the drawer.\n *\n * @param {object} args\n * @param {boolean} [args.focusOnOpenButton=true] Whether to alter page focus when opening the drawer\n * @param {boolean} [args.updatePreferences=true] Whether to update the user prewference\n */\n closeDrawer({focusOnOpenButton = true, updatePreferences = true} = {}) {\n\n const pendingPromise = new Pending('theme_boost/drawers:close');\n\n const hideEvent = this.dispatchEvent(Drawers.eventTypes.drawerHide, true);\n if (hideEvent.defaultPrevented) {\n return;\n }\n\n // Hide close button and header content while the drawer is hiding to prevent glitchy effects.\n const closeButton = this.drawerNode.querySelector(SELECTORS.CLOSEBTN);\n closeButton?.classList.toggle('hidden', true);\n const headerContent = this.drawerNode.querySelector(SELECTORS.HEADERCONTENT);\n headerContent?.classList.toggle('hidden', true);\n // Remove the close button tooltip if visible.\n if (closeButton.hasAttribute('data-original-title')) {\n Tooltip.getInstance(closeButton)?.hide();\n }\n\n const preference = this.drawerNode.dataset.preference;\n if (preference && updatePreferences && !isSmall()) {\n setUserPreference(preference, false);\n }\n\n const state = this.drawerNode.dataset.state;\n if (state) {\n const page = document.getElementById('page');\n page.classList.remove(state);\n }\n\n Aria.hide(this.drawerNode);\n this.drawerNode.classList.remove(CLASSES.SHOW);\n\n getBackdrop().then(backdrop => {\n backdrop.hide();\n\n if (isSmall()) {\n const pageWrapper = document.getElementById('page');\n pageWrapper.style.overflow = 'visible';\n }\n return backdrop;\n })\n .catch();\n\n // Move focus to the open drawer (or toggler) button once the drawer is hidden.\n let openButton = getDrawerOpenButton(this.drawerNode.id);\n if (openButton) {\n disableButtonTooltip(openButton, true);\n }\n setTimeout(() => {\n if (openButton && focusOnOpenButton) {\n openButton.focus();\n }\n pendingPromise.resolve();\n }, 300);\n\n this.dispatchEvent(Drawers.eventTypes.drawerHidden);\n }\n\n /**\n * Toggle visibility of the drawer.\n */\n toggleVisibility() {\n if (this.drawerNode.classList.contains(CLASSES.SHOW)) {\n this.closeDrawer();\n } else {\n this.openDrawer();\n }\n }\n\n /**\n * Displaces the drawer outsite the page.\n *\n * @param {Number} scrollPosition the page current scroll position\n */\n displace(scrollPosition) {\n let displace = scrollPosition;\n let openButton = getDrawerOpenButton(this.drawerNode.id);\n if (scrollPosition === 0) {\n this.drawerNode.style.transform = '';\n if (openButton) {\n openButton.style.transform = '';\n }\n return;\n }\n const state = this.drawerNode.dataset?.state;\n const drawrWidth = this.drawerNode.offsetWidth;\n let scrollThreshold = drawrWidth;\n let direction = -1;\n if (state === 'show-drawer-right') {\n direction = 1;\n scrollThreshold = THRESHOLD;\n }\n // LTR scroll is positive while RTL scroll is negative.\n if (Math.abs(scrollPosition) > scrollThreshold) {\n displace = Math.sign(scrollPosition) * (drawrWidth + THRESHOLD);\n }\n displace *= direction;\n const transform = `translateX(${displace}px)`;\n if (openButton) {\n openButton.style.transform = transform;\n }\n this.drawerNode.style.transform = transform;\n }\n\n /**\n * Prevent drawer from overlapping an element.\n *\n * @param {HTMLElement} currentFocus\n */\n preventOverlap(currentFocus) {\n // Start position drawer (aka. left drawer) will never overlap with the page content.\n if (!this.isOpen || this.drawerNode.dataset?.state === 'show-drawer-left') {\n return;\n }\n const drawrWidth = this.drawerNode.offsetWidth;\n const element = currentFocus.getBoundingClientRect();\n\n // The this.boundingRect is calculated only once and it is reliable\n // for horizontal overlapping (which is the most common). However,\n // it is not reliable for vertical overlapping because the drawer\n // height can be changed by other elements like sticky footer.\n // To prevent recalculating the boundingRect on every\n // focusin event, we use horizontal overlapping as first fast check.\n let overlapping = (\n (element.right + THRESHOLD) > this.boundingRect.left &&\n (element.left - THRESHOLD) < this.boundingRect.right\n );\n if (overlapping) {\n const currentBoundingRect = this.drawerNode.getBoundingClientRect();\n overlapping = (\n (element.bottom) > currentBoundingRect.top &&\n (element.top) < currentBoundingRect.bottom\n );\n }\n\n if (overlapping) {\n // Force drawer to displace out of the page.\n let displaceOut = drawrWidth + 1;\n if (window.right_to_left()) {\n displaceOut *= -1;\n }\n this.displace(displaceOut);\n } else {\n // Reset drawer displacement.\n this.displace(window.scrollX);\n }\n }\n\n /**\n * Close all drawers.\n */\n static closeAllDrawers() {\n drawerMap.forEach(drawerInstance => {\n drawerInstance.closeDrawer();\n });\n }\n\n /**\n * Close all drawers except for the specified drawer.\n *\n * @param {module:theme_boost/drawers} comparisonInstance\n */\n static closeOtherDrawers(comparisonInstance) {\n drawerMap.forEach(drawerInstance => {\n if (drawerInstance === comparisonInstance) {\n return;\n }\n\n drawerInstance.closeDrawer();\n });\n }\n\n /**\n * Prevent drawers from covering the focused element.\n */\n static preventCoveringFocusedElement() {\n const currentFocus = document.activeElement;\n // Focus on page layout elements should be ignored.\n const pagecontent = document.querySelector(SELECTORS.PAGECONTENT);\n if (!currentFocus || !pagecontent?.contains(currentFocus)) {\n Drawers.displaceDrawers(window.scrollX);\n return;\n }\n drawerMap.forEach(drawerInstance => {\n drawerInstance.preventOverlap(currentFocus);\n });\n }\n\n /**\n * Prevent drawer from covering the content when the page content covers the full page.\n *\n * @param {Number} displace\n */\n static displaceDrawers(displace) {\n drawerMap.forEach(drawerInstance => {\n drawerInstance.displace(displace);\n });\n }\n}\n\n/**\n * Set the last used attribute for the last used toggle button for a drawer.\n *\n * @param {object} toggleButton The clicked button.\n */\nconst setLastUsedToggle = (toggleButton) => {\n if (toggleButton.dataset.target) {\n document.querySelectorAll(`${SELECTORS.BUTTONS}[data-target=\"${toggleButton.dataset.target}\"]`)\n .forEach(btn => {\n btn.dataset.lastused = false;\n });\n toggleButton.dataset.lastused = true;\n }\n};\n\n/**\n * Set the focus to the last used button to open this drawer.\n * @param {string} target The drawer target.\n */\nconst focusLastUsedToggle = (target) => {\n const lastUsedButton = document.querySelector(`${SELECTORS.BUTTONS}[data-target=\"${target}\"][data-lastused=\"true\"`);\n if (lastUsedButton) {\n lastUsedButton.focus();\n }\n};\n\n/**\n * Register the event listeners for the drawer.\n *\n * @private\n */\nconst registerListeners = () => {\n // Listen for show/hide events.\n document.addEventListener('click', e => {\n const toggleButton = e.target.closest(SELECTORS.TOGGLEBTN);\n if (toggleButton && toggleButton.dataset.target) {\n e.preventDefault();\n const targetDrawer = document.getElementById(toggleButton.dataset.target);\n const drawerInstance = Drawers.getDrawerInstanceForNode(targetDrawer);\n setLastUsedToggle(toggleButton);\n\n drawerInstance.toggleVisibility();\n }\n\n const openDrawerButton = e.target.closest(SELECTORS.OPENBTN);\n if (openDrawerButton && openDrawerButton.dataset.target) {\n e.preventDefault();\n const targetDrawer = document.getElementById(openDrawerButton.dataset.target);\n const drawerInstance = Drawers.getDrawerInstanceForNode(targetDrawer);\n setLastUsedToggle(toggleButton);\n\n drawerInstance.openDrawer();\n }\n\n const closeDrawerButton = e.target.closest(SELECTORS.CLOSEBTN);\n if (closeDrawerButton && closeDrawerButton.dataset.target) {\n e.preventDefault();\n const targetDrawer = document.getElementById(closeDrawerButton.dataset.target);\n const drawerInstance = Drawers.getDrawerInstanceForNode(targetDrawer);\n\n drawerInstance.closeDrawer();\n focusLastUsedToggle(closeDrawerButton.dataset.target);\n }\n });\n\n // Close drawer when another drawer opens.\n document.addEventListener(Drawers.eventTypes.drawerShow, e => {\n if (isLarge()) {\n return;\n }\n Drawers.closeOtherDrawers(e.detail.drawerInstance);\n });\n\n // Tooglers and openers blur listeners.\n const btnSelector = `${SELECTORS.TOGGLEBTN}, ${SELECTORS.OPENBTN}, ${SELECTORS.CLOSEBTN}`;\n document.addEventListener('focusout', (e) => {\n const button = e.target.closest(btnSelector);\n if (button?.dataset.restoreTooltipOnBlur !== undefined) {\n enableButtonTooltip(button);\n }\n });\n\n const closeOnResizeListener = () => {\n if (isSmall()) {\n let anyOpen = false;\n drawerMap.forEach(drawerInstance => {\n disableDrawerTooltips(drawerInstance.drawerNode);\n if (drawerInstance.isOpen) {\n const currentFocus = document.activeElement;\n const drawerContent = drawerInstance.drawerNode.querySelector(SELECTORS.DRAWERCONTENT);\n const shouldClose = drawerInstance.closeOnResize && (!drawerContent || !drawerContent.contains(currentFocus));\n if (shouldClose) {\n drawerInstance.closeDrawer();\n } else {\n anyOpen = true;\n }\n }\n });\n\n if (anyOpen) {\n getBackdrop().then(backdrop => backdrop.show()).catch();\n }\n } else {\n drawerMap.forEach(drawerInstance => {\n enableDrawerTooltips(drawerInstance.drawerNode);\n });\n getBackdrop().then(backdrop => backdrop.hide()).catch();\n }\n };\n\n document.addEventListener('scroll', () => {\n const currentFocus = document.activeElement;\n const drawerContentElements = document.querySelectorAll(SELECTORS.DRAWERCONTENT);\n // Check if the current focus is within any drawer content.\n if (Array.from(drawerContentElements).some(drawer => drawer.contains(currentFocus))) {\n return;\n }\n const body = document.querySelector('body');\n if (window.scrollY >= window.innerHeight) {\n body.classList.add(CLASSES.SCROLLED);\n } else {\n body.classList.remove(CLASSES.SCROLLED);\n }\n // Horizontal scroll listener to displace the drawers to prevent covering\n // any possible sticky content.\n Drawers.displaceDrawers(window.scrollX);\n });\n\n const preventOverlap = debounce(Drawers.preventCoveringFocusedElement, 100);\n document.addEventListener('focusin', preventOverlap);\n document.addEventListener('focusout', preventOverlap);\n\n window.addEventListener('resize', debounce(closeOnResizeListener, 400, {pending: true}));\n};\n\nregisterListeners();\n\nconst drawers = document.querySelectorAll(SELECTORS.DRAWERS);\ndrawers.forEach(drawerNode => Drawers.getDrawerInstanceForNode(drawerNode));\n"],"names":["backdropPromise","drawerMap","Map","SELECTORS","CLASSES","getDrawerZIndex","drawer","document","querySelector","parseInt","window","getComputedStyle","zIndex","getBackdrop","Templates","render","then","html","ModalBackdrop","modalBackdrop","setZIndex","getAttachmentPoint","get","addEventListener","e","preventDefault","Drawers","closeAllDrawers","catch","getDrawerOpenButton","drawerId","openButton","disableDrawerTooltips","drawerNode","id","forEach","button","disableButtonTooltip","enableOnBlur","hasAttribute","getInstance","disable","setAttribute","dataset","originalTitle","disabledToggle","toggle","removeAttribute","restoreTooltipOnBlur","enableButtonTooltip","enable","Tooltip","constructor","undefined","behatFakeDrawer","closeDrawer","focusOnOpenButton","updatePreferences","this","classList","contains","openDrawer","focusOnCloseButton","setUserPref","forceopen","Aria","hide","content","scrollTop","addInnerScrollListener","set","remove","isOpen","closeOnResize","has","dispatchEvent","eventname","cancelable","drawerInstance","pendingPromise","Pending","eventTypes","drawerShow","defaultPrevented","unhide","add","preference","state","getElementById","boundingRect","getBoundingClientRect","backdrop","show","style","overflow","closeButton","headerContent","setTimeout","focus","resolve","drawerShown","drawerHide","drawerHidden","toggleVisibility","displace","scrollPosition","transform","_this$drawerNode$data","drawrWidth","offsetWidth","scrollThreshold","direction","Math","abs","sign","preventOverlap","currentFocus","element","overlapping","right","left","currentBoundingRect","bottom","top","displaceOut","right_to_left","scrollX","comparisonInstance","activeElement","pagecontent","displaceDrawers","setLastUsedToggle","toggleButton","target","querySelectorAll","btn","lastused","closest","targetDrawer","getDrawerInstanceForNode","openDrawerButton","closeDrawerButton","lastUsedButton","focusLastUsedToggle","closeOtherDrawers","detail","btnSelector","drawerContentElements","Array","from","some","body","scrollY","innerHeight","preventCoveringFocusedElement","anyOpen","drawerContent","pending","registerListeners"],"mappings":"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"} \ No newline at end of file diff --git a/theme/boost/amd/build/footer-popover.min.js b/theme/boost/amd/build/footer-popover.min.js index 99311190bffd0..dc751653773a3 100644 --- a/theme/boost/amd/build/footer-popover.min.js +++ b/theme/boost/amd/build/footer-popover.min.js @@ -1,10 +1,10 @@ -define("theme_boost/footer-popover",["exports","jquery","./popover"],(function(_exports,_jquery,_popover){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("theme_boost/footer-popover",["exports","./bootstrap/popover"],(function(_exports,_popover){var obj; /** * Shows the footer content in a popover. * * @module theme_boost/footer-popover * @copyright 2021 Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),Object.defineProperty(_exports,"Popover",{enumerable:!0,get:function(){return _popover.default}}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),_popover=_interopRequireDefault(_popover);const SELECTORS_FOOTERCONTAINER='[data-region="footer-container-popover"]',SELECTORS_FOOTERCONTENT='[data-region="footer-content-popover"]',SELECTORS_FOOTERBUTTON='[data-action="footer-popover"]';let footerIsShown=!1;_exports.init=()=>{const container=document.querySelector(SELECTORS_FOOTERCONTAINER),footerButton=document.querySelector(SELECTORS_FOOTERBUTTON);(0,_jquery.default)(footerButton).popover({content:getFooterContent,container:container,html:!0,placement:"top",customClass:"footer",trigger:"click",boundary:"viewport",popperConfig:{modifiers:{preventOverflow:{boundariesElement:"viewport",padding:48},offset:{},flip:{behavior:"flip"},arrow:{element:".arrow"}}}}),document.addEventListener("click",(e=>{footerIsShown&&!e.target.closest(SELECTORS_FOOTERCONTAINER)&&(0,_jquery.default)(footerButton).popover("hide")}),!0),document.addEventListener("keydown",(e=>{footerIsShown&&"Escape"===e.key&&((0,_jquery.default)(footerButton).popover("hide"),footerButton.focus())})),document.addEventListener("focus",(e=>{footerIsShown&&!e.target.closest(SELECTORS_FOOTERCONTAINER)&&(0,_jquery.default)(footerButton).popover("hide")}),!0),(0,_jquery.default)(footerButton).on("show.bs.popover",(()=>{footerIsShown=!0})),(0,_jquery.default)(footerButton).on("hide.bs.popover",(()=>{footerIsShown=!1}))};const getFooterContent=()=>document.querySelector(SELECTORS_FOOTERCONTENT).innerHTML})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),Object.defineProperty(_exports,"Popover",{enumerable:!0,get:function(){return _popover.default}}),_exports.init=void 0,_popover=(obj=_popover)&&obj.__esModule?obj:{default:obj};const SELECTORS_FOOTERCONTAINER='[data-region="footer-container-popover"]',SELECTORS_FOOTERCONTENT='[data-region="footer-content-popover"]',SELECTORS_FOOTERBUTTON='[data-action="footer-popover"]';let footerIsShown=!1;_exports.init=()=>{const container=document.querySelector(SELECTORS_FOOTERCONTAINER),footerButton=document.querySelector(SELECTORS_FOOTERBUTTON);new _popover.default(footerButton,{content:getFooterContent,container:container,html:!0,placement:"top",customClass:"footer",trigger:"click",boundary:"viewport",popperConfig:{modifiers:{preventOverflow:{boundariesElement:"viewport",padding:48},offset:{},flip:{behavior:"flip"},arrow:{element:".arrow"}}}}),document.addEventListener("click",(e=>{footerIsShown&&!e.target.closest(SELECTORS_FOOTERCONTAINER)&&_popover.default.getInstance(footerButton).hide()}),!0),document.addEventListener("keydown",(e=>{footerIsShown&&"Escape"===e.key&&(_popover.default.getInstance(footerButton).hide(),footerButton.focus())})),document.addEventListener("focus",(e=>{footerIsShown&&!e.target.closest(SELECTORS_FOOTERCONTAINER)&&_popover.default.getInstance(footerButton).hide()}),!0),footerButton.addEventListener("show.bs.popover",(()=>{footerIsShown=!0})),footerButton.addEventListener("hide.bs.popover",(()=>{footerIsShown=!1}))};const getFooterContent=()=>document.querySelector(SELECTORS_FOOTERCONTENT).innerHTML})); //# sourceMappingURL=footer-popover.min.js.map \ No newline at end of file diff --git a/theme/boost/amd/build/footer-popover.min.js.map b/theme/boost/amd/build/footer-popover.min.js.map index 63b6bcba35ec4..7d85cbabdcc41 100644 --- a/theme/boost/amd/build/footer-popover.min.js.map +++ b/theme/boost/amd/build/footer-popover.min.js.map @@ -1 +1 @@ -{"version":3,"file":"footer-popover.min.js","sources":["../src/footer-popover.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 * Shows the footer content in a popover.\n *\n * @module theme_boost/footer-popover\n * @copyright 2021 Bas Brands\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Popover from './popover';\n\nconst SELECTORS = {\n FOOTERCONTAINER: '[data-region=\"footer-container-popover\"]',\n FOOTERCONTENT: '[data-region=\"footer-content-popover\"]',\n FOOTERBUTTON: '[data-action=\"footer-popover\"]'\n};\n\nlet footerIsShown = false;\n\nexport const init = () => {\n const container = document.querySelector(SELECTORS.FOOTERCONTAINER);\n const footerButton = document.querySelector(SELECTORS.FOOTERBUTTON);\n\n // All jQuery in this code can be replaced when MDL-71979 is integrated.\n $(footerButton).popover({\n content: getFooterContent,\n container: container,\n html: true,\n placement: 'top',\n customClass: 'footer',\n trigger: 'click',\n boundary: 'viewport',\n popperConfig: {\n modifiers: {\n preventOverflow: {\n boundariesElement: 'viewport',\n padding: 48\n },\n offset: {},\n flip: {\n behavior: 'flip'\n },\n arrow: {\n element: '.arrow'\n },\n }\n }\n });\n\n document.addEventListener('click', e => {\n if (footerIsShown && !e.target.closest(SELECTORS.FOOTERCONTAINER)) {\n $(footerButton).popover('hide');\n }\n },\n true);\n\n document.addEventListener('keydown', e => {\n if (footerIsShown && e.key === 'Escape') {\n $(footerButton).popover('hide');\n footerButton.focus();\n }\n });\n\n document.addEventListener('focus', e => {\n if (footerIsShown && !e.target.closest(SELECTORS.FOOTERCONTAINER)) {\n $(footerButton).popover('hide');\n }\n },\n true);\n\n $(footerButton).on('show.bs.popover', () => {\n footerIsShown = true;\n });\n\n $(footerButton).on('hide.bs.popover', () => {\n footerIsShown = false;\n });\n};\n\n/**\n * Get the footer content for popover.\n *\n * @returns {String} HTML string\n * @private\n */\nconst getFooterContent = () => {\n return document.querySelector(SELECTORS.FOOTERCONTENT).innerHTML;\n};\n\nexport {\n Popover\n};\n"],"names":["SELECTORS","footerIsShown","container","document","querySelector","footerButton","popover","content","getFooterContent","html","placement","customClass","trigger","boundary","popperConfig","modifiers","preventOverflow","boundariesElement","padding","offset","flip","behavior","arrow","element","addEventListener","e","target","closest","key","focus","on","innerHTML"],"mappings":";;;;;;;4QA0BMA,0BACe,2CADfA,wBAEa,yCAFbA,uBAGY,qCAGdC,eAAgB,gBAEA,WACVC,UAAYC,SAASC,cAAcJ,2BACnCK,aAAeF,SAASC,cAAcJ,4CAG1CK,cAAcC,QAAQ,CACpBC,QAASC,iBACTN,UAAWA,UACXO,MAAM,EACNC,UAAW,MACXC,YAAa,SACbC,QAAS,QACTC,SAAU,WACVC,aAAc,CACVC,UAAW,CACPC,gBAAiB,CACbC,kBAAmB,WACnBC,QAAS,IAEbC,OAAQ,GACRC,KAAM,CACFC,SAAU,QAEdC,MAAO,CACHC,QAAS,cAMzBpB,SAASqB,iBAAiB,SAASC,IAC3BxB,gBAAkBwB,EAAEC,OAAOC,QAAQ3B,gDACjCK,cAAcC,QAAQ,WAGhC,GAEAH,SAASqB,iBAAiB,WAAWC,IAC7BxB,eAA2B,WAAVwB,EAAEG,0BACjBvB,cAAcC,QAAQ,QACxBD,aAAawB,YAIrB1B,SAASqB,iBAAiB,SAASC,IAC3BxB,gBAAkBwB,EAAEC,OAAOC,QAAQ3B,gDACjCK,cAAcC,QAAQ,WAGhC,uBAEED,cAAcyB,GAAG,mBAAmB,KAClC7B,eAAgB,yBAGlBI,cAAcyB,GAAG,mBAAmB,KAClC7B,eAAgB,YAUlBO,iBAAmB,IACdL,SAASC,cAAcJ,yBAAyB+B"} \ No newline at end of file +{"version":3,"file":"footer-popover.min.js","sources":["../src/footer-popover.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 * Shows the footer content in a popover.\n *\n * @module theme_boost/footer-popover\n * @copyright 2021 Bas Brands\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Popover from './bootstrap/popover';\n\nconst SELECTORS = {\n FOOTERCONTAINER: '[data-region=\"footer-container-popover\"]',\n FOOTERCONTENT: '[data-region=\"footer-content-popover\"]',\n FOOTERBUTTON: '[data-action=\"footer-popover\"]'\n};\n\nlet footerIsShown = false;\n\nexport const init = () => {\n const container = document.querySelector(SELECTORS.FOOTERCONTAINER);\n const footerButton = document.querySelector(SELECTORS.FOOTERBUTTON);\n\n new Popover(footerButton, {\n content: getFooterContent,\n container: container,\n html: true,\n placement: 'top',\n customClass: 'footer',\n trigger: 'click',\n boundary: 'viewport',\n popperConfig: {\n modifiers: {\n preventOverflow: {\n boundariesElement: 'viewport',\n padding: 48\n },\n offset: {},\n flip: {\n behavior: 'flip'\n },\n arrow: {\n element: '.arrow'\n },\n }\n }\n });\n\n document.addEventListener('click', e => {\n if (footerIsShown && !e.target.closest(SELECTORS.FOOTERCONTAINER)) {\n Popover.getInstance(footerButton).hide();\n }\n },\n true);\n\n document.addEventListener('keydown', e => {\n if (footerIsShown && e.key === 'Escape') {\n Popover.getInstance(footerButton).hide();\n footerButton.focus();\n }\n });\n\n document.addEventListener('focus', e => {\n if (footerIsShown && !e.target.closest(SELECTORS.FOOTERCONTAINER)) {\n Popover.getInstance(footerButton).hide();\n }\n },\n true);\n\n footerButton.addEventListener('show.bs.popover', () => {\n footerIsShown = true;\n });\n\n footerButton.addEventListener('hide.bs.popover', () => {\n footerIsShown = false;\n });\n};\n\n/**\n * Get the footer content for popover.\n *\n * @returns {String} HTML string\n * @private\n */\nconst getFooterContent = () => {\n return document.querySelector(SELECTORS.FOOTERCONTENT).innerHTML;\n};\n\nexport {\n Popover\n};\n"],"names":["SELECTORS","footerIsShown","container","document","querySelector","footerButton","Popover","content","getFooterContent","html","placement","customClass","trigger","boundary","popperConfig","modifiers","preventOverflow","boundariesElement","padding","offset","flip","behavior","arrow","element","addEventListener","e","target","closest","getInstance","hide","key","focus","innerHTML"],"mappings":";;;;;;;oPAyBMA,0BACe,2CADfA,wBAEa,yCAFbA,uBAGY,qCAGdC,eAAgB,gBAEA,WACVC,UAAYC,SAASC,cAAcJ,2BACnCK,aAAeF,SAASC,cAAcJ,4BAExCM,iBAAQD,aAAc,CACtBE,QAASC,iBACTN,UAAWA,UACXO,MAAM,EACNC,UAAW,MACXC,YAAa,SACbC,QAAS,QACTC,SAAU,WACVC,aAAc,CACVC,UAAW,CACPC,gBAAiB,CACbC,kBAAmB,WACnBC,QAAS,IAEbC,OAAQ,GACRC,KAAM,CACFC,SAAU,QAEdC,MAAO,CACHC,QAAS,cAMzBpB,SAASqB,iBAAiB,SAASC,IAC3BxB,gBAAkBwB,EAAEC,OAAOC,QAAQ3B,6CAC3B4B,YAAYvB,cAAcwB,UAG1C,GAEA1B,SAASqB,iBAAiB,WAAWC,IAC7BxB,eAA2B,WAAVwB,EAAEK,uBACXF,YAAYvB,cAAcwB,OAClCxB,aAAa0B,YAIrB5B,SAASqB,iBAAiB,SAASC,IAC3BxB,gBAAkBwB,EAAEC,OAAOC,QAAQ3B,6CAC3B4B,YAAYvB,cAAcwB,UAG1C,GAEAxB,aAAamB,iBAAiB,mBAAmB,KAC7CvB,eAAgB,KAGpBI,aAAamB,iBAAiB,mBAAmB,KAC7CvB,eAAgB,YAUlBO,iBAAmB,IACdL,SAASC,cAAcJ,yBAAyBgC"} \ No newline at end of file diff --git a/theme/boost/amd/build/loader.min.js b/theme/boost/amd/build/loader.min.js index e4c11d3b14e62..9bb5a10bf7c1d 100644 --- a/theme/boost/amd/build/loader.min.js +++ b/theme/boost/amd/build/loader.min.js @@ -1,4 +1,4 @@ -define("theme_boost/loader",["exports","jquery","./aria","./index","core/pending","./bootstrap/tools/sanitizer","./pending"],(function(_exports,_jquery,Aria,_index,_pending,_sanitizer,_pending2){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("theme_boost/loader",["exports","./aria","./index","core/pending","./bootstrap/util/sanitizer","./pending"],(function(_exports,Aria,Bootstrap,_pending,_sanitizer,_pending2){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj} /** * Template renderer for Moodle. Load and render Moodle templates with Mustache. * @@ -6,6 +6,6 @@ define("theme_boost/loader",["exports","jquery","./aria","./index","core/pending * @copyright 2015 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 2.9 - */Object.defineProperty(_exports,"__esModule",{value:!0}),Object.defineProperty(_exports,"Bootstrap",{enumerable:!0,get:function(){return _index.default}}),_jquery=_interopRequireDefault(_jquery),Aria=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Aria),_index=_interopRequireDefault(_index),_pending=_interopRequireDefault(_pending),_pending2=_interopRequireDefault(_pending2);const pendingPromise=new _pending.default("theme_boost/loader:init");(0,_pending2.default)(),Aria.init(),(()=>{(0,_jquery.default)('a[data-toggle="tab"]').on("shown.bs.tab",(function(e){var hash=(0,_jquery.default)(e.target).attr("href");history.replaceState?history.replaceState(null,null,hash):location.hash=hash}));const hash=window.location.hash;if(hash){const tab=document.querySelector('[role="tablist"] [href="'+hash+'"]');tab&&tab.click()}})(),(0,_jquery.default)("body").popover({container:"body",selector:'[data-toggle="popover"]',trigger:"focus click",whitelist:Object.assign(_sanitizer.DefaultWhitelist,{table:[],thead:[],tbody:[],tr:[],th:[],td:[]})}),document.addEventListener("keydown",(e=>{"Escape"===e.key&&e.target.closest('[data-toggle="popover"]')&&(0,_jquery.default)(e.target).popover("hide"),"Enter"===e.key&&e.target.closest('[data-toggle="popover"]')&&(0,_jquery.default)(e.target).popover("show")})),(0,_jquery.default)("body").tooltip({container:"body",selector:'[data-toggle="tooltip"]'}),_jquery.default.fn.dropdown.Constructor.Default.popperConfig={modifiers:{flip:{enabled:!1},storeTopPosition:{enabled:!0,fn:(data,options)=>(data.storedTop=data.offsets.popper.top,data),order:299},restoreTopPosition:{enabled:!0,fn:(data,options)=>(data.offsets.popper.top=data.storedTop,data),order:301}}},pendingPromise.resolve()})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.Bootstrap=void 0,Aria=_interopRequireWildcard(Aria),Bootstrap=_interopRequireWildcard(Bootstrap),_exports.Bootstrap=Bootstrap,_pending=_interopRequireDefault(_pending),_pending2=_interopRequireDefault(_pending2);const pendingPromise=new _pending.default("theme_boost/loader:init");(0,_pending2.default)(),Aria.init(),(()=>{[...document.querySelectorAll('a[data-bs-toggle="tab"]')].map((tabTriggerEl=>tabTriggerEl.addEventListener("shown.bs.tab",(e=>{var hash=e.target.getAttribute("href");history.replaceState?history.replaceState(null,null,hash):location.hash=hash}))));const hash=window.location.hash;if(hash){const tab=document.querySelector('[role="tablist"] [href="'+hash+'"]');tab&&tab.click()}})(),(()=>{const popoverTriggerList=document.querySelectorAll('[data-bs-toggle="popover"]'),popoverConfig={container:"body",trigger:"focus",allowList:Object.assign(_sanitizer.DefaultAllowlist,{table:[],thead:[],tbody:[],tr:[],th:[],td:[]})};[...popoverTriggerList].map((popoverTriggerEl=>new Bootstrap.Popover(popoverTriggerEl,popoverConfig))),document.addEventListener("keydown",(e=>{"Escape"===e.key&&e.target.closest('[data-bs-toggle="popover"]')&&Bootstrap.Popover.getInstance(e.target).hide(),"Enter"===e.key&&e.target.closest('[data-bs-toggle="popover"]')&&Bootstrap.Popover.getInstance(e.target).show()}))})(),[...document.querySelectorAll('[data-bs-toggle="tooltip"]')].map((tooltipTriggerEl=>new Bootstrap.Tooltip(tooltipTriggerEl))),pendingPromise.resolve()})); //# sourceMappingURL=loader.min.js.map \ No newline at end of file diff --git a/theme/boost/amd/build/loader.min.js.map b/theme/boost/amd/build/loader.min.js.map index d7e43817e0913..6626bd327fbfc 100644 --- a/theme/boost/amd/build/loader.min.js.map +++ b/theme/boost/amd/build/loader.min.js.map @@ -1 +1 @@ -{"version":3,"file":"loader.min.js","sources":["../src/loader.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 * Template renderer for Moodle. Load and render Moodle templates with Mustache.\n *\n * @module theme_boost/loader\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\n\nimport $ from 'jquery';\nimport * as Aria from './aria';\nimport Bootstrap from './index';\nimport Pending from 'core/pending';\nimport {DefaultWhitelist} from './bootstrap/tools/sanitizer';\nimport setupBootstrapPendingChecks from './pending';\n\n/**\n * Rember the last visited tabs.\n */\nconst rememberTabs = () => {\n $('a[data-toggle=\"tab\"]').on('shown.bs.tab', function(e) {\n var hash = $(e.target).attr('href');\n if (history.replaceState) {\n history.replaceState(null, null, hash);\n } else {\n location.hash = hash;\n }\n });\n const hash = window.location.hash;\n if (hash) {\n const tab = document.querySelector('[role=\"tablist\"] [href=\"' + hash + '\"]');\n if (tab) {\n tab.click();\n }\n }\n};\n\n/**\n * Enable all popovers\n *\n */\nconst enablePopovers = () => {\n $('body').popover({\n container: 'body',\n selector: '[data-toggle=\"popover\"]',\n trigger: 'focus click',\n whitelist: Object.assign(DefaultWhitelist, {\n table: [],\n thead: [],\n tbody: [],\n tr: [],\n th: [],\n td: [],\n }),\n });\n\n document.addEventListener('keydown', e => {\n if (e.key === 'Escape' && e.target.closest('[data-toggle=\"popover\"]')) {\n $(e.target).popover('hide');\n }\n if (e.key === 'Enter' && e.target.closest('[data-toggle=\"popover\"]')) {\n $(e.target).popover('show');\n }\n });\n};\n\n/**\n * Enable tooltips\n *\n */\nconst enableTooltips = () => {\n $('body').tooltip({\n container: 'body',\n selector: '[data-toggle=\"tooltip\"]',\n });\n};\n\nconst pendingPromise = new Pending('theme_boost/loader:init');\n\n// Add pending promise event listeners to relevant Bootstrap custom events.\nsetupBootstrapPendingChecks();\n\n// Setup Aria helpers for Bootstrap features.\nAria.init();\n\n// Remember the last visited tabs.\nrememberTabs();\n\n// Enable all popovers.\nenablePopovers();\n\n// Enable all tooltips.\nenableTooltips();\n\n// Disables flipping the dropdowns up or dynamically repositioning them along the Y-axis (based on the viewport)\n// to prevent the dropdowns getting hidden behind the navbar or them covering the trigger element.\n$.fn.dropdown.Constructor.Default.popperConfig = {\n modifiers: {\n flip: {\n enabled: false,\n },\n storeTopPosition: {\n enabled: true,\n // eslint-disable-next-line no-unused-vars\n fn(data, options) {\n data.storedTop = data.offsets.popper.top;\n return data;\n },\n order: 299\n },\n restoreTopPosition: {\n enabled: true,\n // eslint-disable-next-line no-unused-vars\n fn(data, options) {\n data.offsets.popper.top = data.storedTop;\n return data;\n },\n order: 301\n }\n },\n};\n\npendingPromise.resolve();\n\nexport {\n Bootstrap,\n};\n"],"names":["pendingPromise","Pending","Aria","init","on","e","hash","target","attr","history","replaceState","location","window","tab","document","querySelector","click","rememberTabs","popover","container","selector","trigger","whitelist","Object","assign","DefaultWhitelist","table","thead","tbody","tr","th","td","addEventListener","key","closest","tooltip","fn","dropdown","Constructor","Default","popperConfig","modifiers","flip","enabled","storeTopPosition","data","options","storedTop","offsets","popper","top","order","restoreTopPosition","resolve"],"mappings":";;;;;;;;i+BA4FMA,eAAiB,IAAIC,iBAAQ,mDAMnCC,KAAKC,OAhEgB,0BACf,wBAAwBC,GAAG,gBAAgB,SAASC,OAC9CC,MAAO,mBAAED,EAAEE,QAAQC,KAAK,QACxBC,QAAQC,aACRD,QAAQC,aAAa,KAAM,KAAMJ,MAEjCK,SAASL,KAAOA,cAGlBA,KAAOM,OAAOD,SAASL,QACzBA,KAAM,OACAO,IAAMC,SAASC,cAAc,2BAA6BT,KAAO,MACnEO,KACAA,IAAIG,UAsDhBC,uBA5CM,QAAQC,QAAQ,CACdC,UAAW,OACXC,SAAU,0BACVC,QAAS,cACTC,UAAWC,OAAOC,OAAOC,4BAAkB,CACvCC,MAAO,GACPC,MAAO,GACPC,MAAO,GACPC,GAAI,GACJC,GAAI,GACJC,GAAI,OAIZjB,SAASkB,iBAAiB,WAAW3B,IACnB,WAAVA,EAAE4B,KAAoB5B,EAAEE,OAAO2B,QAAQ,gDACrC7B,EAAEE,QAAQW,QAAQ,QAEV,UAAVb,EAAE4B,KAAmB5B,EAAEE,OAAO2B,QAAQ,gDACpC7B,EAAEE,QAAQW,QAAQ,+BAU1B,QAAQiB,QAAQ,CACdhB,UAAW,OACXC,SAAU,4CAuBhBgB,GAAGC,SAASC,YAAYC,QAAQC,aAAe,CAC7CC,UAAW,CACPC,KAAM,CACFC,SAAS,GAEbC,iBAAkB,CACdD,SAAS,EAETP,GAAE,CAACS,KAAMC,WACLD,KAAKE,UAAYF,KAAKG,QAAQC,OAAOC,IAC9BL,MAEXM,MAAO,KAEXC,mBAAoB,CAChBT,SAAS,EAETP,GAAE,CAACS,KAAMC,WACLD,KAAKG,QAAQC,OAAOC,IAAML,KAAKE,UACxBF,MAEXM,MAAO,OAKnBnD,eAAeqD"} \ No newline at end of file +{"version":3,"file":"loader.min.js","sources":["../src/loader.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 * Template renderer for Moodle. Load and render Moodle templates with Mustache.\n *\n * @module theme_boost/loader\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\n\nimport * as Aria from './aria';\nimport * as Bootstrap from './index';\nimport Pending from 'core/pending';\nimport {DefaultAllowlist} from './bootstrap/util/sanitizer';\nimport setupBootstrapPendingChecks from './pending';\n\n/**\n * Rember the last visited tabs.\n */\nconst rememberTabs = () => {\n const tabTriggerList = document.querySelectorAll('a[data-bs-toggle=\"tab\"]');\n [...tabTriggerList].map(tabTriggerEl => tabTriggerEl.addEventListener('shown.bs.tab', (e) => {\n var hash = e.target.getAttribute('href');\n if (history.replaceState) {\n history.replaceState(null, null, hash);\n } else {\n location.hash = hash;\n }\n }));\n const hash = window.location.hash;\n if (hash) {\n const tab = document.querySelector('[role=\"tablist\"] [href=\"' + hash + '\"]');\n if (tab) {\n tab.click();\n }\n }\n};\n\n/**\n * Enable all popovers\n *\n */\nconst enablePopovers = () => {\n const popoverTriggerList = document.querySelectorAll('[data-bs-toggle=\"popover\"]');\n const popoverConfig = {\n container: 'body',\n trigger: 'focus',\n allowList: Object.assign(DefaultAllowlist, {table: [], thead: [], tbody: [], tr: [], th: [], td: []}),\n };\n [...popoverTriggerList].map(popoverTriggerEl => new Bootstrap.Popover(popoverTriggerEl, popoverConfig));\n\n document.addEventListener('keydown', e => {\n if (e.key === 'Escape' && e.target.closest('[data-bs-toggle=\"popover\"]')) {\n Bootstrap.Popover.getInstance(e.target).hide();\n }\n if (e.key === 'Enter' && e.target.closest('[data-bs-toggle=\"popover\"]')) {\n Bootstrap.Popover.getInstance(e.target).show();\n }\n });\n};\n\n/**\n * Enable tooltips\n *\n */\nconst enableTooltips = () => {\n const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle=\"tooltip\"]');\n [...tooltipTriggerList].map(tooltipTriggerEl => new Bootstrap.Tooltip(tooltipTriggerEl));\n};\n\nconst pendingPromise = new Pending('theme_boost/loader:init');\n\n// Add pending promise event listeners to relevant Bootstrap custom events.\nsetupBootstrapPendingChecks();\n\n// Setup Aria helpers for Bootstrap features.\nAria.init();\n\n// Remember the last visited tabs.\nrememberTabs();\n\n// Enable all popovers.\nenablePopovers();\n\n// Enable all tooltips.\nenableTooltips();\n\n// TODO: Refactor this on Boostrap 5.\n\n// Disables flipping the dropdowns up or dynamically repositioning them along the Y-axis (based on the viewport)\n// to prevent the dropdowns getting hidden behind the navbar or them covering the trigger element.\n// $.fn.dropdown.Constructor.Default.popperConfig = {\n// modifiers: {\n// flip: {\n// enabled: false,\n// },\n// storeTopPosition: {\n// enabled: true,\n// // eslint-disable-next-line no-unused-vars\n// fn(data, options) {\n// data.storedTop = data.offsets.popper.top;\n// return data;\n// },\n// order: 299\n// },\n// restoreTopPosition: {\n// enabled: true,\n// // eslint-disable-next-line no-unused-vars\n// fn(data, options) {\n// data.offsets.popper.top = data.storedTop;\n// return data;\n// },\n// order: 301\n// }\n// },\n// };\n\npendingPromise.resolve();\n\nexport {\n Bootstrap,\n};\n"],"names":["pendingPromise","Pending","Aria","init","document","querySelectorAll","map","tabTriggerEl","addEventListener","e","hash","target","getAttribute","history","replaceState","location","window","tab","querySelector","click","rememberTabs","popoverTriggerList","popoverConfig","container","trigger","allowList","Object","assign","DefaultAllowlist","table","thead","tbody","tr","th","td","popoverTriggerEl","Bootstrap","Popover","key","closest","getInstance","hide","show","enablePopovers","tooltipTriggerEl","Tooltip","resolve"],"mappings":";;;;;;;;gSAoFMA,eAAiB,IAAIC,iBAAQ,mDAMnCC,KAAKC,OAzDgB,UACMC,SAASC,iBAAiB,4BAC7BC,KAAIC,cAAgBA,aAAaC,iBAAiB,gBAAiBC,QAC/EC,KAAOD,EAAEE,OAAOC,aAAa,QAC7BC,QAAQC,aACRD,QAAQC,aAAa,KAAM,KAAMJ,MAEjCK,SAASL,KAAOA,gBAGlBA,KAAOM,OAAOD,SAASL,QACzBA,KAAM,OACAO,IAAMb,SAASc,cAAc,2BAA6BR,KAAO,MACnEO,KACAA,IAAIE,UA8ChBC,GArCuB,YACbC,mBAAqBjB,SAASC,iBAAiB,8BAC/CiB,cAAgB,CAClBC,UAAW,OACXC,QAAS,QACTC,UAAWC,OAAOC,OAAOC,4BAAkB,CAACC,MAAO,GAAIC,MAAO,GAAIC,MAAO,GAAIC,GAAI,GAAIC,GAAI,GAAIC,GAAI,UAEjGb,oBAAoBf,KAAI6B,kBAAoB,IAAIC,UAAUC,QAAQF,iBAAkBb,iBAExFlB,SAASI,iBAAiB,WAAWC,IACnB,WAAVA,EAAE6B,KAAoB7B,EAAEE,OAAO4B,QAAQ,+BACvCH,UAAUC,QAAQG,YAAY/B,EAAEE,QAAQ8B,OAE9B,UAAVhC,EAAE6B,KAAmB7B,EAAEE,OAAO4B,QAAQ,+BACtCH,UAAUC,QAAQG,YAAY/B,EAAEE,QAAQ+B,WA0BpDC,OAhB+BvC,SAASC,iBAAiB,+BAC7BC,KAAIsC,kBAAoB,IAAIR,UAAUS,QAAQD,oBAkD1E5C,eAAe8C"} \ No newline at end of file diff --git a/theme/boost/amd/src/aria.js b/theme/boost/amd/src/aria.js index 39d874f263be9..37cd71ba745c4 100644 --- a/theme/boost/amd/src/aria.js +++ b/theme/boost/amd/src/aria.js @@ -22,6 +22,7 @@ */ import $ from 'jquery'; +import Tab from 'theme_boost/bootstrap/tab'; import Pending from 'core/pending'; import * as FocusLockManager from 'core/local/aria/focuslock'; @@ -486,7 +487,7 @@ const tabElementFix = () => { if (e.target.matches('[role="tablist"] [data-bs-toggle="tab"], [role="tablist"] [data-bs-toggle="pill"]')) { const tabs = e.target.closest('[role="tablist"]').querySelectorAll('[data-bs-toggle="tab"], [data-bs-toggle="pill"]'); e.preventDefault(); - $(e.target).tab('show'); + Tab.getOrCreateInstance(e.target).show(); tabs.forEach(tab => { tab.tabIndex = -1; }); diff --git a/theme/boost/amd/src/drawers.js b/theme/boost/amd/src/drawers.js index b0518a8b5f311..6d74ecfa052d9 100644 --- a/theme/boost/amd/src/drawers.js +++ b/theme/boost/amd/src/drawers.js @@ -28,8 +28,7 @@ import {debounce} from 'core/utils'; import {isSmall, isLarge} from 'core/pagehelpers'; import Pending from 'core/pending'; import {setUserPreference} from 'core_user/repository'; -// The jQuery module is only used for interacting with Boostrap 4. It can we removed when MDL-71979 is integrated. -import jQuery from 'jquery'; +import Tooltip from './bootstrap/tooltip'; let backdropPromise = null; @@ -142,12 +141,11 @@ const disableDrawerTooltips = (drawerNode) => { */ const disableButtonTooltip = (button, enableOnBlur) => { if (button.hasAttribute('data-original-title')) { - // The jQuery is still used in Boostrap 4. It can we removed when MDL-71979 is integrated. - jQuery(button).tooltip('disable'); + Tooltip.getInstance(button).disable(); button.setAttribute('title', button.dataset.originalTitle); } else { button.dataset.disabledToggle = button.dataset.toggle; - button.removeAttribute('data-toggle'); + button.removeAttribute('data-bs-toggle'); } if (enableOnBlur) { button.dataset.restoreTooltipOnBlur = true; @@ -180,13 +178,12 @@ const enableDrawerTooltips = (drawerNode) => { * @private */ const enableButtonTooltip = (button) => { - // The jQuery is still used in Boostrap 4. It can we removed when MDL-71979 is integrated. - if (button.hasAttribute('data-original-title')) { - jQuery(button).tooltip('enable'); + if (button.hasAttribute('data-bs-original-title')) { + Tooltip.getInstance(button).enable(); button.removeAttribute('title'); } else if (button.dataset.disabledToggle) { button.dataset.toggle = button.dataset.disabledToggle; - jQuery(button).tooltip(); + new Tooltip(button); } delete button.dataset.restoreTooltipOnBlur; }; @@ -432,8 +429,7 @@ export default class Drawers { // Remove open tooltip if still visible. let openButton = getDrawerOpenButton(this.drawerNode.id); if (openButton && openButton.hasAttribute('data-original-title')) { - // The jQuery is still used in Boostrap 4. It can we removed when MDL-71979 is integrated. - jQuery(openButton)?.tooltip('hide'); + Tooltip.getInstance(openButton)?.hide(); } Aria.unhide(this.drawerNode); @@ -504,8 +500,7 @@ export default class Drawers { headerContent?.classList.toggle('hidden', true); // Remove the close button tooltip if visible. if (closeButton.hasAttribute('data-original-title')) { - // The jQuery is still used in Boostrap 4. It can we removed when MDL-71979 is integrated. - jQuery(closeButton)?.tooltip('hide'); + Tooltip.getInstance(closeButton)?.hide(); } const preference = this.drawerNode.dataset.preference; diff --git a/theme/boost/amd/src/footer-popover.js b/theme/boost/amd/src/footer-popover.js index fb703293b8d30..0f45d2a10b82e 100644 --- a/theme/boost/amd/src/footer-popover.js +++ b/theme/boost/amd/src/footer-popover.js @@ -21,8 +21,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import $ from 'jquery'; -import Popover from './popover'; +import Popover from './bootstrap/popover'; const SELECTORS = { FOOTERCONTAINER: '[data-region="footer-container-popover"]', @@ -36,8 +35,7 @@ export const init = () => { const container = document.querySelector(SELECTORS.FOOTERCONTAINER); const footerButton = document.querySelector(SELECTORS.FOOTERBUTTON); - // All jQuery in this code can be replaced when MDL-71979 is integrated. - $(footerButton).popover({ + new Popover(footerButton, { content: getFooterContent, container: container, html: true, @@ -64,30 +62,30 @@ export const init = () => { document.addEventListener('click', e => { if (footerIsShown && !e.target.closest(SELECTORS.FOOTERCONTAINER)) { - $(footerButton).popover('hide'); + Popover.getInstance(footerButton).hide(); } }, true); document.addEventListener('keydown', e => { if (footerIsShown && e.key === 'Escape') { - $(footerButton).popover('hide'); + Popover.getInstance(footerButton).hide(); footerButton.focus(); } }); document.addEventListener('focus', e => { if (footerIsShown && !e.target.closest(SELECTORS.FOOTERCONTAINER)) { - $(footerButton).popover('hide'); + Popover.getInstance(footerButton).hide(); } }, true); - $(footerButton).on('show.bs.popover', () => { + footerButton.addEventListener('show.bs.popover', () => { footerIsShown = true; }); - $(footerButton).on('hide.bs.popover', () => { + footerButton.addEventListener('hide.bs.popover', () => { footerIsShown = false; }); }; diff --git a/theme/boost/amd/src/loader.js b/theme/boost/amd/src/loader.js index fd7d72bfbeaf1..137767deeb52e 100644 --- a/theme/boost/amd/src/loader.js +++ b/theme/boost/amd/src/loader.js @@ -22,25 +22,25 @@ * @since 2.9 */ -import $ from 'jquery'; import * as Aria from './aria'; -import Bootstrap from './index'; +import * as Bootstrap from './index'; import Pending from 'core/pending'; -import {DefaultWhitelist} from './bootstrap/tools/sanitizer'; +import {DefaultAllowlist} from './bootstrap/util/sanitizer'; import setupBootstrapPendingChecks from './pending'; /** * Rember the last visited tabs. */ const rememberTabs = () => { - $('a[data-toggle="tab"]').on('shown.bs.tab', function(e) { - var hash = $(e.target).attr('href'); + const tabTriggerList = document.querySelectorAll('a[data-bs-toggle="tab"]'); + [...tabTriggerList].map(tabTriggerEl => tabTriggerEl.addEventListener('shown.bs.tab', (e) => { + var hash = e.target.getAttribute('href'); if (history.replaceState) { history.replaceState(null, null, hash); } else { location.hash = hash; } - }); + })); const hash = window.location.hash; if (hash) { const tab = document.querySelector('[role="tablist"] [href="' + hash + '"]'); @@ -55,26 +55,20 @@ const rememberTabs = () => { * */ const enablePopovers = () => { - $('body').popover({ + const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]'); + const popoverConfig = { container: 'body', - selector: '[data-toggle="popover"]', - trigger: 'focus click', - whitelist: Object.assign(DefaultWhitelist, { - table: [], - thead: [], - tbody: [], - tr: [], - th: [], - td: [], - }), - }); + trigger: 'focus', + allowList: Object.assign(DefaultAllowlist, {table: [], thead: [], tbody: [], tr: [], th: [], td: []}), + }; + [...popoverTriggerList].map(popoverTriggerEl => new Bootstrap.Popover(popoverTriggerEl, popoverConfig)); document.addEventListener('keydown', e => { - if (e.key === 'Escape' && e.target.closest('[data-toggle="popover"]')) { - $(e.target).popover('hide'); + if (e.key === 'Escape' && e.target.closest('[data-bs-toggle="popover"]')) { + Bootstrap.Popover.getInstance(e.target).hide(); } - if (e.key === 'Enter' && e.target.closest('[data-toggle="popover"]')) { - $(e.target).popover('show'); + if (e.key === 'Enter' && e.target.closest('[data-bs-toggle="popover"]')) { + Bootstrap.Popover.getInstance(e.target).show(); } }); }; @@ -84,10 +78,8 @@ const enablePopovers = () => { * */ const enableTooltips = () => { - $('body').tooltip({ - container: 'body', - selector: '[data-toggle="tooltip"]', - }); + const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); + [...tooltipTriggerList].map(tooltipTriggerEl => new Bootstrap.Tooltip(tooltipTriggerEl)); }; const pendingPromise = new Pending('theme_boost/loader:init'); @@ -107,33 +99,35 @@ enablePopovers(); // Enable all tooltips. enableTooltips(); +// TODO: Refactor this on Boostrap 5. + // Disables flipping the dropdowns up or dynamically repositioning them along the Y-axis (based on the viewport) // to prevent the dropdowns getting hidden behind the navbar or them covering the trigger element. -$.fn.dropdown.Constructor.Default.popperConfig = { - modifiers: { - flip: { - enabled: false, - }, - storeTopPosition: { - enabled: true, - // eslint-disable-next-line no-unused-vars - fn(data, options) { - data.storedTop = data.offsets.popper.top; - return data; - }, - order: 299 - }, - restoreTopPosition: { - enabled: true, - // eslint-disable-next-line no-unused-vars - fn(data, options) { - data.offsets.popper.top = data.storedTop; - return data; - }, - order: 301 - } - }, -}; +// $.fn.dropdown.Constructor.Default.popperConfig = { +// modifiers: { +// flip: { +// enabled: false, +// }, +// storeTopPosition: { +// enabled: true, +// // eslint-disable-next-line no-unused-vars +// fn(data, options) { +// data.storedTop = data.offsets.popper.top; +// return data; +// }, +// order: 299 +// }, +// restoreTopPosition: { +// enabled: true, +// // eslint-disable-next-line no-unused-vars +// fn(data, options) { +// data.offsets.popper.top = data.storedTop; +// return data; +// }, +// order: 301 +// } +// }, +// }; pendingPromise.resolve(); diff --git a/theme/boost/templates/drawer.mustache b/theme/boost/templates/drawer.mustache index 9e8cc0008f0d2..afe4f18f141f8 100644 --- a/theme/boost/templates/drawer.mustache +++ b/theme/boost/templates/drawer.mustache @@ -41,8 +41,8 @@ data-toggler="drawers" data-action="closedrawer" data-target="{{$id}}{{/id}}" - data-toggle="tooltip" - data-placement="{{$tooltipplacement}}right{{/tooltipplacement}}" + data-bs-toggle="tooltip" + data-bs-placement="{{$tooltipplacement}}right{{/tooltipplacement}}" title="{{$closebuttontext}}{{#str}}closedrawer, core{{/str}}{{/closebuttontext}}" > {{#pix}} e/cancel, core {{/pix}} diff --git a/theme/boost/templates/drawers.mustache b/theme/boost/templates/drawers.mustache index 7918f2e617795..43f39ad1ebf42 100644 --- a/theme/boost/templates/drawers.mustache +++ b/theme/boost/templates/drawers.mustache @@ -102,8 +102,8 @@ data-toggler="drawers" data-action="toggle" data-target="theme_boost-drawers-courseindex" - data-toggle="tooltip" - data-placement="right" + data-bs-toggle="tooltip" + data-bs-placement="right" title="{{#str}}opendrawerindex, core{{/str}}" > {{#str}}opendrawerindex, core{{/str}} @@ -118,8 +118,8 @@ data-toggler="drawers" data-action="toggle" data-target="theme_boost-drawers-blocks" - data-toggle="tooltip" - data-placement="right" + data-bs-toggle="tooltip" + data-bs-placement="right" title="{{#str}}opendrawerblocks, core{{/str}}" > {{#str}}opendrawerblocks, core{{/str}}