From 77b684caa7cb6e878c7426bb5478927e6187e177 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 BS5 related JS - Migrate Boostrap components related JS from JQuery to vanilla JS. - Migrate Boostrap components related Events from JQuery to vanilla 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 +- course/amd/build/actionbar/initials.min.js | 4 +- .../amd/build/actionbar/initials.min.js.map | 2 +- .../local/activitychooser/dialogue.min.js | 2 +- .../local/activitychooser/dialogue.min.js.map | 2 +- course/amd/src/actionbar/initials.js | 6 +- .../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 +- grade/amd/build/comboboxsearch/grade.min.js | 2 +- .../amd/build/comboboxsearch/grade.min.js.map | 2 +- grade/amd/build/searchwidget/initials.min.js | 4 +- .../build/searchwidget/initials.min.js.map | 2 +- grade/amd/src/comboboxsearch/grade.js | 2 +- grade/amd/src/searchwidget/initials.js | 6 +- grade/report/grader/amd/build/collapse.min.js | 2 +- .../grader/amd/build/collapse.min.js.map | 2 +- .../grader/amd/build/stickycolspan.min.js | 5 +- .../grader/amd/build/stickycolspan.min.js.map | 2 +- grade/report/grader/amd/src/collapse.js | 8 +- grade/report/grader/amd/src/stickycolspan.js | 9 +- group/amd/build/comboboxsearch/group.min.js | 2 +- .../amd/build/comboboxsearch/group.min.js.map | 2 +- group/amd/src/comboboxsearch/group.js | 2 +- .../comboboxsearch/search_combobox.min.js | 4 +- .../comboboxsearch/search_combobox.min.js.map | 2 +- lib/amd/build/dynamic_tabs.min.js | 2 +- lib/amd/build/dynamic_tabs.min.js.map | 2 +- .../build/local/action_menu/subpanel.min.js | 4 +- .../local/action_menu/subpanel.min.js.map | 2 +- lib/amd/build/local/dropdown/dialog.min.js | 4 +- .../build/local/dropdown/dialog.min.js.map | 2 +- lib/amd/build/modal.min.js | 2 +- lib/amd/build/modal.min.js.map | 2 +- lib/amd/build/moremenu.min.js | 4 +- lib/amd/build/moremenu.min.js.map | 2 +- lib/amd/src/comboboxsearch/search_combobox.js | 5 +- lib/amd/src/dynamic_tabs.js | 61 +++++----- lib/amd/src/local/action_menu/subpanel.js | 6 +- lib/amd/src/local/dropdown/dialog.js | 6 +- lib/amd/src/modal.js | 3 + lib/amd/src/moremenu.js | 3 +- lib/form/amd/build/collapsesections.min.js | 4 +- .../amd/build/collapsesections.min.js.map | 2 +- lib/form/amd/src/collapsesections.js | 40 ++++--- lib/templates/local/toast/message.mustache | 11 +- lib/templates/search_input_navbar.mustache | 28 ++--- .../fixtures/dropdown_output_testpage.php | 10 +- message/amd/build/message_drawer.min.js | 2 +- message/amd/build/message_drawer.min.js.map | 2 +- .../build/message_drawer_view_contacts.min.js | 2 +- .../message_drawer_view_contacts.min.js.map | 2 +- ...essage_drawer_view_overview_section.min.js | 2 +- ...ge_drawer_view_overview_section.min.js.map | 2 +- message/amd/src/message_drawer.js | 20 ++-- .../amd/src/message_drawer_view_contacts.js | 4 +- .../message_drawer_view_overview_section.js | 4 +- .../grading/extra_filters_dropdown.min.js | 5 +- .../grading/extra_filters_dropdown.min.js.map | 2 +- .../grading/extra_filters_dropdown.js | 3 +- .../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 | 12 +- theme/boost/amd/src/drawers.js | 21 ++-- theme/boost/amd/src/footer-popover.js | 16 ++- theme/boost/amd/src/loader.js | 109 ++++++++++-------- 98 files changed, 373 insertions(+), 367 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/actionbar/initials.min.js b/course/amd/build/actionbar/initials.min.js index b712e4d2a31be..89a24585b3291 100644 --- a/course/amd/build/actionbar/initials.min.js +++ b/course/amd/build/actionbar/initials.min.js @@ -1,10 +1,10 @@ -define("core_course/actionbar/initials",["exports","core/pending","core/url","core/custom_interaction_events","jquery"],(function(_exports,_pending,Url,_custom_interaction_events,_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}} +define("core_course/actionbar/initials",["exports","core/pending","core/url","core/custom_interaction_events","theme_boost/bootstrap/dropdown"],(function(_exports,_pending,Url,_custom_interaction_events,_dropdown){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}} /** * A small dropdown to filter users. * * @module core_course/actionbar/initials * @copyright 2022 Mathew May * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Url=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}(Url),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_jquery=_interopRequireDefault(_jquery);let registered=!1;const selectors_pageListItem="page-item",selectors_pageClickableItem=".page-link",selectors_activeItem="active",selectors_formDropdown=".initialsdropdownform",selectors_parentDomNode=".initials-selector",selectors_firstInitial="firstinitial",selectors_lastInitial="lastinitial",selectors_initialBars=".initialbar",selectors_targetButton="initialswidget",selectors_formItems={type:"submit",save:"save",cancel:"cancel"};_exports.init=function(callingLink){let firstInitialParam=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"sifirst",lastInitialParam=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"silast",additionalParams=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[];if(registered)return;const pendingPromise=new _pending.default;registerListenerEvents(callingLink,firstInitialParam,lastInitialParam,additionalParams),(0,_jquery.default)(selectors_parentDomNode).on("shown.bs.dropdown",(()=>{document.querySelector(selectors_pageClickableItem).focus({preventScroll:!0})})),pendingPromise.resolve(),registered=!0};const registerListenerEvents=function(callingLink){let firstInitialParam=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"sifirst",lastInitialParam=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"silast",additionalParams=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[];const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events),events.forEach((event=>{document.addEventListener(event,(e=>{let{firstActive:firstActive,lastActive:lastActive,sifirst:sifirst,silast:silast}=onClickVariables(),itemToReset="";if(e.target.closest(selectors_formDropdown)&&e.preventDefault(),e.target.closest("".concat(selectors_formDropdown," .").concat(selectors_pageListItem))){if(e.target.classList.contains(selectors_pageListItem))return;e.target.closest(selectors_initialBars).classList.contains(selectors_firstInitial)?(sifirst=e.target,itemToReset=firstActive):(silast=e.target,itemToReset=lastActive),swapActiveItems(itemToReset,e)}if(e.target.closest("".concat(selectors_formDropdown))&&e.target.type===selectors_formItems.type){if(e.target.dataset.action===selectors_formItems.save){const params={id:e.target.closest(selectors_formDropdown).dataset.courseid,[firstInitialParam]:sifirst.parentElement.classList.contains("initialbarall")?"":sifirst.value,[lastInitialParam]:silast.parentElement.classList.contains("initialbarall")?"":silast.value};for(const[key,value]of Object.entries(additionalParams))params[key]=value;window.location=Url.relativeUrl(callingLink,params)}e.target.dataset.action===selectors_formItems.cancel&&(0,_jquery.default)(".".concat(selectors_targetButton)).dropdown("toggle")}}))}))},onClickVariables=()=>{const firstItems=[...document.querySelectorAll(".".concat(selectors_firstInitial," li"))],lastItems=[...document.querySelectorAll(".".concat(selectors_lastInitial," li"))],firstActive=firstItems.filter((item=>item.classList.contains(selectors_activeItem)))[0],lastActive=lastItems.filter((item=>item.classList.contains(selectors_activeItem)))[0];let sifirst=firstActive.querySelector(selectors_pageClickableItem),silast=lastActive.querySelector(selectors_pageClickableItem);return{firstActive:firstActive,lastActive:lastActive,sifirst:sifirst,silast:silast}},swapActiveItems=(itemToReset,e)=>{itemToReset.classList.remove(selectors_activeItem),itemToReset.querySelector(selectors_pageClickableItem).ariaCurrent=!1;e.target.parentElement.classList.add(selectors_activeItem),e.target.ariaCurrent=!0}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Url=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}(Url),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_dropdown=_interopRequireDefault(_dropdown);let registered=!1;const selectors_pageListItem="page-item",selectors_pageClickableItem=".page-link",selectors_activeItem="active",selectors_formDropdown=".initialsdropdownform",selectors_parentDomNode=".initials-selector",selectors_firstInitial="firstinitial",selectors_lastInitial="lastinitial",selectors_initialBars=".initialbar",selectors_targetButton="initialswidget",selectors_formItems={type:"submit",save:"save",cancel:"cancel"};_exports.init=function(callingLink){let firstInitialParam=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"sifirst",lastInitialParam=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"silast",additionalParams=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[];if(registered)return;const pendingPromise=new _pending.default;registerListenerEvents(callingLink,firstInitialParam,lastInitialParam,additionalParams),document.querySelector(selectors_parentDomNode).addEventListener("shown.bs.dropdown",(()=>{document.querySelector(selectors_pageClickableItem).focus({preventScroll:!0})})),pendingPromise.resolve(),registered=!0};const registerListenerEvents=function(callingLink){let firstInitialParam=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"sifirst",lastInitialParam=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"silast",additionalParams=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[];const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events),events.forEach((event=>{document.addEventListener(event,(e=>{let{firstActive:firstActive,lastActive:lastActive,sifirst:sifirst,silast:silast}=onClickVariables(),itemToReset="";if(e.target.closest(selectors_formDropdown)&&e.preventDefault(),e.target.closest("".concat(selectors_formDropdown," .").concat(selectors_pageListItem))){if(e.target.classList.contains(selectors_pageListItem))return;e.target.closest(selectors_initialBars).classList.contains(selectors_firstInitial)?(sifirst=e.target,itemToReset=firstActive):(silast=e.target,itemToReset=lastActive),swapActiveItems(itemToReset,e)}if(e.target.closest("".concat(selectors_formDropdown))&&e.target.type===selectors_formItems.type){if(e.target.dataset.action===selectors_formItems.save){const params={id:e.target.closest(selectors_formDropdown).dataset.courseid,[firstInitialParam]:sifirst.parentElement.classList.contains("initialbarall")?"":sifirst.value,[lastInitialParam]:silast.parentElement.classList.contains("initialbarall")?"":silast.value};for(const[key,value]of Object.entries(additionalParams))params[key]=value;window.location=Url.relativeUrl(callingLink,params)}e.target.dataset.action===selectors_formItems.cancel&&_dropdown.default.getOrCreateInstance(document.querySelector(".".concat(selectors_targetButton))).toggle()}}))}))},onClickVariables=()=>{const firstItems=[...document.querySelectorAll(".".concat(selectors_firstInitial," li"))],lastItems=[...document.querySelectorAll(".".concat(selectors_lastInitial," li"))],firstActive=firstItems.filter((item=>item.classList.contains(selectors_activeItem)))[0],lastActive=lastItems.filter((item=>item.classList.contains(selectors_activeItem)))[0];let sifirst=firstActive.querySelector(selectors_pageClickableItem),silast=lastActive.querySelector(selectors_pageClickableItem);return{firstActive:firstActive,lastActive:lastActive,sifirst:sifirst,silast:silast}},swapActiveItems=(itemToReset,e)=>{itemToReset.classList.remove(selectors_activeItem),itemToReset.querySelector(selectors_pageClickableItem).ariaCurrent=!1;e.target.parentElement.classList.add(selectors_activeItem),e.target.ariaCurrent=!0}})); //# sourceMappingURL=initials.min.js.map \ No newline at end of file diff --git a/course/amd/build/actionbar/initials.min.js.map b/course/amd/build/actionbar/initials.min.js.map index 9cc36c1bfd94e..10413db8c3a76 100644 --- a/course/amd/build/actionbar/initials.min.js.map +++ b/course/amd/build/actionbar/initials.min.js.map @@ -1 +1 @@ -{"version":3,"file":"initials.min.js","sources":["../../src/actionbar/initials.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 small dropdown to filter users.\n *\n * @module core_course/actionbar/initials\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Pending from 'core/pending';\nimport * as Url from 'core/url';\nimport CustomEvents from \"core/custom_interaction_events\";\nimport $ from 'jquery';\n\n/**\n * Whether the event listener has already been registered for this module.\n *\n * @type {boolean}\n */\nlet registered = false;\n\n// Contain our selectors within this file until they could be of use elsewhere.\nconst selectors = {\n pageListItem: 'page-item',\n pageClickableItem: '.page-link',\n activeItem: 'active',\n formDropdown: '.initialsdropdownform',\n parentDomNode: '.initials-selector',\n firstInitial: 'firstinitial',\n lastInitial: 'lastinitial',\n initialBars: '.initialbar', // Both first and last name use this class.\n targetButton: 'initialswidget',\n formItems: {\n type: 'submit',\n save: 'save',\n cancel: 'cancel'\n }\n};\n\n/**\n * Our initial hook into the module which will eventually allow us to handle the dropdown initials bar form.\n *\n * @param {String} callingLink The link to redirect upon form submission.\n * @param {String} firstInitialParam The URL parameter to set for the first name initial.\n * @param {String} lastInitialParam The URL parameter to set for the last name initial.\n * @param {Array} additionalParams Any additional parameters to set for the URL.\n */\nexport const init = (callingLink, firstInitialParam = 'sifirst',\n lastInitialParam = 'silast', additionalParams = []) => {\n if (registered) {\n return;\n }\n const pendingPromise = new Pending();\n registerListenerEvents(callingLink, firstInitialParam, lastInitialParam, additionalParams);\n // BS events always bubble so, we need to listen for the event higher up the chain.\n $(selectors.parentDomNode).on('shown.bs.dropdown', () => {\n document.querySelector(selectors.pageClickableItem).focus({preventScroll: true});\n });\n pendingPromise.resolve();\n registered = true;\n};\n\n/**\n * Register event listeners.\n *\n * @param {String} callingLink The link to redirect upon form submission.\n * @param {String} firstInitialParam The URL parameter to set for the first name initial.\n * @param {String} lastInitialParam The URL parameter to set for the last name initial.\n * @param {Array} additionalParams Any additional parameters to set for the URL.\n */\nconst registerListenerEvents = (callingLink, firstInitialParam = 'sifirst',\n lastInitialParam = 'silast', additionalParams = []) => {\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n CustomEvents.define(document, events);\n\n // Register events.\n events.forEach((event) => {\n document.addEventListener(event, (e) => {\n // Always fetch the latest information when we click as state is a fickle thing.\n let {firstActive, lastActive, sifirst, silast} = onClickVariables();\n let itemToReset = '';\n\n // Prevent the usual form behaviour.\n if (e.target.closest(selectors.formDropdown)) {\n e.preventDefault();\n }\n\n // Handle the state of active initials before form submission.\n if (e.target.closest(`${selectors.formDropdown} .${selectors.pageListItem}`)) {\n // Ensure the li items don't cause weird clicking emptying out the form.\n if (e.target.classList.contains(selectors.pageListItem)) {\n return;\n }\n\n const initialsBar = e.target.closest(selectors.initialBars); // Find out which initial bar we are in.\n\n // We want to find the current active item in the menu area the user selected.\n // We also want to fetch the raw item out of the array for instant manipulation.\n if (initialsBar.classList.contains(selectors.firstInitial)) {\n sifirst = e.target;\n itemToReset = firstActive;\n } else {\n silast = e.target;\n itemToReset = lastActive;\n }\n swapActiveItems(itemToReset, e);\n }\n\n // Handle form submissions.\n if (e.target.closest(`${selectors.formDropdown}`) && e.target.type === selectors.formItems.type) {\n if (e.target.dataset.action === selectors.formItems.save) {\n // Ensure we strip out the value (All) as it messes with the PHP side of the initials bar.\n // Then we will redirect the user back onto the page with new filters applied.\n const params = {\n 'id': e.target.closest(selectors.formDropdown).dataset.courseid,\n [firstInitialParam]: sifirst.parentElement.classList.contains('initialbarall') ? '' : sifirst.value,\n [lastInitialParam]: silast.parentElement.classList.contains('initialbarall') ? '' : silast.value,\n };\n\n // If additional parameters are passed, add them here (overriding any already set above).\n for (const [key, value] of Object.entries(additionalParams)) {\n params[key] = value;\n }\n window.location = Url.relativeUrl(callingLink, params);\n }\n if (e.target.dataset.action === selectors.formItems.cancel) {\n $(`.${selectors.targetButton}`).dropdown('toggle');\n }\n }\n });\n });\n};\n\n/**\n * A small abstracted helper function which allows us to ensure we have up-to-date lists of nodes.\n *\n * @returns {{firstActive: HTMLElement, lastActive: HTMLElement, sifirst: ?String, silast: ?String}}\n */\nconst onClickVariables = () => {\n // Ensure we have an up-to-date initials bar.\n const firstItems = [...document.querySelectorAll(`.${selectors.firstInitial} li`)];\n const lastItems = [...document.querySelectorAll(`.${selectors.lastInitial} li`)];\n const firstActive = firstItems.filter((item) => item.classList.contains(selectors.activeItem))[0];\n const lastActive = lastItems.filter((item) => item.classList.contains(selectors.activeItem))[0];\n // Ensure we retain both of the selections from a previous instance.\n let sifirst = firstActive.querySelector(selectors.pageClickableItem);\n let silast = lastActive.querySelector(selectors.pageClickableItem);\n return {firstActive, lastActive, sifirst, silast};\n};\n\n/**\n * Given we are provided the old li and current click event, swap around the active properties.\n *\n * @param {HTMLElement} itemToReset\n * @param {Event} e\n */\nconst swapActiveItems = (itemToReset, e) => {\n itemToReset.classList.remove(selectors.activeItem);\n itemToReset.querySelector(selectors.pageClickableItem).ariaCurrent = false;\n\n // Set the select item as the current item.\n const itemToSetActive = e.target.parentElement;\n itemToSetActive.classList.add(selectors.activeItem);\n e.target.ariaCurrent = true;\n};\n"],"names":["registered","selectors","type","save","cancel","callingLink","firstInitialParam","lastInitialParam","additionalParams","pendingPromise","Pending","registerListenerEvents","on","document","querySelector","focus","preventScroll","resolve","events","CustomEvents","activate","keyboardActivate","define","forEach","event","addEventListener","e","firstActive","lastActive","sifirst","silast","onClickVariables","itemToReset","target","closest","preventDefault","classList","contains","swapActiveItems","dataset","action","params","courseid","parentElement","value","key","Object","entries","window","location","Url","relativeUrl","dropdown","firstItems","querySelectorAll","lastItems","filter","item","remove","ariaCurrent","add"],"mappings":";;;;;;;44BAiCIA,YAAa,QAGXC,uBACY,YADZA,4BAEiB,aAFjBA,qBAGU,SAHVA,uBAIY,wBAJZA,wBAKa,qBALbA,uBAMY,eANZA,sBAOW,cAPXA,sBAQW,cARXA,uBASY,iBATZA,oBAUS,CACPC,KAAM,SACNC,KAAM,OACNC,OAAQ,wBAYI,SAACC,iBAAaC,yEAAoB,UAC9CC,wEAAmB,SAAUC,wEAAmB,MAChDR,wBAGES,eAAiB,IAAIC,iBAC3BC,uBAAuBN,YAAaC,kBAAmBC,iBAAkBC,sCAEvEP,yBAAyBW,GAAG,qBAAqB,KAC/CC,SAASC,cAAcb,6BAA6Bc,MAAM,CAACC,eAAe,OAE9EP,eAAeQ,UACfjB,YAAa,SAWXW,uBAAyB,SAACN,iBAAaC,yEAAoB,UACzDC,wEAAmB,SAAUC,wEAAmB,SAC9CU,OAAS,CACX,QACAC,mCAAaD,OAAOE,SACpBD,mCAAaD,OAAOG,qDAEXC,OAAOT,SAAUK,QAG9BA,OAAOK,SAASC,QACZX,SAASY,iBAAiBD,OAAQE,QAE1BC,YAACA,YAADC,WAAcA,WAAdC,QAA0BA,QAA1BC,OAAmCA,QAAUC,mBAC7CC,YAAc,MAGdN,EAAEO,OAAOC,QAAQjC,yBACjByB,EAAES,iBAIFT,EAAEO,OAAOC,kBAAWjC,oCAA2BA,yBAA2B,IAEtEyB,EAAEO,OAAOG,UAAUC,SAASpC,+BAIZyB,EAAEO,OAAOC,QAAQjC,uBAIrBmC,UAAUC,SAASpC,yBAC/B4B,QAAUH,EAAEO,OACZD,YAAcL,cAEdG,OAASJ,EAAEO,OACXD,YAAcJ,YAElBU,gBAAgBN,YAAaN,MAI7BA,EAAEO,OAAOC,kBAAWjC,0BAA6ByB,EAAEO,OAAO/B,OAASD,oBAAoBC,KAAM,IACzFwB,EAAEO,OAAOM,QAAQC,SAAWvC,oBAAoBE,KAAM,OAGhDsC,OAAS,IACLf,EAAEO,OAAOC,QAAQjC,wBAAwBsC,QAAQG,UACtDpC,mBAAoBuB,QAAQc,cAAcP,UAAUC,SAAS,iBAAmB,GAAKR,QAAQe,OAC7FrC,kBAAmBuB,OAAOa,cAAcP,UAAUC,SAAS,iBAAmB,GAAKP,OAAOc,WAI1F,MAAOC,IAAKD,SAAUE,OAAOC,QAAQvC,kBACtCiC,OAAOI,KAAOD,MAElBI,OAAOC,SAAWC,IAAIC,YAAY9C,YAAaoC,QAE/Cf,EAAEO,OAAOM,QAAQC,SAAWvC,oBAAoBG,uCAC1CH,yBAA0BmD,SAAS,kBAYvDrB,iBAAmB,WAEfsB,WAAa,IAAIxC,SAASyC,4BAAqBrD,gCAC/CsD,UAAY,IAAI1C,SAASyC,4BAAqBrD,+BAC9C0B,YAAc0B,WAAWG,QAAQC,MAASA,KAAKrB,UAAUC,SAASpC,wBAAuB,GACzF2B,WAAa2B,UAAUC,QAAQC,MAASA,KAAKrB,UAAUC,SAASpC,wBAAuB,OAEzF4B,QAAUF,YAAYb,cAAcb,6BACpC6B,OAASF,WAAWd,cAAcb,mCAC/B,CAAC0B,YAAAA,YAAaC,WAAAA,WAAYC,QAAAA,QAASC,OAAAA,SASxCQ,gBAAkB,CAACN,YAAaN,KAClCM,YAAYI,UAAUsB,OAAOzD,sBAC7B+B,YAAYlB,cAAcb,6BAA6B0D,aAAc,EAG7CjC,EAAEO,OAAOU,cACjBP,UAAUwB,IAAI3D,sBAC9ByB,EAAEO,OAAO0B,aAAc"} \ No newline at end of file +{"version":3,"file":"initials.min.js","sources":["../../src/actionbar/initials.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 small dropdown to filter users.\n *\n * @module core_course/actionbar/initials\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Pending from 'core/pending';\nimport * as Url from 'core/url';\nimport CustomEvents from \"core/custom_interaction_events\";\nimport Dropdown from 'theme_boost/bootstrap/dropdown';\n\n/**\n * Whether the event listener has already been registered for this module.\n *\n * @type {boolean}\n */\nlet registered = false;\n\n// Contain our selectors within this file until they could be of use elsewhere.\nconst selectors = {\n pageListItem: 'page-item',\n pageClickableItem: '.page-link',\n activeItem: 'active',\n formDropdown: '.initialsdropdownform',\n parentDomNode: '.initials-selector',\n firstInitial: 'firstinitial',\n lastInitial: 'lastinitial',\n initialBars: '.initialbar', // Both first and last name use this class.\n targetButton: 'initialswidget',\n formItems: {\n type: 'submit',\n save: 'save',\n cancel: 'cancel'\n }\n};\n\n/**\n * Our initial hook into the module which will eventually allow us to handle the dropdown initials bar form.\n *\n * @param {String} callingLink The link to redirect upon form submission.\n * @param {String} firstInitialParam The URL parameter to set for the first name initial.\n * @param {String} lastInitialParam The URL parameter to set for the last name initial.\n * @param {Array} additionalParams Any additional parameters to set for the URL.\n */\nexport const init = (callingLink, firstInitialParam = 'sifirst',\n lastInitialParam = 'silast', additionalParams = []) => {\n if (registered) {\n return;\n }\n const pendingPromise = new Pending();\n registerListenerEvents(callingLink, firstInitialParam, lastInitialParam, additionalParams);\n // BS events always bubble so, we need to listen for the event higher up the chain.\n document.querySelector(selectors.parentDomNode).addEventListener('shown.bs.dropdown', () => {\n document.querySelector(selectors.pageClickableItem).focus({preventScroll: true});\n });\n pendingPromise.resolve();\n registered = true;\n};\n\n/**\n * Register event listeners.\n *\n * @param {String} callingLink The link to redirect upon form submission.\n * @param {String} firstInitialParam The URL parameter to set for the first name initial.\n * @param {String} lastInitialParam The URL parameter to set for the last name initial.\n * @param {Array} additionalParams Any additional parameters to set for the URL.\n */\nconst registerListenerEvents = (callingLink, firstInitialParam = 'sifirst',\n lastInitialParam = 'silast', additionalParams = []) => {\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n CustomEvents.define(document, events);\n\n // Register events.\n events.forEach((event) => {\n document.addEventListener(event, (e) => {\n // Always fetch the latest information when we click as state is a fickle thing.\n let {firstActive, lastActive, sifirst, silast} = onClickVariables();\n let itemToReset = '';\n\n // Prevent the usual form behaviour.\n if (e.target.closest(selectors.formDropdown)) {\n e.preventDefault();\n }\n\n // Handle the state of active initials before form submission.\n if (e.target.closest(`${selectors.formDropdown} .${selectors.pageListItem}`)) {\n // Ensure the li items don't cause weird clicking emptying out the form.\n if (e.target.classList.contains(selectors.pageListItem)) {\n return;\n }\n\n const initialsBar = e.target.closest(selectors.initialBars); // Find out which initial bar we are in.\n\n // We want to find the current active item in the menu area the user selected.\n // We also want to fetch the raw item out of the array for instant manipulation.\n if (initialsBar.classList.contains(selectors.firstInitial)) {\n sifirst = e.target;\n itemToReset = firstActive;\n } else {\n silast = e.target;\n itemToReset = lastActive;\n }\n swapActiveItems(itemToReset, e);\n }\n\n // Handle form submissions.\n if (e.target.closest(`${selectors.formDropdown}`) && e.target.type === selectors.formItems.type) {\n if (e.target.dataset.action === selectors.formItems.save) {\n // Ensure we strip out the value (All) as it messes with the PHP side of the initials bar.\n // Then we will redirect the user back onto the page with new filters applied.\n const params = {\n 'id': e.target.closest(selectors.formDropdown).dataset.courseid,\n [firstInitialParam]: sifirst.parentElement.classList.contains('initialbarall') ? '' : sifirst.value,\n [lastInitialParam]: silast.parentElement.classList.contains('initialbarall') ? '' : silast.value,\n };\n\n // If additional parameters are passed, add them here (overriding any already set above).\n for (const [key, value] of Object.entries(additionalParams)) {\n params[key] = value;\n }\n window.location = Url.relativeUrl(callingLink, params);\n }\n if (e.target.dataset.action === selectors.formItems.cancel) {\n Dropdown.getOrCreateInstance(document.querySelector(`.${selectors.targetButton}`)).toggle();\n }\n }\n });\n });\n};\n\n/**\n * A small abstracted helper function which allows us to ensure we have up-to-date lists of nodes.\n *\n * @returns {{firstActive: HTMLElement, lastActive: HTMLElement, sifirst: ?String, silast: ?String}}\n */\nconst onClickVariables = () => {\n // Ensure we have an up-to-date initials bar.\n const firstItems = [...document.querySelectorAll(`.${selectors.firstInitial} li`)];\n const lastItems = [...document.querySelectorAll(`.${selectors.lastInitial} li`)];\n const firstActive = firstItems.filter((item) => item.classList.contains(selectors.activeItem))[0];\n const lastActive = lastItems.filter((item) => item.classList.contains(selectors.activeItem))[0];\n // Ensure we retain both of the selections from a previous instance.\n let sifirst = firstActive.querySelector(selectors.pageClickableItem);\n let silast = lastActive.querySelector(selectors.pageClickableItem);\n return {firstActive, lastActive, sifirst, silast};\n};\n\n/**\n * Given we are provided the old li and current click event, swap around the active properties.\n *\n * @param {HTMLElement} itemToReset\n * @param {Event} e\n */\nconst swapActiveItems = (itemToReset, e) => {\n itemToReset.classList.remove(selectors.activeItem);\n itemToReset.querySelector(selectors.pageClickableItem).ariaCurrent = false;\n\n // Set the select item as the current item.\n const itemToSetActive = e.target.parentElement;\n itemToSetActive.classList.add(selectors.activeItem);\n e.target.ariaCurrent = true;\n};\n"],"names":["registered","selectors","type","save","cancel","callingLink","firstInitialParam","lastInitialParam","additionalParams","pendingPromise","Pending","registerListenerEvents","document","querySelector","addEventListener","focus","preventScroll","resolve","events","CustomEvents","activate","keyboardActivate","define","forEach","event","e","firstActive","lastActive","sifirst","silast","onClickVariables","itemToReset","target","closest","preventDefault","classList","contains","swapActiveItems","dataset","action","params","courseid","parentElement","value","key","Object","entries","window","location","Url","relativeUrl","getOrCreateInstance","toggle","firstItems","querySelectorAll","lastItems","filter","item","remove","ariaCurrent","add"],"mappings":";;;;;;;g5BAiCIA,YAAa,QAGXC,uBACY,YADZA,4BAEiB,aAFjBA,qBAGU,SAHVA,uBAIY,wBAJZA,wBAKa,qBALbA,uBAMY,eANZA,sBAOW,cAPXA,sBAQW,cARXA,uBASY,iBATZA,oBAUS,CACPC,KAAM,SACNC,KAAM,OACNC,OAAQ,wBAYI,SAACC,iBAAaC,yEAAoB,UAC9CC,wEAAmB,SAAUC,wEAAmB,MAChDR,wBAGES,eAAiB,IAAIC,iBAC3BC,uBAAuBN,YAAaC,kBAAmBC,iBAAkBC,kBAEzEI,SAASC,cAAcZ,yBAAyBa,iBAAiB,qBAAqB,KAClFF,SAASC,cAAcZ,6BAA6Bc,MAAM,CAACC,eAAe,OAE9EP,eAAeQ,UACfjB,YAAa,SAWXW,uBAAyB,SAACN,iBAAaC,yEAAoB,UACzDC,wEAAmB,SAAUC,wEAAmB,SAC9CU,OAAS,CACX,QACAC,mCAAaD,OAAOE,SACpBD,mCAAaD,OAAOG,qDAEXC,OAAOV,SAAUM,QAG9BA,OAAOK,SAASC,QACZZ,SAASE,iBAAiBU,OAAQC,QAE1BC,YAACA,YAADC,WAAcA,WAAdC,QAA0BA,QAA1BC,OAAmCA,QAAUC,mBAC7CC,YAAc,MAGdN,EAAEO,OAAOC,QAAQhC,yBACjBwB,EAAES,iBAIFT,EAAEO,OAAOC,kBAAWhC,oCAA2BA,yBAA2B,IAEtEwB,EAAEO,OAAOG,UAAUC,SAASnC,+BAIZwB,EAAEO,OAAOC,QAAQhC,uBAIrBkC,UAAUC,SAASnC,yBAC/B2B,QAAUH,EAAEO,OACZD,YAAcL,cAEdG,OAASJ,EAAEO,OACXD,YAAcJ,YAElBU,gBAAgBN,YAAaN,MAI7BA,EAAEO,OAAOC,kBAAWhC,0BAA6BwB,EAAEO,OAAO9B,OAASD,oBAAoBC,KAAM,IACzFuB,EAAEO,OAAOM,QAAQC,SAAWtC,oBAAoBE,KAAM,OAGhDqC,OAAS,IACLf,EAAEO,OAAOC,QAAQhC,wBAAwBqC,QAAQG,UACtDnC,mBAAoBsB,QAAQc,cAAcP,UAAUC,SAAS,iBAAmB,GAAKR,QAAQe,OAC7FpC,kBAAmBsB,OAAOa,cAAcP,UAAUC,SAAS,iBAAmB,GAAKP,OAAOc,WAI1F,MAAOC,IAAKD,SAAUE,OAAOC,QAAQtC,kBACtCgC,OAAOI,KAAOD,MAElBI,OAAOC,SAAWC,IAAIC,YAAY7C,YAAamC,QAE/Cf,EAAEO,OAAOM,QAAQC,SAAWtC,oBAAoBG,0BACvC+C,oBAAoBvC,SAASC,yBAAkBZ,0BAA2BmD,iBAYjGtB,iBAAmB,WAEfuB,WAAa,IAAIzC,SAAS0C,4BAAqBrD,gCAC/CsD,UAAY,IAAI3C,SAAS0C,4BAAqBrD,+BAC9CyB,YAAc2B,WAAWG,QAAQC,MAASA,KAAKtB,UAAUC,SAASnC,wBAAuB,GACzF0B,WAAa4B,UAAUC,QAAQC,MAASA,KAAKtB,UAAUC,SAASnC,wBAAuB,OAEzF2B,QAAUF,YAAYb,cAAcZ,6BACpC4B,OAASF,WAAWd,cAAcZ,mCAC/B,CAACyB,YAAAA,YAAaC,WAAAA,WAAYC,QAAAA,QAASC,OAAAA,SASxCQ,gBAAkB,CAACN,YAAaN,KAClCM,YAAYI,UAAUuB,OAAOzD,sBAC7B8B,YAAYlB,cAAcZ,6BAA6B0D,aAAc,EAG7ClC,EAAEO,OAAOU,cACjBP,UAAUyB,IAAI3D,sBAC9BwB,EAAEO,OAAO2B,aAAc"} \ No newline at end of file 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/actionbar/initials.js b/course/amd/src/actionbar/initials.js index e48bf533a9072..37a5504432c36 100644 --- a/course/amd/src/actionbar/initials.js +++ b/course/amd/src/actionbar/initials.js @@ -24,7 +24,7 @@ import Pending from 'core/pending'; import * as Url from 'core/url'; import CustomEvents from "core/custom_interaction_events"; -import $ from 'jquery'; +import Dropdown from 'theme_boost/bootstrap/dropdown'; /** * Whether the event listener has already been registered for this module. @@ -67,7 +67,7 @@ export const init = (callingLink, firstInitialParam = 'sifirst', const pendingPromise = new Pending(); registerListenerEvents(callingLink, firstInitialParam, lastInitialParam, additionalParams); // BS events always bubble so, we need to listen for the event higher up the chain. - $(selectors.parentDomNode).on('shown.bs.dropdown', () => { + document.querySelector(selectors.parentDomNode).addEventListener('shown.bs.dropdown', () => { document.querySelector(selectors.pageClickableItem).focus({preventScroll: true}); }); pendingPromise.resolve(); @@ -142,7 +142,7 @@ const registerListenerEvents = (callingLink, firstInitialParam = 'sifirst', window.location = Url.relativeUrl(callingLink, params); } if (e.target.dataset.action === selectors.formItems.cancel) { - $(`.${selectors.targetButton}`).dropdown('toggle'); + Dropdown.getOrCreateInstance(document.querySelector(`.${selectors.targetButton}`)).toggle(); } } }); 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":"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"} \ 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/grade/amd/build/comboboxsearch/grade.min.js b/grade/amd/build/comboboxsearch/grade.min.js index 3fd01ec603281..d557a57438736 100644 --- a/grade/amd/build/comboboxsearch/grade.min.js +++ b/grade/amd/build/comboboxsearch/grade.min.js @@ -1,3 +1,3 @@ -define("core_grades/comboboxsearch/grade",["exports","core/comboboxsearch/search_combobox","core_grades/searchwidget/repository","core/templates","core/utils"],(function(_exports,_search_combobox,Repository,_templates,_utils){var 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)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_search_combobox=(obj=_search_combobox)&&obj.__esModule?obj:{default:obj},Repository=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}(Repository);class GradeItemSearch extends _search_combobox.default{constructor(){super(),function(obj,key,value){key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}(this,"courseID",void 0),this.selectors={...this.selectors,courseid:'[data-region="courseid"]',placeholder:'.gradesearchdropdown [data-region="searchplaceholder"]'};const component=document.querySelector(this.componentSelector());this.courseID=component.querySelector(this.selectors.courseid).dataset.courseid,this.instance=this.component.querySelector(this.selectors.instance).dataset.instance;const searchValueElement=this.component.querySelector("#".concat(this.searchInput.dataset.inputElement));searchValueElement.addEventListener("change",(()=>{this.toggleDropdown();const valueElement=this.component.querySelector("#".concat(this.combobox.dataset.inputElement));valueElement.value!==searchValueElement.value&&(valueElement.value=searchValueElement.value,valueElement.dispatchEvent(new Event("change",{bubbles:!0}))),searchValueElement.value=""})),this.$component.on("hide.bs.dropdown",(()=>{this.searchInput.removeAttribute("aria-activedescendant");const listbox=document.querySelector("#".concat(this.searchInput.getAttribute("aria-controls"),'[role="listbox"]'));listbox.querySelectorAll('.active[role="option"]').forEach((option=>{option.classList.remove("active")})),listbox.scrollTop=0,setTimeout((()=>{""!==this.searchInput.value&&(this.searchInput.value="",this.searchInput.dispatchEvent(new Event("input",{bubbles:!0})))}))})),this.renderDefault()}static init(){return new GradeItemSearch}componentSelector(){return".grade-search"}dropdownSelector(){return".gradesearchdropdown"}async renderDropdown(){const{html:html,js:js}=await(0,_templates.renderForPromise)("core/local/comboboxsearch/resultset",{instance:this.instance,results:this.getMatchedResults(),hasresults:this.getMatchedResults().length>0,searchterm:this.getSearchTerm()});(0,_templates.replaceNodeContents)(this.selectors.placeholder,html,js),this.searchInput.removeAttribute("aria-activedescendant")}async renderDefault(){this.setMatchedResults(await this.filterDataset(await this.getDataset())),this.filterMatchDataset(),await this.renderDropdown(),this.updateNodes(),this.registerInputEvents()}async fetchDataset(){return await Repository.gradeitemFetch(this.courseID).then((r=>r.gradeitems))}async filterDataset(filterableData){return""===this.getPreppedSearchTerm()?filterableData:filterableData.filter((grade=>Object.keys(grade).some((key=>""!==grade[key]&&grade[key].toString().toLowerCase().includes(this.getPreppedSearchTerm())))))}filterMatchDataset(){this.setMatchedResults(this.getMatchedResults().map((grade=>({id:grade.id,name:grade.name}))))}registerInputEvents(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{this.setSearchTerms(this.searchInput.value),""===this.searchInput.value?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none"),await this.filterrenderpipe()}),300))}async clickHandler(e){e.target.closest(this.selectors.clearSearch)&&(e.stopPropagation(),this.searchInput.value="",this.setSearchTerms(this.searchInput.value),this.searchInput.focus(),this.clearSearchButton.classList.add("d-none"),await this.filterrenderpipe())}changeHandler(e){window.location=this.selectOneLink(e.target.value)}registerInputHandlers(){this.searchInput.addEventListener("input",(0,_utils.debounce)((()=>{this.setSearchTerms(this.searchInput.value),""===this.getSearchTerm()?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none")}),300))}selectOneLink(gradeID){throw new Error("selectOneLink(".concat(gradeID,") must be implemented in ").concat(this.constructor.name))}}return _exports.default=GradeItemSearch,_exports.default})); +define("core_grades/comboboxsearch/grade",["exports","core/comboboxsearch/search_combobox","core_grades/searchwidget/repository","core/templates","core/utils"],(function(_exports,_search_combobox,Repository,_templates,_utils){var 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)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_search_combobox=(obj=_search_combobox)&&obj.__esModule?obj:{default:obj},Repository=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}(Repository);class GradeItemSearch extends _search_combobox.default{constructor(){super(),function(obj,key,value){key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}(this,"courseID",void 0),this.selectors={...this.selectors,courseid:'[data-region="courseid"]',placeholder:'.gradesearchdropdown [data-region="searchplaceholder"]'};const component=document.querySelector(this.componentSelector());this.courseID=component.querySelector(this.selectors.courseid).dataset.courseid,this.instance=this.component.querySelector(this.selectors.instance).dataset.instance;const searchValueElement=this.component.querySelector("#".concat(this.searchInput.dataset.inputElement));searchValueElement.addEventListener("change",(()=>{this.toggleDropdown();const valueElement=this.component.querySelector("#".concat(this.combobox.dataset.inputElement));valueElement.value!==searchValueElement.value&&(valueElement.value=searchValueElement.value,valueElement.dispatchEvent(new Event("change",{bubbles:!0}))),searchValueElement.value=""})),this.component.addEventListener("hide.bs.dropdown",(()=>{this.searchInput.removeAttribute("aria-activedescendant");const listbox=document.querySelector("#".concat(this.searchInput.getAttribute("aria-controls"),'[role="listbox"]'));listbox.querySelectorAll('.active[role="option"]').forEach((option=>{option.classList.remove("active")})),listbox.scrollTop=0,setTimeout((()=>{""!==this.searchInput.value&&(this.searchInput.value="",this.searchInput.dispatchEvent(new Event("input",{bubbles:!0})))}))})),this.renderDefault()}static init(){return new GradeItemSearch}componentSelector(){return".grade-search"}dropdownSelector(){return".gradesearchdropdown"}async renderDropdown(){const{html:html,js:js}=await(0,_templates.renderForPromise)("core/local/comboboxsearch/resultset",{instance:this.instance,results:this.getMatchedResults(),hasresults:this.getMatchedResults().length>0,searchterm:this.getSearchTerm()});(0,_templates.replaceNodeContents)(this.selectors.placeholder,html,js),this.searchInput.removeAttribute("aria-activedescendant")}async renderDefault(){this.setMatchedResults(await this.filterDataset(await this.getDataset())),this.filterMatchDataset(),await this.renderDropdown(),this.updateNodes(),this.registerInputEvents()}async fetchDataset(){return await Repository.gradeitemFetch(this.courseID).then((r=>r.gradeitems))}async filterDataset(filterableData){return""===this.getPreppedSearchTerm()?filterableData:filterableData.filter((grade=>Object.keys(grade).some((key=>""!==grade[key]&&grade[key].toString().toLowerCase().includes(this.getPreppedSearchTerm())))))}filterMatchDataset(){this.setMatchedResults(this.getMatchedResults().map((grade=>({id:grade.id,name:grade.name}))))}registerInputEvents(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{this.setSearchTerms(this.searchInput.value),""===this.searchInput.value?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none"),await this.filterrenderpipe()}),300))}async clickHandler(e){e.target.closest(this.selectors.clearSearch)&&(e.stopPropagation(),this.searchInput.value="",this.setSearchTerms(this.searchInput.value),this.searchInput.focus(),this.clearSearchButton.classList.add("d-none"),await this.filterrenderpipe())}changeHandler(e){window.location=this.selectOneLink(e.target.value)}registerInputHandlers(){this.searchInput.addEventListener("input",(0,_utils.debounce)((()=>{this.setSearchTerms(this.searchInput.value),""===this.getSearchTerm()?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none")}),300))}selectOneLink(gradeID){throw new Error("selectOneLink(".concat(gradeID,") must be implemented in ").concat(this.constructor.name))}}return _exports.default=GradeItemSearch,_exports.default})); //# sourceMappingURL=grade.min.js.map \ No newline at end of file diff --git a/grade/amd/build/comboboxsearch/grade.min.js.map b/grade/amd/build/comboboxsearch/grade.min.js.map index 9b0ed4097ed61..c00f5ab29be76 100644 --- a/grade/amd/build/comboboxsearch/grade.min.js.map +++ b/grade/amd/build/comboboxsearch/grade.min.js.map @@ -1 +1 @@ -{"version":3,"file":"grade.min.js","sources":["../../src/comboboxsearch/grade.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 * Allow the user to search for grades within the grade area.\n *\n * @module core_grades/comboboxsearch/grade\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport search_combobox from 'core/comboboxsearch/search_combobox';\nimport * as Repository from 'core_grades/searchwidget/repository';\nimport {renderForPromise, replaceNodeContents} from 'core/templates';\nimport {debounce} from 'core/utils';\n\nexport default class GradeItemSearch extends search_combobox {\n\n courseID;\n\n constructor() {\n super();\n\n // Define our standard lookups.\n this.selectors = {\n ...this.selectors,\n courseid: '[data-region=\"courseid\"]',\n placeholder: '.gradesearchdropdown [data-region=\"searchplaceholder\"]',\n };\n const component = document.querySelector(this.componentSelector());\n this.courseID = component.querySelector(this.selectors.courseid).dataset.courseid;\n this.instance = this.component.querySelector(this.selectors.instance).dataset.instance;\n\n const searchValueElement = this.component.querySelector(`#${this.searchInput.dataset.inputElement}`);\n searchValueElement.addEventListener('change', () => {\n this.toggleDropdown(); // Otherwise the dropdown stays open when user choose an option using keyboard.\n\n const valueElement = this.component.querySelector(`#${this.combobox.dataset.inputElement}`);\n if (valueElement.value !== searchValueElement.value) {\n valueElement.value = searchValueElement.value;\n valueElement.dispatchEvent(new Event('change', {bubbles: true}));\n }\n\n searchValueElement.value = '';\n });\n\n this.$component.on('hide.bs.dropdown', () => {\n this.searchInput.removeAttribute('aria-activedescendant');\n\n const listbox = document.querySelector(`#${this.searchInput.getAttribute('aria-controls')}[role=\"listbox\"]`);\n listbox.querySelectorAll('.active[role=\"option\"]').forEach(option => {\n option.classList.remove('active');\n });\n listbox.scrollTop = 0;\n\n // Use setTimeout to make sure the following code is executed after the click event is handled.\n setTimeout(() => {\n if (this.searchInput.value !== '') {\n this.searchInput.value = '';\n this.searchInput.dispatchEvent(new Event('input', {bubbles: true}));\n }\n });\n });\n\n this.renderDefault();\n }\n\n static init() {\n return new GradeItemSearch();\n }\n\n /**\n * The overall div that contains the searching widget.\n *\n * @returns {string}\n */\n componentSelector() {\n return '.grade-search';\n }\n\n /**\n * The dropdown div that contains the searching widget result space.\n *\n * @returns {string}\n */\n dropdownSelector() {\n return '.gradesearchdropdown';\n }\n\n /**\n * Build the content then replace the node.\n */\n async renderDropdown() {\n const {html, js} = await renderForPromise('core/local/comboboxsearch/resultset', {\n instance: this.instance,\n results: this.getMatchedResults(),\n hasresults: this.getMatchedResults().length > 0,\n searchterm: this.getSearchTerm(),\n });\n replaceNodeContents(this.selectors.placeholder, html, js);\n // Remove aria-activedescendant when the available options change.\n this.searchInput.removeAttribute('aria-activedescendant');\n }\n\n /**\n * Build the content then replace the node by default we want our form to exist.\n */\n async renderDefault() {\n this.setMatchedResults(await this.filterDataset(await this.getDataset()));\n this.filterMatchDataset();\n\n await this.renderDropdown();\n\n this.updateNodes();\n this.registerInputEvents();\n }\n\n /**\n * Get the data we will be searching against in this component.\n *\n * @returns {Promise<*>}\n */\n async fetchDataset() {\n return await Repository.gradeitemFetch(this.courseID).then((r) => r.gradeitems);\n }\n\n /**\n * Dictate to the search component how and what we want to match upon.\n *\n * @param {Array} filterableData\n * @returns {Array} The users that match the given criteria.\n */\n async filterDataset(filterableData) {\n // Sometimes we just want to show everything.\n if (this.getPreppedSearchTerm() === '') {\n return filterableData;\n }\n return filterableData.filter((grade) => Object.keys(grade).some((key) => {\n if (grade[key] === \"\") {\n return false;\n }\n return grade[key].toString().toLowerCase().includes(this.getPreppedSearchTerm());\n }));\n }\n\n /**\n * Given we have a subset of the dataset, set the field that we matched upon to inform the end user.\n */\n filterMatchDataset() {\n this.setMatchedResults(\n this.getMatchedResults().map((grade) => {\n return {\n id: grade.id,\n name: grade.name,\n };\n })\n );\n }\n\n /**\n * Handle any keyboard inputs.\n */\n registerInputEvents() {\n // Register & handle the text input.\n this.searchInput.addEventListener('input', debounce(async() => {\n this.setSearchTerms(this.searchInput.value);\n // We can also require a set amount of input before search.\n if (this.searchInput.value === '') {\n // Hide the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.add('d-none');\n } else {\n // Display the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.remove('d-none');\n }\n // User has given something for us to filter against.\n await this.filterrenderpipe();\n }, 300));\n }\n\n /**\n * The handler for when a user interacts with the component.\n *\n * @param {MouseEvent} e The triggering event that we are working with.\n */\n async clickHandler(e) {\n if (e.target.closest(this.selectors.clearSearch)) {\n e.stopPropagation();\n // Clear the entered search query in the search bar.\n this.searchInput.value = '';\n this.setSearchTerms(this.searchInput.value);\n this.searchInput.focus();\n this.clearSearchButton.classList.add('d-none');\n // Display results.\n await this.filterrenderpipe();\n }\n }\n\n /**\n * The handler for when a user changes the value of the component (selects an option from the dropdown).\n *\n * @param {Event} e The change event.\n */\n changeHandler(e) {\n window.location = this.selectOneLink(e.target.value);\n }\n\n /**\n * Override the input event listener for the text input area.\n */\n registerInputHandlers() {\n // Register & handle the text input.\n this.searchInput.addEventListener('input', debounce(() => {\n this.setSearchTerms(this.searchInput.value);\n // We can also require a set amount of input before search.\n if (this.getSearchTerm() === '') {\n // Hide the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.add('d-none');\n } else {\n // Display the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.remove('d-none');\n }\n }, 300));\n }\n\n /**\n * Build up the view all link that is dedicated to a particular result.\n * We will call this function when a user interacts with the combobox to redirect them to show their results in the page.\n *\n * @param {Number} gradeID The ID of the grade item selected.\n */\n selectOneLink(gradeID) {\n throw new Error(`selectOneLink(${gradeID}) must be implemented in ${this.constructor.name}`);\n }\n}\n"],"names":["GradeItemSearch","search_combobox","constructor","selectors","this","courseid","placeholder","component","document","querySelector","componentSelector","courseID","dataset","instance","searchValueElement","searchInput","inputElement","addEventListener","toggleDropdown","valueElement","combobox","value","dispatchEvent","Event","bubbles","$component","on","removeAttribute","listbox","getAttribute","querySelectorAll","forEach","option","classList","remove","scrollTop","setTimeout","renderDefault","dropdownSelector","html","js","results","getMatchedResults","hasresults","length","searchterm","getSearchTerm","setMatchedResults","filterDataset","getDataset","filterMatchDataset","renderDropdown","updateNodes","registerInputEvents","Repository","gradeitemFetch","then","r","gradeitems","filterableData","getPreppedSearchTerm","filter","grade","Object","keys","some","key","toString","toLowerCase","includes","map","id","name","async","setSearchTerms","clearSearchButton","add","filterrenderpipe","e","target","closest","clearSearch","stopPropagation","focus","changeHandler","window","location","selectOneLink","registerInputHandlers","gradeID","Error"],"mappings":"i0CA2BqBA,wBAAwBC,yBAIzCC,6LAISC,UAAY,IACVC,KAAKD,UACRE,SAAU,2BACVC,YAAa,gEAEXC,UAAYC,SAASC,cAAcL,KAAKM,0BACzCC,SAAWJ,UAAUE,cAAcL,KAAKD,UAAUE,UAAUO,QAAQP,cACpEQ,SAAWT,KAAKG,UAAUE,cAAcL,KAAKD,UAAUU,UAAUD,QAAQC,eAExEC,mBAAqBV,KAAKG,UAAUE,yBAAkBL,KAAKW,YAAYH,QAAQI,eACrFF,mBAAmBG,iBAAiB,UAAU,UACrCC,uBAECC,aAAef,KAAKG,UAAUE,yBAAkBL,KAAKgB,SAASR,QAAQI,eACxEG,aAAaE,QAAUP,mBAAmBO,QAC1CF,aAAaE,MAAQP,mBAAmBO,MACxCF,aAAaG,cAAc,IAAIC,MAAM,SAAU,CAACC,SAAS,MAG7DV,mBAAmBO,MAAQ,WAG1BI,WAAWC,GAAG,oBAAoB,UAC9BX,YAAYY,gBAAgB,+BAE3BC,QAAUpB,SAASC,yBAAkBL,KAAKW,YAAYc,aAAa,sCACzED,QAAQE,iBAAiB,0BAA0BC,SAAQC,SACvDA,OAAOC,UAAUC,OAAO,aAE5BN,QAAQO,UAAY,EAGpBC,YAAW,KACwB,KAA3BhC,KAAKW,YAAYM,aACZN,YAAYM,MAAQ,QACpBN,YAAYO,cAAc,IAAIC,MAAM,QAAS,CAACC,SAAS,iBAKnEa,qCAIE,IAAIrC,gBAQfU,0BACW,gBAQX4B,yBACW,oDAODC,KAACA,KAADC,GAAOA,UAAY,+BAAiB,sCAAuC,CAC7E3B,SAAUT,KAAKS,SACf4B,QAASrC,KAAKsC,oBACdC,WAAYvC,KAAKsC,oBAAoBE,OAAS,EAC9CC,WAAYzC,KAAK0C,qDAED1C,KAAKD,UAAUG,YAAaiC,KAAMC,SAEjDzB,YAAYY,gBAAgB,oDAO5BoB,wBAAwB3C,KAAK4C,oBAAoB5C,KAAK6C,oBACtDC,2BAEC9C,KAAK+C,sBAENC,mBACAC,wDASQC,WAAWC,eAAenD,KAAKO,UAAU6C,MAAMC,GAAMA,EAAEC,iCASpDC,sBAEoB,KAAhCvD,KAAKwD,uBACED,eAEJA,eAAeE,QAAQC,OAAUC,OAAOC,KAAKF,OAAOG,MAAMC,KAC1C,KAAfJ,MAAMI,MAGHJ,MAAMI,KAAKC,WAAWC,cAAcC,SAASjE,KAAKwD,4BAOjEV,0BACSH,kBACD3C,KAAKsC,oBAAoB4B,KAAKR,QACnB,CACHS,GAAIT,MAAMS,GACVC,KAAMV,MAAMU,UAS5BnB,2BAEStC,YAAYE,iBAAiB,SAAS,oBAASwD,eAC3CC,eAAetE,KAAKW,YAAYM,OAEN,KAA3BjB,KAAKW,YAAYM,WAEZsD,kBAAkB1C,UAAU2C,IAAI,eAGhCD,kBAAkB1C,UAAUC,OAAO,gBAGtC9B,KAAKyE,qBACZ,yBAQYC,GACXA,EAAEC,OAAOC,QAAQ5E,KAAKD,UAAU8E,eAChCH,EAAEI,uBAEGnE,YAAYM,MAAQ,QACpBqD,eAAetE,KAAKW,YAAYM,YAChCN,YAAYoE,aACZR,kBAAkB1C,UAAU2C,IAAI,gBAE/BxE,KAAKyE,oBASnBO,cAAcN,GACVO,OAAOC,SAAWlF,KAAKmF,cAAcT,EAAEC,OAAO1D,OAMlDmE,6BAESzE,YAAYE,iBAAiB,SAAS,oBAAS,UAC3CyD,eAAetE,KAAKW,YAAYM,OAER,KAAzBjB,KAAK0C,qBAEA6B,kBAAkB1C,UAAU2C,IAAI,eAGhCD,kBAAkB1C,UAAUC,OAAO,YAE7C,MASPqD,cAAcE,eACJ,IAAIC,8BAAuBD,4CAAmCrF,KAAKF,YAAYsE"} \ No newline at end of file +{"version":3,"file":"grade.min.js","sources":["../../src/comboboxsearch/grade.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 * Allow the user to search for grades within the grade area.\n *\n * @module core_grades/comboboxsearch/grade\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport search_combobox from 'core/comboboxsearch/search_combobox';\nimport * as Repository from 'core_grades/searchwidget/repository';\nimport {renderForPromise, replaceNodeContents} from 'core/templates';\nimport {debounce} from 'core/utils';\n\nexport default class GradeItemSearch extends search_combobox {\n\n courseID;\n\n constructor() {\n super();\n\n // Define our standard lookups.\n this.selectors = {\n ...this.selectors,\n courseid: '[data-region=\"courseid\"]',\n placeholder: '.gradesearchdropdown [data-region=\"searchplaceholder\"]',\n };\n const component = document.querySelector(this.componentSelector());\n this.courseID = component.querySelector(this.selectors.courseid).dataset.courseid;\n this.instance = this.component.querySelector(this.selectors.instance).dataset.instance;\n\n const searchValueElement = this.component.querySelector(`#${this.searchInput.dataset.inputElement}`);\n searchValueElement.addEventListener('change', () => {\n this.toggleDropdown(); // Otherwise the dropdown stays open when user choose an option using keyboard.\n\n const valueElement = this.component.querySelector(`#${this.combobox.dataset.inputElement}`);\n if (valueElement.value !== searchValueElement.value) {\n valueElement.value = searchValueElement.value;\n valueElement.dispatchEvent(new Event('change', {bubbles: true}));\n }\n\n searchValueElement.value = '';\n });\n\n this.component.addEventListener('hide.bs.dropdown', () => {\n this.searchInput.removeAttribute('aria-activedescendant');\n\n const listbox = document.querySelector(`#${this.searchInput.getAttribute('aria-controls')}[role=\"listbox\"]`);\n listbox.querySelectorAll('.active[role=\"option\"]').forEach(option => {\n option.classList.remove('active');\n });\n listbox.scrollTop = 0;\n\n // Use setTimeout to make sure the following code is executed after the click event is handled.\n setTimeout(() => {\n if (this.searchInput.value !== '') {\n this.searchInput.value = '';\n this.searchInput.dispatchEvent(new Event('input', {bubbles: true}));\n }\n });\n });\n\n this.renderDefault();\n }\n\n static init() {\n return new GradeItemSearch();\n }\n\n /**\n * The overall div that contains the searching widget.\n *\n * @returns {string}\n */\n componentSelector() {\n return '.grade-search';\n }\n\n /**\n * The dropdown div that contains the searching widget result space.\n *\n * @returns {string}\n */\n dropdownSelector() {\n return '.gradesearchdropdown';\n }\n\n /**\n * Build the content then replace the node.\n */\n async renderDropdown() {\n const {html, js} = await renderForPromise('core/local/comboboxsearch/resultset', {\n instance: this.instance,\n results: this.getMatchedResults(),\n hasresults: this.getMatchedResults().length > 0,\n searchterm: this.getSearchTerm(),\n });\n replaceNodeContents(this.selectors.placeholder, html, js);\n // Remove aria-activedescendant when the available options change.\n this.searchInput.removeAttribute('aria-activedescendant');\n }\n\n /**\n * Build the content then replace the node by default we want our form to exist.\n */\n async renderDefault() {\n this.setMatchedResults(await this.filterDataset(await this.getDataset()));\n this.filterMatchDataset();\n\n await this.renderDropdown();\n\n this.updateNodes();\n this.registerInputEvents();\n }\n\n /**\n * Get the data we will be searching against in this component.\n *\n * @returns {Promise<*>}\n */\n async fetchDataset() {\n return await Repository.gradeitemFetch(this.courseID).then((r) => r.gradeitems);\n }\n\n /**\n * Dictate to the search component how and what we want to match upon.\n *\n * @param {Array} filterableData\n * @returns {Array} The users that match the given criteria.\n */\n async filterDataset(filterableData) {\n // Sometimes we just want to show everything.\n if (this.getPreppedSearchTerm() === '') {\n return filterableData;\n }\n return filterableData.filter((grade) => Object.keys(grade).some((key) => {\n if (grade[key] === \"\") {\n return false;\n }\n return grade[key].toString().toLowerCase().includes(this.getPreppedSearchTerm());\n }));\n }\n\n /**\n * Given we have a subset of the dataset, set the field that we matched upon to inform the end user.\n */\n filterMatchDataset() {\n this.setMatchedResults(\n this.getMatchedResults().map((grade) => {\n return {\n id: grade.id,\n name: grade.name,\n };\n })\n );\n }\n\n /**\n * Handle any keyboard inputs.\n */\n registerInputEvents() {\n // Register & handle the text input.\n this.searchInput.addEventListener('input', debounce(async() => {\n this.setSearchTerms(this.searchInput.value);\n // We can also require a set amount of input before search.\n if (this.searchInput.value === '') {\n // Hide the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.add('d-none');\n } else {\n // Display the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.remove('d-none');\n }\n // User has given something for us to filter against.\n await this.filterrenderpipe();\n }, 300));\n }\n\n /**\n * The handler for when a user interacts with the component.\n *\n * @param {MouseEvent} e The triggering event that we are working with.\n */\n async clickHandler(e) {\n if (e.target.closest(this.selectors.clearSearch)) {\n e.stopPropagation();\n // Clear the entered search query in the search bar.\n this.searchInput.value = '';\n this.setSearchTerms(this.searchInput.value);\n this.searchInput.focus();\n this.clearSearchButton.classList.add('d-none');\n // Display results.\n await this.filterrenderpipe();\n }\n }\n\n /**\n * The handler for when a user changes the value of the component (selects an option from the dropdown).\n *\n * @param {Event} e The change event.\n */\n changeHandler(e) {\n window.location = this.selectOneLink(e.target.value);\n }\n\n /**\n * Override the input event listener for the text input area.\n */\n registerInputHandlers() {\n // Register & handle the text input.\n this.searchInput.addEventListener('input', debounce(() => {\n this.setSearchTerms(this.searchInput.value);\n // We can also require a set amount of input before search.\n if (this.getSearchTerm() === '') {\n // Hide the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.add('d-none');\n } else {\n // Display the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.remove('d-none');\n }\n }, 300));\n }\n\n /**\n * Build up the view all link that is dedicated to a particular result.\n * We will call this function when a user interacts with the combobox to redirect them to show their results in the page.\n *\n * @param {Number} gradeID The ID of the grade item selected.\n */\n selectOneLink(gradeID) {\n throw new Error(`selectOneLink(${gradeID}) must be implemented in ${this.constructor.name}`);\n }\n}\n"],"names":["GradeItemSearch","search_combobox","constructor","selectors","this","courseid","placeholder","component","document","querySelector","componentSelector","courseID","dataset","instance","searchValueElement","searchInput","inputElement","addEventListener","toggleDropdown","valueElement","combobox","value","dispatchEvent","Event","bubbles","removeAttribute","listbox","getAttribute","querySelectorAll","forEach","option","classList","remove","scrollTop","setTimeout","renderDefault","dropdownSelector","html","js","results","getMatchedResults","hasresults","length","searchterm","getSearchTerm","setMatchedResults","filterDataset","getDataset","filterMatchDataset","renderDropdown","updateNodes","registerInputEvents","Repository","gradeitemFetch","then","r","gradeitems","filterableData","getPreppedSearchTerm","filter","grade","Object","keys","some","key","toString","toLowerCase","includes","map","id","name","async","setSearchTerms","clearSearchButton","add","filterrenderpipe","e","target","closest","clearSearch","stopPropagation","focus","changeHandler","window","location","selectOneLink","registerInputHandlers","gradeID","Error"],"mappings":"i0CA2BqBA,wBAAwBC,yBAIzCC,6LAISC,UAAY,IACVC,KAAKD,UACRE,SAAU,2BACVC,YAAa,gEAEXC,UAAYC,SAASC,cAAcL,KAAKM,0BACzCC,SAAWJ,UAAUE,cAAcL,KAAKD,UAAUE,UAAUO,QAAQP,cACpEQ,SAAWT,KAAKG,UAAUE,cAAcL,KAAKD,UAAUU,UAAUD,QAAQC,eAExEC,mBAAqBV,KAAKG,UAAUE,yBAAkBL,KAAKW,YAAYH,QAAQI,eACrFF,mBAAmBG,iBAAiB,UAAU,UACrCC,uBAECC,aAAef,KAAKG,UAAUE,yBAAkBL,KAAKgB,SAASR,QAAQI,eACxEG,aAAaE,QAAUP,mBAAmBO,QAC1CF,aAAaE,MAAQP,mBAAmBO,MACxCF,aAAaG,cAAc,IAAIC,MAAM,SAAU,CAACC,SAAS,MAG7DV,mBAAmBO,MAAQ,WAG1Bd,UAAUU,iBAAiB,oBAAoB,UAC3CF,YAAYU,gBAAgB,+BAE3BC,QAAUlB,SAASC,yBAAkBL,KAAKW,YAAYY,aAAa,sCACzED,QAAQE,iBAAiB,0BAA0BC,SAAQC,SACvDA,OAAOC,UAAUC,OAAO,aAE5BN,QAAQO,UAAY,EAGpBC,YAAW,KACwB,KAA3B9B,KAAKW,YAAYM,aACZN,YAAYM,MAAQ,QACpBN,YAAYO,cAAc,IAAIC,MAAM,QAAS,CAACC,SAAS,iBAKnEW,qCAIE,IAAInC,gBAQfU,0BACW,gBAQX0B,yBACW,oDAODC,KAACA,KAADC,GAAOA,UAAY,+BAAiB,sCAAuC,CAC7EzB,SAAUT,KAAKS,SACf0B,QAASnC,KAAKoC,oBACdC,WAAYrC,KAAKoC,oBAAoBE,OAAS,EAC9CC,WAAYvC,KAAKwC,qDAEDxC,KAAKD,UAAUG,YAAa+B,KAAMC,SAEjDvB,YAAYU,gBAAgB,oDAO5BoB,wBAAwBzC,KAAK0C,oBAAoB1C,KAAK2C,oBACtDC,2BAEC5C,KAAK6C,sBAENC,mBACAC,wDASQC,WAAWC,eAAejD,KAAKO,UAAU2C,MAAMC,GAAMA,EAAEC,iCASpDC,sBAEoB,KAAhCrD,KAAKsD,uBACED,eAEJA,eAAeE,QAAQC,OAAUC,OAAOC,KAAKF,OAAOG,MAAMC,KAC1C,KAAfJ,MAAMI,MAGHJ,MAAMI,KAAKC,WAAWC,cAAcC,SAAS/D,KAAKsD,4BAOjEV,0BACSH,kBACDzC,KAAKoC,oBAAoB4B,KAAKR,QACnB,CACHS,GAAIT,MAAMS,GACVC,KAAMV,MAAMU,UAS5BnB,2BAESpC,YAAYE,iBAAiB,SAAS,oBAASsD,eAC3CC,eAAepE,KAAKW,YAAYM,OAEN,KAA3BjB,KAAKW,YAAYM,WAEZoD,kBAAkB1C,UAAU2C,IAAI,eAGhCD,kBAAkB1C,UAAUC,OAAO,gBAGtC5B,KAAKuE,qBACZ,yBAQYC,GACXA,EAAEC,OAAOC,QAAQ1E,KAAKD,UAAU4E,eAChCH,EAAEI,uBAEGjE,YAAYM,MAAQ,QACpBmD,eAAepE,KAAKW,YAAYM,YAChCN,YAAYkE,aACZR,kBAAkB1C,UAAU2C,IAAI,gBAE/BtE,KAAKuE,oBASnBO,cAAcN,GACVO,OAAOC,SAAWhF,KAAKiF,cAAcT,EAAEC,OAAOxD,OAMlDiE,6BAESvE,YAAYE,iBAAiB,SAAS,oBAAS,UAC3CuD,eAAepE,KAAKW,YAAYM,OAER,KAAzBjB,KAAKwC,qBAEA6B,kBAAkB1C,UAAU2C,IAAI,eAGhCD,kBAAkB1C,UAAUC,OAAO,YAE7C,MASPqD,cAAcE,eACJ,IAAIC,8BAAuBD,4CAAmCnF,KAAKF,YAAYoE"} \ No newline at end of file diff --git a/grade/amd/build/searchwidget/initials.min.js b/grade/amd/build/searchwidget/initials.min.js index 4fbde753536b9..6bf7090c951d1 100644 --- a/grade/amd/build/searchwidget/initials.min.js +++ b/grade/amd/build/searchwidget/initials.min.js @@ -1,4 +1,4 @@ -define("core_grades/searchwidget/initials",["exports","core/pending","core/url","core/custom_interaction_events","jquery"],(function(_exports,_pending,Url,_custom_interaction_events,_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}} +define("core_grades/searchwidget/initials",["exports","core/pending","core/url","core/custom_interaction_events","theme_boost/bootstrap/dropdown"],(function(_exports,_pending,Url,_custom_interaction_events,_dropdown){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}} /** * A small dropdown to filter users within the gradebook. * @@ -7,6 +7,6 @@ define("core_grades/searchwidget/initials",["exports","core/pending","core/url", * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @deprecated since Moodle 4.5 - please use core_course/actionbar/initials instead. * @todo Final deprecation in Moodle 6.0. See MDL-82421. - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Url=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}(Url),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_jquery=_interopRequireDefault(_jquery);let registered=!1;const selectors_pageListItem="page-item",selectors_pageClickableItem=".page-link",selectors_activeItem="active",selectors_formDropdown=".initialsdropdownform",selectors_parentDomNode=".initials-selector",selectors_firstInitial="firstinitial",selectors_lastInitial="lastinitial",selectors_initialBars=".initialbar",selectors_targetButton="initialswidget",selectors_formItems={type:"submit",save:"save",cancel:"cancel"};_exports.init=function(callingLink){let gpr_userid=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,gpr_search=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;if(registered)return;const pendingPromise=new _pending.default;registerListenerEvents(callingLink,gpr_userid,gpr_search),(0,_jquery.default)(selectors_parentDomNode).on("shown.bs.dropdown",(()=>{document.querySelector(selectors_pageClickableItem).focus({preventScroll:!0})})),pendingPromise.resolve(),registered=!0};const registerListenerEvents=function(callingLink){let gpr_userid=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,gpr_search=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events),events.forEach((event=>{document.addEventListener(event,(e=>{let{firstActive:firstActive,lastActive:lastActive,sifirst:sifirst,silast:silast}=onClickVariables(),itemToReset="";if(e.target.closest(selectors_formDropdown)&&e.preventDefault(),e.target.closest("".concat(selectors_formDropdown," .").concat(selectors_pageListItem))){if(e.target.classList.contains(selectors_pageListItem))return;e.target.closest(selectors_initialBars).classList.contains(selectors_firstInitial)?(sifirst=e.target,itemToReset=firstActive):(silast=e.target,itemToReset=lastActive),swapActiveItems(itemToReset,e)}if(e.target.closest("".concat(selectors_formDropdown))&&e.target.type===selectors_formItems.type){if(e.target.dataset.action===selectors_formItems.save){const params={id:e.target.closest(selectors_formDropdown).dataset.courseid,gpr_search:null!==gpr_search?gpr_search:"",sifirst:sifirst.parentElement.classList.contains("initialbarall")?"":sifirst.value,silast:silast.parentElement.classList.contains("initialbarall")?"":silast.value};null!==gpr_userid&&(params.gpr_userid=gpr_userid),window.location=Url.relativeUrl(callingLink,params)}e.target.dataset.action===selectors_formItems.cancel&&(0,_jquery.default)(".".concat(selectors_targetButton)).dropdown("toggle")}}))}))},onClickVariables=()=>{const firstItems=[...document.querySelectorAll(".".concat(selectors_firstInitial," li"))],lastItems=[...document.querySelectorAll(".".concat(selectors_lastInitial," li"))],firstActive=firstItems.filter((item=>item.classList.contains(selectors_activeItem)))[0],lastActive=lastItems.filter((item=>item.classList.contains(selectors_activeItem)))[0];let sifirst=firstActive.querySelector(selectors_pageClickableItem),silast=lastActive.querySelector(selectors_pageClickableItem);return{firstActive:firstActive,lastActive:lastActive,sifirst:sifirst,silast:silast}},swapActiveItems=(itemToReset,e)=>{itemToReset.classList.remove(selectors_activeItem),itemToReset.querySelector(selectors_pageClickableItem).ariaCurrent=!1;e.target.parentElement.classList.add(selectors_activeItem),e.target.ariaCurrent=!0}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Url=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}(Url),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_dropdown=_interopRequireDefault(_dropdown);let registered=!1;const selectors_pageListItem="page-item",selectors_pageClickableItem=".page-link",selectors_activeItem="active",selectors_formDropdown=".initialsdropdownform",selectors_parentDomNode=".initials-selector",selectors_firstInitial="firstinitial",selectors_lastInitial="lastinitial",selectors_initialBars=".initialbar",selectors_targetButton="initialswidget",selectors_formItems={type:"submit",save:"save",cancel:"cancel"};_exports.init=function(callingLink){let gpr_userid=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,gpr_search=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;if(registered)return;const pendingPromise=new _pending.default;registerListenerEvents(callingLink,gpr_userid,gpr_search),document.querySelector(selectors_parentDomNode).addEventListener("shown.bs.dropdown",(()=>{document.querySelector(selectors_pageClickableItem).focus({preventScroll:!0})})),pendingPromise.resolve(),registered=!0};const registerListenerEvents=function(callingLink){let gpr_userid=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,gpr_search=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events),events.forEach((event=>{document.addEventListener(event,(e=>{let{firstActive:firstActive,lastActive:lastActive,sifirst:sifirst,silast:silast}=onClickVariables(),itemToReset="";if(e.target.closest(selectors_formDropdown)&&e.preventDefault(),e.target.closest("".concat(selectors_formDropdown," .").concat(selectors_pageListItem))){if(e.target.classList.contains(selectors_pageListItem))return;e.target.closest(selectors_initialBars).classList.contains(selectors_firstInitial)?(sifirst=e.target,itemToReset=firstActive):(silast=e.target,itemToReset=lastActive),swapActiveItems(itemToReset,e)}if(e.target.closest("".concat(selectors_formDropdown))&&e.target.type===selectors_formItems.type){if(e.target.dataset.action===selectors_formItems.save){const params={id:e.target.closest(selectors_formDropdown).dataset.courseid,gpr_search:null!==gpr_search?gpr_search:"",sifirst:sifirst.parentElement.classList.contains("initialbarall")?"":sifirst.value,silast:silast.parentElement.classList.contains("initialbarall")?"":silast.value};null!==gpr_userid&&(params.gpr_userid=gpr_userid),window.location=Url.relativeUrl(callingLink,params)}e.target.dataset.action===selectors_formItems.cancel&&_dropdown.default.getOrCreateInstance(document.querySelector(".".concat(selectors_targetButton))).toggle()}}))}))},onClickVariables=()=>{const firstItems=[...document.querySelectorAll(".".concat(selectors_firstInitial," li"))],lastItems=[...document.querySelectorAll(".".concat(selectors_lastInitial," li"))],firstActive=firstItems.filter((item=>item.classList.contains(selectors_activeItem)))[0],lastActive=lastItems.filter((item=>item.classList.contains(selectors_activeItem)))[0];let sifirst=firstActive.querySelector(selectors_pageClickableItem),silast=lastActive.querySelector(selectors_pageClickableItem);return{firstActive:firstActive,lastActive:lastActive,sifirst:sifirst,silast:silast}},swapActiveItems=(itemToReset,e)=>{itemToReset.classList.remove(selectors_activeItem),itemToReset.querySelector(selectors_pageClickableItem).ariaCurrent=!1;e.target.parentElement.classList.add(selectors_activeItem),e.target.ariaCurrent=!0}})); //# sourceMappingURL=initials.min.js.map \ No newline at end of file diff --git a/grade/amd/build/searchwidget/initials.min.js.map b/grade/amd/build/searchwidget/initials.min.js.map index b2a47a7aa3b50..acbcc11f59bf7 100644 --- a/grade/amd/build/searchwidget/initials.min.js.map +++ b/grade/amd/build/searchwidget/initials.min.js.map @@ -1 +1 @@ -{"version":3,"file":"initials.min.js","sources":["../../src/searchwidget/initials.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 small dropdown to filter users within the gradebook.\n *\n * @module core_grades/searchwidget/initials\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @deprecated since Moodle 4.5 - please use core_course/actionbar/initials instead.\n * @todo Final deprecation in Moodle 6.0. See MDL-82421.\n */\n\nimport Pending from 'core/pending';\nimport * as Url from 'core/url';\nimport CustomEvents from \"core/custom_interaction_events\";\nimport $ from 'jquery';\n\n/**\n * Whether the event listener has already been registered for this module.\n *\n * @type {boolean}\n */\nlet registered = false;\n\n// Contain our selectors within this file until they could be of use elsewhere.\nconst selectors = {\n pageListItem: 'page-item',\n pageClickableItem: '.page-link',\n activeItem: 'active',\n formDropdown: '.initialsdropdownform',\n parentDomNode: '.initials-selector',\n firstInitial: 'firstinitial',\n lastInitial: 'lastinitial',\n initialBars: '.initialbar', // Both first and last name use this class.\n targetButton: 'initialswidget',\n formItems: {\n type: 'submit',\n save: 'save',\n cancel: 'cancel'\n }\n};\n\n/**\n * Our initial hook into the module which will eventually allow us to handle the dropdown initials bar form.\n *\n * @param {String} callingLink The link to redirect upon form submission.\n * @param {Null|Number} gpr_userid The user id to filter by.\n * @param {Null|String} gpr_search The search value to filter by.\n */\nexport const init = (callingLink, gpr_userid = null, gpr_search = null) => {\n if (registered) {\n return;\n }\n const pendingPromise = new Pending();\n registerListenerEvents(callingLink, gpr_userid, gpr_search);\n // BS events always bubble so, we need to listen for the event higher up the chain.\n $(selectors.parentDomNode).on('shown.bs.dropdown', () => {\n document.querySelector(selectors.pageClickableItem).focus({preventScroll: true});\n });\n pendingPromise.resolve();\n registered = true;\n};\n\n/**\n * Register event listeners.\n *\n * @param {String} callingLink The link to redirect upon form submission.\n * @param {Null|Number} gpr_userid The user id to filter by.\n * @param {Null|String} gpr_search The search value to filter by.\n */\nconst registerListenerEvents = (callingLink, gpr_userid = null, gpr_search = null) => {\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n CustomEvents.define(document, events);\n\n // Register events.\n events.forEach((event) => {\n document.addEventListener(event, (e) => {\n // Always fetch the latest information when we click as state is a fickle thing.\n let {firstActive, lastActive, sifirst, silast} = onClickVariables();\n let itemToReset = '';\n\n // Prevent the usual form behaviour.\n if (e.target.closest(selectors.formDropdown)) {\n e.preventDefault();\n }\n\n // Handle the state of active initials before form submission.\n if (e.target.closest(`${selectors.formDropdown} .${selectors.pageListItem}`)) {\n // Ensure the li items don't cause weird clicking emptying out the form.\n if (e.target.classList.contains(selectors.pageListItem)) {\n return;\n }\n\n const initialsBar = e.target.closest(selectors.initialBars); // Find out which initial bar we are in.\n\n // We want to find the current active item in the menu area the user selected.\n // We also want to fetch the raw item out of the array for instant manipulation.\n if (initialsBar.classList.contains(selectors.firstInitial)) {\n sifirst = e.target;\n itemToReset = firstActive;\n } else {\n silast = e.target;\n itemToReset = lastActive;\n }\n swapActiveItems(itemToReset, e);\n }\n\n // Handle form submissions.\n if (e.target.closest(`${selectors.formDropdown}`) && e.target.type === selectors.formItems.type) {\n if (e.target.dataset.action === selectors.formItems.save) {\n // Ensure we strip out the value (All) as it messes with the PHP side of the initials bar.\n // Then we will redirect the user back onto the page with new filters applied.\n const params = {\n 'id': e.target.closest(selectors.formDropdown).dataset.courseid,\n 'gpr_search': gpr_search !== null ? gpr_search : '',\n 'sifirst': sifirst.parentElement.classList.contains('initialbarall') ? '' : sifirst.value,\n 'silast': silast.parentElement.classList.contains('initialbarall') ? '' : silast.value,\n };\n if (gpr_userid !== null) {\n params.gpr_userid = gpr_userid;\n }\n window.location = Url.relativeUrl(callingLink, params);\n }\n if (e.target.dataset.action === selectors.formItems.cancel) {\n $(`.${selectors.targetButton}`).dropdown('toggle');\n }\n }\n });\n });\n};\n\n/**\n * A small abstracted helper function which allows us to ensure we have up-to-date lists of nodes.\n *\n * @returns {{firstActive: HTMLElement, lastActive: HTMLElement, sifirst: ?String, silast: ?String}}\n */\nconst onClickVariables = () => {\n // Ensure we have an up-to-date initials bar.\n const firstItems = [...document.querySelectorAll(`.${selectors.firstInitial} li`)];\n const lastItems = [...document.querySelectorAll(`.${selectors.lastInitial} li`)];\n const firstActive = firstItems.filter((item) => item.classList.contains(selectors.activeItem))[0];\n const lastActive = lastItems.filter((item) => item.classList.contains(selectors.activeItem))[0];\n // Ensure we retain both of the selections from a previous instance.\n let sifirst = firstActive.querySelector(selectors.pageClickableItem);\n let silast = lastActive.querySelector(selectors.pageClickableItem);\n return {firstActive, lastActive, sifirst, silast};\n};\n\n/**\n * Given we are provided the old li and current click event, swap around the active properties.\n *\n * @param {HTMLElement} itemToReset\n * @param {Event} e\n */\nconst swapActiveItems = (itemToReset, e) => {\n itemToReset.classList.remove(selectors.activeItem);\n itemToReset.querySelector(selectors.pageClickableItem).ariaCurrent = false;\n\n // Set the select item as the current item.\n const itemToSetActive = e.target.parentElement;\n itemToSetActive.classList.add(selectors.activeItem);\n e.target.ariaCurrent = true;\n};\n"],"names":["registered","selectors","type","save","cancel","callingLink","gpr_userid","gpr_search","pendingPromise","Pending","registerListenerEvents","on","document","querySelector","focus","preventScroll","resolve","events","CustomEvents","activate","keyboardActivate","define","forEach","event","addEventListener","e","firstActive","lastActive","sifirst","silast","onClickVariables","itemToReset","target","closest","preventDefault","classList","contains","swapActiveItems","dataset","action","params","courseid","parentElement","value","window","location","Url","relativeUrl","dropdown","firstItems","querySelectorAll","lastItems","filter","item","remove","ariaCurrent","add"],"mappings":";;;;;;;;;44BAmCIA,YAAa,QAGXC,uBACY,YADZA,4BAEiB,aAFjBA,qBAGU,SAHVA,uBAIY,wBAJZA,wBAKa,qBALbA,uBAMY,eANZA,sBAOW,cAPXA,sBAQW,cARXA,uBASY,iBATZA,oBAUS,CACPC,KAAM,SACNC,KAAM,OACNC,OAAQ,wBAWI,SAACC,iBAAaC,kEAAa,KAAMC,kEAAa,QAC1DP,wBAGEQ,eAAiB,IAAIC,iBAC3BC,uBAAuBL,YAAaC,WAAYC,gCAE9CN,yBAAyBU,GAAG,qBAAqB,KAC/CC,SAASC,cAAcZ,6BAA6Ba,MAAM,CAACC,eAAe,OAE9EP,eAAeQ,UACfhB,YAAa,SAUXU,uBAAyB,SAACL,iBAAaC,kEAAa,KAAMC,kEAAa,WACnEU,OAAS,CACX,QACAC,mCAAaD,OAAOE,SACpBD,mCAAaD,OAAOG,qDAEXC,OAAOT,SAAUK,QAG9BA,OAAOK,SAASC,QACZX,SAASY,iBAAiBD,OAAQE,QAE1BC,YAACA,YAADC,WAAcA,WAAdC,QAA0BA,QAA1BC,OAAmCA,QAAUC,mBAC7CC,YAAc,MAGdN,EAAEO,OAAOC,QAAQhC,yBACjBwB,EAAES,iBAIFT,EAAEO,OAAOC,kBAAWhC,oCAA2BA,yBAA2B,IAEtEwB,EAAEO,OAAOG,UAAUC,SAASnC,+BAIZwB,EAAEO,OAAOC,QAAQhC,uBAIrBkC,UAAUC,SAASnC,yBAC/B2B,QAAUH,EAAEO,OACZD,YAAcL,cAEdG,OAASJ,EAAEO,OACXD,YAAcJ,YAElBU,gBAAgBN,YAAaN,MAI7BA,EAAEO,OAAOC,kBAAWhC,0BAA6BwB,EAAEO,OAAO9B,OAASD,oBAAoBC,KAAM,IACzFuB,EAAEO,OAAOM,QAAQC,SAAWtC,oBAAoBE,KAAM,OAGhDqC,OAAS,IACLf,EAAEO,OAAOC,QAAQhC,wBAAwBqC,QAAQG,oBAC1B,OAAflC,WAAsBA,WAAa,WACtCqB,QAAQc,cAAcP,UAAUC,SAAS,iBAAmB,GAAKR,QAAQe,aAC1Ed,OAAOa,cAAcP,UAAUC,SAAS,iBAAmB,GAAKP,OAAOc,OAElE,OAAfrC,aACAkC,OAAOlC,WAAaA,YAExBsC,OAAOC,SAAWC,IAAIC,YAAY1C,YAAamC,QAE/Cf,EAAEO,OAAOM,QAAQC,SAAWtC,oBAAoBG,uCAC1CH,yBAA0B+C,SAAS,kBAYvDlB,iBAAmB,WAEfmB,WAAa,IAAIrC,SAASsC,4BAAqBjD,gCAC/CkD,UAAY,IAAIvC,SAASsC,4BAAqBjD,+BAC9CyB,YAAcuB,WAAWG,QAAQC,MAASA,KAAKlB,UAAUC,SAASnC,wBAAuB,GACzF0B,WAAawB,UAAUC,QAAQC,MAASA,KAAKlB,UAAUC,SAASnC,wBAAuB,OAEzF2B,QAAUF,YAAYb,cAAcZ,6BACpC4B,OAASF,WAAWd,cAAcZ,mCAC/B,CAACyB,YAAAA,YAAaC,WAAAA,WAAYC,QAAAA,QAASC,OAAAA,SASxCQ,gBAAkB,CAACN,YAAaN,KAClCM,YAAYI,UAAUmB,OAAOrD,sBAC7B8B,YAAYlB,cAAcZ,6BAA6BsD,aAAc,EAG7C9B,EAAEO,OAAOU,cACjBP,UAAUqB,IAAIvD,sBAC9BwB,EAAEO,OAAOuB,aAAc"} \ No newline at end of file +{"version":3,"file":"initials.min.js","sources":["../../src/searchwidget/initials.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 small dropdown to filter users within the gradebook.\n *\n * @module core_grades/searchwidget/initials\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @deprecated since Moodle 4.5 - please use core_course/actionbar/initials instead.\n * @todo Final deprecation in Moodle 6.0. See MDL-82421.\n */\n\nimport Pending from 'core/pending';\nimport * as Url from 'core/url';\nimport CustomEvents from \"core/custom_interaction_events\";\nimport Dropdown from 'theme_boost/bootstrap/dropdown';\n\n/**\n * Whether the event listener has already been registered for this module.\n *\n * @type {boolean}\n */\nlet registered = false;\n\n// Contain our selectors within this file until they could be of use elsewhere.\nconst selectors = {\n pageListItem: 'page-item',\n pageClickableItem: '.page-link',\n activeItem: 'active',\n formDropdown: '.initialsdropdownform',\n parentDomNode: '.initials-selector',\n firstInitial: 'firstinitial',\n lastInitial: 'lastinitial',\n initialBars: '.initialbar', // Both first and last name use this class.\n targetButton: 'initialswidget',\n formItems: {\n type: 'submit',\n save: 'save',\n cancel: 'cancel'\n }\n};\n\n/**\n * Our initial hook into the module which will eventually allow us to handle the dropdown initials bar form.\n *\n * @param {String} callingLink The link to redirect upon form submission.\n * @param {Null|Number} gpr_userid The user id to filter by.\n * @param {Null|String} gpr_search The search value to filter by.\n */\nexport const init = (callingLink, gpr_userid = null, gpr_search = null) => {\n if (registered) {\n return;\n }\n const pendingPromise = new Pending();\n registerListenerEvents(callingLink, gpr_userid, gpr_search);\n // BS events always bubble so, we need to listen for the event higher up the chain.\n document.querySelector(selectors.parentDomNode).addEventListener('shown.bs.dropdown', () => {\n document.querySelector(selectors.pageClickableItem).focus({preventScroll: true});\n });\n pendingPromise.resolve();\n registered = true;\n};\n\n/**\n * Register event listeners.\n *\n * @param {String} callingLink The link to redirect upon form submission.\n * @param {Null|Number} gpr_userid The user id to filter by.\n * @param {Null|String} gpr_search The search value to filter by.\n */\nconst registerListenerEvents = (callingLink, gpr_userid = null, gpr_search = null) => {\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n CustomEvents.define(document, events);\n\n // Register events.\n events.forEach((event) => {\n document.addEventListener(event, (e) => {\n // Always fetch the latest information when we click as state is a fickle thing.\n let {firstActive, lastActive, sifirst, silast} = onClickVariables();\n let itemToReset = '';\n\n // Prevent the usual form behaviour.\n if (e.target.closest(selectors.formDropdown)) {\n e.preventDefault();\n }\n\n // Handle the state of active initials before form submission.\n if (e.target.closest(`${selectors.formDropdown} .${selectors.pageListItem}`)) {\n // Ensure the li items don't cause weird clicking emptying out the form.\n if (e.target.classList.contains(selectors.pageListItem)) {\n return;\n }\n\n const initialsBar = e.target.closest(selectors.initialBars); // Find out which initial bar we are in.\n\n // We want to find the current active item in the menu area the user selected.\n // We also want to fetch the raw item out of the array for instant manipulation.\n if (initialsBar.classList.contains(selectors.firstInitial)) {\n sifirst = e.target;\n itemToReset = firstActive;\n } else {\n silast = e.target;\n itemToReset = lastActive;\n }\n swapActiveItems(itemToReset, e);\n }\n\n // Handle form submissions.\n if (e.target.closest(`${selectors.formDropdown}`) && e.target.type === selectors.formItems.type) {\n if (e.target.dataset.action === selectors.formItems.save) {\n // Ensure we strip out the value (All) as it messes with the PHP side of the initials bar.\n // Then we will redirect the user back onto the page with new filters applied.\n const params = {\n 'id': e.target.closest(selectors.formDropdown).dataset.courseid,\n 'gpr_search': gpr_search !== null ? gpr_search : '',\n 'sifirst': sifirst.parentElement.classList.contains('initialbarall') ? '' : sifirst.value,\n 'silast': silast.parentElement.classList.contains('initialbarall') ? '' : silast.value,\n };\n if (gpr_userid !== null) {\n params.gpr_userid = gpr_userid;\n }\n window.location = Url.relativeUrl(callingLink, params);\n }\n if (e.target.dataset.action === selectors.formItems.cancel) {\n Dropdown.getOrCreateInstance(document.querySelector(`.${selectors.targetButton}`)).toggle();\n }\n }\n });\n });\n};\n\n/**\n * A small abstracted helper function which allows us to ensure we have up-to-date lists of nodes.\n *\n * @returns {{firstActive: HTMLElement, lastActive: HTMLElement, sifirst: ?String, silast: ?String}}\n */\nconst onClickVariables = () => {\n // Ensure we have an up-to-date initials bar.\n const firstItems = [...document.querySelectorAll(`.${selectors.firstInitial} li`)];\n const lastItems = [...document.querySelectorAll(`.${selectors.lastInitial} li`)];\n const firstActive = firstItems.filter((item) => item.classList.contains(selectors.activeItem))[0];\n const lastActive = lastItems.filter((item) => item.classList.contains(selectors.activeItem))[0];\n // Ensure we retain both of the selections from a previous instance.\n let sifirst = firstActive.querySelector(selectors.pageClickableItem);\n let silast = lastActive.querySelector(selectors.pageClickableItem);\n return {firstActive, lastActive, sifirst, silast};\n};\n\n/**\n * Given we are provided the old li and current click event, swap around the active properties.\n *\n * @param {HTMLElement} itemToReset\n * @param {Event} e\n */\nconst swapActiveItems = (itemToReset, e) => {\n itemToReset.classList.remove(selectors.activeItem);\n itemToReset.querySelector(selectors.pageClickableItem).ariaCurrent = false;\n\n // Set the select item as the current item.\n const itemToSetActive = e.target.parentElement;\n itemToSetActive.classList.add(selectors.activeItem);\n e.target.ariaCurrent = true;\n};\n"],"names":["registered","selectors","type","save","cancel","callingLink","gpr_userid","gpr_search","pendingPromise","Pending","registerListenerEvents","document","querySelector","addEventListener","focus","preventScroll","resolve","events","CustomEvents","activate","keyboardActivate","define","forEach","event","e","firstActive","lastActive","sifirst","silast","onClickVariables","itemToReset","target","closest","preventDefault","classList","contains","swapActiveItems","dataset","action","params","courseid","parentElement","value","window","location","Url","relativeUrl","getOrCreateInstance","toggle","firstItems","querySelectorAll","lastItems","filter","item","remove","ariaCurrent","add"],"mappings":";;;;;;;;;g5BAmCIA,YAAa,QAGXC,uBACY,YADZA,4BAEiB,aAFjBA,qBAGU,SAHVA,uBAIY,wBAJZA,wBAKa,qBALbA,uBAMY,eANZA,sBAOW,cAPXA,sBAQW,cARXA,uBASY,iBATZA,oBAUS,CACPC,KAAM,SACNC,KAAM,OACNC,OAAQ,wBAWI,SAACC,iBAAaC,kEAAa,KAAMC,kEAAa,QAC1DP,wBAGEQ,eAAiB,IAAIC,iBAC3BC,uBAAuBL,YAAaC,WAAYC,YAEhDI,SAASC,cAAcX,yBAAyBY,iBAAiB,qBAAqB,KAClFF,SAASC,cAAcX,6BAA6Ba,MAAM,CAACC,eAAe,OAE9EP,eAAeQ,UACfhB,YAAa,SAUXU,uBAAyB,SAACL,iBAAaC,kEAAa,KAAMC,kEAAa,WACnEU,OAAS,CACX,QACAC,mCAAaD,OAAOE,SACpBD,mCAAaD,OAAOG,qDAEXC,OAAOV,SAAUM,QAG9BA,OAAOK,SAASC,QACZZ,SAASE,iBAAiBU,OAAQC,QAE1BC,YAACA,YAADC,WAAcA,WAAdC,QAA0BA,QAA1BC,OAAmCA,QAAUC,mBAC7CC,YAAc,MAGdN,EAAEO,OAAOC,QAAQ/B,yBACjBuB,EAAES,iBAIFT,EAAEO,OAAOC,kBAAW/B,oCAA2BA,yBAA2B,IAEtEuB,EAAEO,OAAOG,UAAUC,SAASlC,+BAIZuB,EAAEO,OAAOC,QAAQ/B,uBAIrBiC,UAAUC,SAASlC,yBAC/B0B,QAAUH,EAAEO,OACZD,YAAcL,cAEdG,OAASJ,EAAEO,OACXD,YAAcJ,YAElBU,gBAAgBN,YAAaN,MAI7BA,EAAEO,OAAOC,kBAAW/B,0BAA6BuB,EAAEO,OAAO7B,OAASD,oBAAoBC,KAAM,IACzFsB,EAAEO,OAAOM,QAAQC,SAAWrC,oBAAoBE,KAAM,OAGhDoC,OAAS,IACLf,EAAEO,OAAOC,QAAQ/B,wBAAwBoC,QAAQG,oBAC1B,OAAfjC,WAAsBA,WAAa,WACtCoB,QAAQc,cAAcP,UAAUC,SAAS,iBAAmB,GAAKR,QAAQe,aAC1Ed,OAAOa,cAAcP,UAAUC,SAAS,iBAAmB,GAAKP,OAAOc,OAElE,OAAfpC,aACAiC,OAAOjC,WAAaA,YAExBqC,OAAOC,SAAWC,IAAIC,YAAYzC,YAAakC,QAE/Cf,EAAEO,OAAOM,QAAQC,SAAWrC,oBAAoBG,0BACvC2C,oBAAoBpC,SAASC,yBAAkBX,0BAA2B+C,iBAYjGnB,iBAAmB,WAEfoB,WAAa,IAAItC,SAASuC,4BAAqBjD,gCAC/CkD,UAAY,IAAIxC,SAASuC,4BAAqBjD,+BAC9CwB,YAAcwB,WAAWG,QAAQC,MAASA,KAAKnB,UAAUC,SAASlC,wBAAuB,GACzFyB,WAAayB,UAAUC,QAAQC,MAASA,KAAKnB,UAAUC,SAASlC,wBAAuB,OAEzF0B,QAAUF,YAAYb,cAAcX,6BACpC2B,OAASF,WAAWd,cAAcX,mCAC/B,CAACwB,YAAAA,YAAaC,WAAAA,WAAYC,QAAAA,QAASC,OAAAA,SASxCQ,gBAAkB,CAACN,YAAaN,KAClCM,YAAYI,UAAUoB,OAAOrD,sBAC7B6B,YAAYlB,cAAcX,6BAA6BsD,aAAc,EAG7C/B,EAAEO,OAAOU,cACjBP,UAAUsB,IAAIvD,sBAC9BuB,EAAEO,OAAOwB,aAAc"} \ No newline at end of file diff --git a/grade/amd/src/comboboxsearch/grade.js b/grade/amd/src/comboboxsearch/grade.js index 611258173a7b7..b965e2c5a76d4 100644 --- a/grade/amd/src/comboboxsearch/grade.js +++ b/grade/amd/src/comboboxsearch/grade.js @@ -55,7 +55,7 @@ export default class GradeItemSearch extends search_combobox { searchValueElement.value = ''; }); - this.$component.on('hide.bs.dropdown', () => { + this.component.addEventListener('hide.bs.dropdown', () => { this.searchInput.removeAttribute('aria-activedescendant'); const listbox = document.querySelector(`#${this.searchInput.getAttribute('aria-controls')}[role="listbox"]`); diff --git a/grade/amd/src/searchwidget/initials.js b/grade/amd/src/searchwidget/initials.js index 32f2739d98765..da1c2d755a0fb 100644 --- a/grade/amd/src/searchwidget/initials.js +++ b/grade/amd/src/searchwidget/initials.js @@ -26,7 +26,7 @@ import Pending from 'core/pending'; import * as Url from 'core/url'; import CustomEvents from "core/custom_interaction_events"; -import $ from 'jquery'; +import Dropdown from 'theme_boost/bootstrap/dropdown'; /** * Whether the event listener has already been registered for this module. @@ -67,7 +67,7 @@ export const init = (callingLink, gpr_userid = null, gpr_search = null) => { const pendingPromise = new Pending(); registerListenerEvents(callingLink, gpr_userid, gpr_search); // BS events always bubble so, we need to listen for the event higher up the chain. - $(selectors.parentDomNode).on('shown.bs.dropdown', () => { + document.querySelector(selectors.parentDomNode).addEventListener('shown.bs.dropdown', () => { document.querySelector(selectors.pageClickableItem).focus({preventScroll: true}); }); pendingPromise.resolve(); @@ -139,7 +139,7 @@ const registerListenerEvents = (callingLink, gpr_userid = null, gpr_search = nul window.location = Url.relativeUrl(callingLink, params); } if (e.target.dataset.action === selectors.formItems.cancel) { - $(`.${selectors.targetButton}`).dropdown('toggle'); + Dropdown.getOrCreateInstance(document.querySelector(`.${selectors.targetButton}`)).toggle(); } } }); diff --git a/grade/report/grader/amd/build/collapse.min.js b/grade/report/grader/amd/build/collapse.min.js index f1e987797ebad..90ba8d14ac1b9 100644 --- a/grade/report/grader/amd/build/collapse.min.js +++ b/grade/report/grader/amd/build/collapse.min.js @@ -1,3 +1,3 @@ -define("gradereport_grader/collapse",["exports","gradereport_grader/collapse/repository","core/comboboxsearch/search_combobox","core/templates","core/utils","jquery","core/str","core/custom_interaction_events","core/localstorage","core/loadingicon","core/notification","core/pending"],(function(_exports,Repository,_search_combobox,_templates,_utils,_jquery,_str,_custom_interaction_events,_localstorage,_loadingicon,_notification,_pending){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 _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,Repository=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}(Repository),_search_combobox=_interopRequireDefault(_search_combobox),_jquery=_interopRequireDefault(_jquery),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_localstorage=_interopRequireDefault(_localstorage),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const selectors_component=".collapse-columns",selectors_formDropdown=".columnsdropdownform",selectors_formItems={cancel:"cancel",save:"save",checked:'input[type="checkbox"]:checked',currentlyUnchecked:'input[type="checkbox"]:not([data-action="selectall"])'},selectors_hider="hide",selectors_expand="expand",selectors_colVal="[data-col]",selectors_itemVal="[data-itemid]",selectors_content='[data-collapse="content"]',selectors_sort='[data-collapse="sort"]',selectors_expandbutton='[data-collapse="expandbutton"]',selectors_rangerowcell='[data-collapse="rangerowcell"]',selectors_avgrowcell='[data-collapse="avgrowcell"]',selectors_menu='[data-collapse="menu"]',selectors_icons=".data-collapse_gradeicons",selectors_count='[data-collapse="count"]',selectors_placeholder='.collapsecolumndropdown [data-region="placeholder"]',selectors_fullDropdown=".collapsecolumndropdown",selectors_searchResultContainer=".searchresultitemscontainer",countIndicator=document.querySelector(selectors_count);class ColumnSearch extends _search_combobox.default{static init(userID,courseID,defaultSort){return new ColumnSearch(userID,courseID,defaultSort)}constructor(userID,courseID,defaultSort){super(),_defineProperty(this,"userID",-1),_defineProperty(this,"courseID",null),_defineProperty(this,"defaultSort",""),_defineProperty(this,"nodes",[]),_defineProperty(this,"gradeStrings",null),_defineProperty(this,"userStrings",null),_defineProperty(this,"stringMap",[]),this.userID=userID,this.courseID=courseID,this.defaultSort=defaultSort,this.component=document.querySelector(selectors_component);const pendingPromise=new _pending.default;(0,_loadingicon.addIconToContainer)(document.querySelector(".gradeparent")).then((loader=>{setTimeout((()=>{this.getDataset().forEach((item=>{this.nodesUpdate(item)})),this.renderDefault(),loader.remove(),document.querySelector(".gradereport-grader-table").classList.remove("d-none")}),10)})).then((()=>pendingPromise.resolve())).catch(_notification.default.exception),this.$component.on("hide.bs.dropdown",(()=>{this.component.querySelector(selectors_searchResultContainer).scrollTop=0,setTimeout((()=>{""!==this.searchInput.value&&(this.searchInput.value="",this.searchInput.dispatchEvent(new Event("input",{bubbles:!0})))}))}))}componentSelector(){return".collapse-columns"}dropdownSelector(){return".searchresultitemscontainer"}getDataset(){if(!this.dataset){const cols=this.fetchDataset();this.dataset=JSON.parse(cols)?JSON.parse(cols).split(","):[]}return this.datasetSize=this.dataset.length,this.dataset}fetchDataset(){return _localstorage.default.get("gradereport_grader_collapseditems_".concat(this.courseID,"_").concat(this.userID))}setPreferences(){_localstorage.default.set("gradereport_grader_collapseditems_".concat(this.courseID,"_").concat(this.userID),JSON.stringify(this.getDataset().join(",")))}registerClickHandlers(){this.component.addEventListener("click",this.clickHandler.bind(this)),document.addEventListener("click",this.docClickHandler.bind(this))}clickHandler(e){super.clickHandler(e),e.target.closest(selectors_fullDropdown)&&e.stopPropagation()}async docClickHandler(e){var _e$target$closest3;if(e.target.dataset.hider===selectors_hider){var _e$target$closest,_e$target$closest2;e.preventDefault();const desiredToHide=e.target.closest(selectors_colVal)?null===(_e$target$closest=e.target.closest(selectors_colVal))||void 0===_e$target$closest?void 0:_e$target$closest.dataset.col:null===(_e$target$closest2=e.target.closest(selectors_itemVal))||void 0===_e$target$closest2?void 0:_e$target$closest2.dataset.itemid;-1===this.getDataset().indexOf(desiredToHide)&&this.getDataset().push(desiredToHide),await this.prefcountpipe(),this.nodesUpdate(desiredToHide)}if((null===(_e$target$closest3=e.target.closest("button"))||void 0===_e$target$closest3?void 0:_e$target$closest3.dataset.hider)===selectors_expand){var _e$target$closest4,_e$target$closest5,_e$target$closest6,_e$target$closest7;e.preventDefault();const desiredToHide=e.target.closest(selectors_colVal)?null===(_e$target$closest4=e.target.closest(selectors_colVal))||void 0===_e$target$closest4?void 0:_e$target$closest4.dataset.col:null===(_e$target$closest5=e.target.closest(selectors_itemVal))||void 0===_e$target$closest5?void 0:_e$target$closest5.dataset.itemid,idx=this.getDataset().indexOf(desiredToHide);this.getDataset().splice(idx,1),await this.prefcountpipe(),this.nodesUpdate(null===(_e$target$closest6=e.target.closest(selectors_colVal))||void 0===_e$target$closest6?void 0:_e$target$closest6.dataset.col),this.nodesUpdate(null===(_e$target$closest7=e.target.closest(selectors_colVal))||void 0===_e$target$closest7?void 0:_e$target$closest7.dataset.itemid)}}registerInputEvents(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{if(this.getSearchTerm()===this.searchInput.value&&this.searchResultsVisible())return void window.console.warn("Search term matches input value - skipping");this.setSearchTerms(this.searchInput.value),""===this.searchInput.value?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none");const pendingPromise=new _pending.default;await this.filterrenderpipe().then((()=>(pendingPromise.resolve(),!0)))}),300,{pending:!0}))}registerFormEvents(){const form=this.component.querySelector(selectors_formDropdown),events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events);const selectall=form.querySelector('[data-action="selectall"]');events.forEach((event=>{const submitBtn=form.querySelector('[data-action="'.concat(selectors_formItems.save,'"'));form.addEventListener(event,(e=>{e.stopPropagation();const input=e.target.closest("input");if(input){selectall.checked&&!input.checked&&(selectall.checked=!1);const checkedCount=Array.from(form.querySelectorAll(selectors_formItems.checked)).length;submitBtn.disabled=checkedCount<=0}}),!1),this.searchInput.addEventListener(event,(e=>e.stopPropagation())),this.clearSearchButton.addEventListener(event,(async e=>{e.stopPropagation(),this.searchInput.value="",this.setSearchTerms(this.searchInput.value),await this.filterrenderpipe()})),selectall.addEventListener(event,(e=>{if(e.stopPropagation(),selectall.checked){Array.from(form.querySelectorAll(selectors_formItems.currentlyUnchecked)).forEach((item=>{item.checked=!0})),submitBtn.disabled=!1}else{Array.from(form.querySelectorAll(selectors_formItems.checked)).forEach((item=>{item.checked=!1})),submitBtn.disabled=!0}}))})),form.addEventListener("submit",(async e=>{if(e.preventDefault(),e.submitter.dataset.action===selectors_formItems.cancel)return void(0,_jquery.default)(this.component).dropdown("toggle");[...form.elements].filter((item=>item.checked)).forEach((item=>{const idx=this.getDataset().indexOf(item.dataset.collapse);this.getDataset().splice(idx,1),this.nodesUpdate(item.dataset.collapse)})),selectall.checked=!1,e.submitter.disabled=!0,await this.prefcountpipe()}))}nodesUpdate(item){const colNodesToHide=[...document.querySelectorAll('[data-col="'.concat(item,'"]'))],itemIDNodesToHide=[...document.querySelectorAll('[data-itemid="'.concat(item,'"]'))];this.nodes=[...colNodesToHide,...itemIDNodesToHide],this.updateDisplay()}async prefcountpipe(){this.setPreferences(),this.countUpdate(),await this.filterrenderpipe()}async filterDataset(filterableData){const stringUserMap=await this.fetchRequiredUserStrings(),stringGradeMap=await this.fetchRequiredGradeStrings(),customFieldMap=this.fetchCustomFieldValues();this.stringMap=new Map([...stringGradeMap,...stringUserMap,...customFieldMap]);const searching=filterableData.map((s=>{var _mapObj$itemname,_mapObj$category;const mapObj=this.stringMap.get(s);return void 0===mapObj?{key:s,string:s}:{key:s,string:null!==(_mapObj$itemname=mapObj.itemname)&&void 0!==_mapObj$itemname?_mapObj$itemname:this.stringMap.get(s),category:null!==(_mapObj$category=mapObj.category)&&void 0!==_mapObj$category?_mapObj$category:""}}));return""===this.getPreppedSearchTerm()?searching:searching.filter((col=>col.string.toString().toLowerCase().includes(this.getPreppedSearchTerm())))}filterMatchDataset(){this.setMatchedResults(this.getMatchedResults().map((column=>{var _column$string,_column$category;return{name:column.key,displayName:null!==(_column$string=column.string)&&void 0!==_column$string?_column$string:column.key,category:null!==(_column$category=column.category)&&void 0!==_column$category?_column$category:""}})))}updateDisplay(){this.nodes.forEach((element=>{const content=element.querySelector(selectors_content),sort=element.querySelector(selectors_sort),expandButton=element.querySelector(selectors_expandbutton),rangeRowCell=element.querySelector(selectors_rangerowcell),avgRowCell=element.querySelector(selectors_avgrowcell),nodeSet=[element.querySelector(selectors_menu),element.querySelector(selectors_icons),content];if(element.classList.contains("cell"))if(null!==sort&&(window.location=this.defaultSort),null===content){const rowCell=null!=avgRowCell?avgRowCell:rangeRowCell;null==rowCell||rowCell.classList.toggle("d-none")}else content.classList.contains("d-none")?(element.classList.remove("collapsed"),content.childNodes.length>1&&content.classList.add("d-flex"),nodeSet.forEach((node=>{null==node||node.classList.remove("d-none")})),null==expandButton||expandButton.classList.add("d-none")):(element.classList.add("collapsed"),content.classList.remove("d-flex"),nodeSet.forEach((node=>{null==node||node.classList.add("d-none")})),null==expandButton||expandButton.classList.remove("d-none"))}))}countUpdate(){countIndicator.textContent=this.getDatasetSize(),this.getDatasetSize()>0?(this.component.parentElement.classList.add("d-flex"),this.component.parentElement.classList.remove("d-none")):(this.component.parentElement.classList.remove("d-flex"),this.component.parentElement.classList.add("d-none"))}async renderDefault(){this.setMatchedResults(await this.filterDataset(this.getDataset())),this.filterMatchDataset(),this.countUpdate();const{html:html,js:js}=await(0,_templates.renderForPromise)("gradereport_grader/collapse/collapsebody",{instance:this.instance,results:this.getMatchedResults(),userid:this.userID});(0,_templates.replaceNode)(selectors_placeholder,html,js),this.updateNodes(),this.registerFormEvents(),this.registerInputEvents(),this.$component.on("shown.bs.dropdown",(()=>{this.searchInput.focus({preventScroll:!0}),this.selectallEnable()}))}async renderDropdown(){const{html:html,js:js}=await(0,_templates.renderForPromise)("gradereport_grader/collapse/collapseresults",{instance:this.instance,results:this.getMatchedResults(),searchTerm:this.getSearchTerm()});(0,_templates.replaceNodeContents)(this.getHTMLElements().searchDropdown,html,js),this.selectallEnable();this.component.querySelector(selectors_formDropdown).querySelector('[data-action="'.concat(selectors_formItems.save,'"')).disabled=!0}selectallEnable(){this.component.querySelector(selectors_formDropdown).querySelector('[data-action="selectall"]').disabled=0===this.getMatchedResults().length}fetchCustomFieldValues(){return[...document.querySelectorAll("[data-collapse-name]")].map((field=>[field.parentElement.dataset.col,field.dataset.collapseName]))}fetchRequiredUserStrings(){if(!this.userStrings){const requiredStrings=["username","firstname","lastname","email","city","country","department","institution","idnumber","phone1","phone2"];this.userStrings=(0,_str.getStrings)(requiredStrings.map((key=>({key:key})))).then((stringArray=>new Map(requiredStrings.map(((key,index)=>[key,stringArray[index]])))))}return this.userStrings}fetchRequiredGradeStrings(){return this.gradeStrings||(this.gradeStrings=Repository.gradeItems(this.courseID).then((result=>new Map(result.gradeItems.map((key=>[key.id,key])))))),this.gradeStrings}}return _exports.default=ColumnSearch,_exports.default})); +define("gradereport_grader/collapse",["exports","gradereport_grader/collapse/repository","core/comboboxsearch/search_combobox","core/templates","core/utils","theme_boost/bootstrap/dropdown","core/str","core/custom_interaction_events","core/localstorage","core/loadingicon","core/notification","core/pending"],(function(_exports,Repository,_search_combobox,_templates,_utils,_dropdown,_str,_custom_interaction_events,_localstorage,_loadingicon,_notification,_pending){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 _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,Repository=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}(Repository),_search_combobox=_interopRequireDefault(_search_combobox),_dropdown=_interopRequireDefault(_dropdown),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_localstorage=_interopRequireDefault(_localstorage),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const selectors_component=".collapse-columns",selectors_formDropdown=".columnsdropdownform",selectors_formItems={cancel:"cancel",save:"save",checked:'input[type="checkbox"]:checked',currentlyUnchecked:'input[type="checkbox"]:not([data-action="selectall"])'},selectors_hider="hide",selectors_expand="expand",selectors_colVal="[data-col]",selectors_itemVal="[data-itemid]",selectors_content='[data-collapse="content"]',selectors_sort='[data-collapse="sort"]',selectors_expandbutton='[data-collapse="expandbutton"]',selectors_rangerowcell='[data-collapse="rangerowcell"]',selectors_avgrowcell='[data-collapse="avgrowcell"]',selectors_menu='[data-collapse="menu"]',selectors_icons=".data-collapse_gradeicons",selectors_count='[data-collapse="count"]',selectors_placeholder='.collapsecolumndropdown [data-region="placeholder"]',selectors_fullDropdown=".collapsecolumndropdown",selectors_searchResultContainer=".searchresultitemscontainer",countIndicator=document.querySelector(selectors_count);class ColumnSearch extends _search_combobox.default{static init(userID,courseID,defaultSort){return new ColumnSearch(userID,courseID,defaultSort)}constructor(userID,courseID,defaultSort){super(),_defineProperty(this,"userID",-1),_defineProperty(this,"courseID",null),_defineProperty(this,"defaultSort",""),_defineProperty(this,"nodes",[]),_defineProperty(this,"gradeStrings",null),_defineProperty(this,"userStrings",null),_defineProperty(this,"stringMap",[]),this.userID=userID,this.courseID=courseID,this.defaultSort=defaultSort,this.component=document.querySelector(selectors_component);const pendingPromise=new _pending.default;(0,_loadingicon.addIconToContainer)(document.querySelector(".gradeparent")).then((loader=>{setTimeout((()=>{this.getDataset().forEach((item=>{this.nodesUpdate(item)})),this.renderDefault(),loader.remove(),document.querySelector(".gradereport-grader-table").classList.remove("d-none")}),10)})).then((()=>pendingPromise.resolve())).catch(_notification.default.exception),this.component.addEventListener("hide.bs.dropdown",(()=>{this.component.querySelector(selectors_searchResultContainer).scrollTop=0,setTimeout((()=>{""!==this.searchInput.value&&(this.searchInput.value="",this.searchInput.dispatchEvent(new Event("input",{bubbles:!0})))}))}))}componentSelector(){return".collapse-columns"}dropdownSelector(){return".searchresultitemscontainer"}getDataset(){if(!this.dataset){const cols=this.fetchDataset();this.dataset=JSON.parse(cols)?JSON.parse(cols).split(","):[]}return this.datasetSize=this.dataset.length,this.dataset}fetchDataset(){return _localstorage.default.get("gradereport_grader_collapseditems_".concat(this.courseID,"_").concat(this.userID))}setPreferences(){_localstorage.default.set("gradereport_grader_collapseditems_".concat(this.courseID,"_").concat(this.userID),JSON.stringify(this.getDataset().join(",")))}registerClickHandlers(){this.component.addEventListener("click",this.clickHandler.bind(this)),document.addEventListener("click",this.docClickHandler.bind(this))}clickHandler(e){super.clickHandler(e),e.target.closest(selectors_fullDropdown)&&e.stopPropagation()}async docClickHandler(e){var _e$target$closest3;if(e.target.dataset.hider===selectors_hider){var _e$target$closest,_e$target$closest2;e.preventDefault();const desiredToHide=e.target.closest(selectors_colVal)?null===(_e$target$closest=e.target.closest(selectors_colVal))||void 0===_e$target$closest?void 0:_e$target$closest.dataset.col:null===(_e$target$closest2=e.target.closest(selectors_itemVal))||void 0===_e$target$closest2?void 0:_e$target$closest2.dataset.itemid;-1===this.getDataset().indexOf(desiredToHide)&&this.getDataset().push(desiredToHide),await this.prefcountpipe(),this.nodesUpdate(desiredToHide)}if((null===(_e$target$closest3=e.target.closest("button"))||void 0===_e$target$closest3?void 0:_e$target$closest3.dataset.hider)===selectors_expand){var _e$target$closest4,_e$target$closest5,_e$target$closest6,_e$target$closest7;e.preventDefault();const desiredToHide=e.target.closest(selectors_colVal)?null===(_e$target$closest4=e.target.closest(selectors_colVal))||void 0===_e$target$closest4?void 0:_e$target$closest4.dataset.col:null===(_e$target$closest5=e.target.closest(selectors_itemVal))||void 0===_e$target$closest5?void 0:_e$target$closest5.dataset.itemid,idx=this.getDataset().indexOf(desiredToHide);this.getDataset().splice(idx,1),await this.prefcountpipe(),this.nodesUpdate(null===(_e$target$closest6=e.target.closest(selectors_colVal))||void 0===_e$target$closest6?void 0:_e$target$closest6.dataset.col),this.nodesUpdate(null===(_e$target$closest7=e.target.closest(selectors_colVal))||void 0===_e$target$closest7?void 0:_e$target$closest7.dataset.itemid)}}registerInputEvents(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{if(this.getSearchTerm()===this.searchInput.value&&this.searchResultsVisible())return void window.console.warn("Search term matches input value - skipping");this.setSearchTerms(this.searchInput.value),""===this.searchInput.value?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none");const pendingPromise=new _pending.default;await this.filterrenderpipe().then((()=>(pendingPromise.resolve(),!0)))}),300,{pending:!0}))}registerFormEvents(){const form=this.component.querySelector(selectors_formDropdown),events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events);const selectall=form.querySelector('[data-action="selectall"]');events.forEach((event=>{const submitBtn=form.querySelector('[data-action="'.concat(selectors_formItems.save,'"'));form.addEventListener(event,(e=>{e.stopPropagation();const input=e.target.closest("input");if(input){selectall.checked&&!input.checked&&(selectall.checked=!1);const checkedCount=Array.from(form.querySelectorAll(selectors_formItems.checked)).length;submitBtn.disabled=checkedCount<=0}}),!1),this.searchInput.addEventListener(event,(e=>e.stopPropagation())),this.clearSearchButton.addEventListener(event,(async e=>{e.stopPropagation(),this.searchInput.value="",this.setSearchTerms(this.searchInput.value),await this.filterrenderpipe()})),selectall.addEventListener(event,(e=>{if(e.stopPropagation(),selectall.checked){Array.from(form.querySelectorAll(selectors_formItems.currentlyUnchecked)).forEach((item=>{item.checked=!0})),submitBtn.disabled=!1}else{Array.from(form.querySelectorAll(selectors_formItems.checked)).forEach((item=>{item.checked=!1})),submitBtn.disabled=!0}}))})),form.addEventListener("submit",(async e=>{if(e.preventDefault(),e.submitter.dataset.action===selectors_formItems.cancel)return void _dropdown.default.getOrCreateInstance(this.component).toggle();[...form.elements].filter((item=>item.checked)).forEach((item=>{const idx=this.getDataset().indexOf(item.dataset.collapse);this.getDataset().splice(idx,1),this.nodesUpdate(item.dataset.collapse)})),selectall.checked=!1,e.submitter.disabled=!0,await this.prefcountpipe()}))}nodesUpdate(item){const colNodesToHide=[...document.querySelectorAll('[data-col="'.concat(item,'"]'))],itemIDNodesToHide=[...document.querySelectorAll('[data-itemid="'.concat(item,'"]'))];this.nodes=[...colNodesToHide,...itemIDNodesToHide],this.updateDisplay()}async prefcountpipe(){this.setPreferences(),this.countUpdate(),await this.filterrenderpipe()}async filterDataset(filterableData){const stringUserMap=await this.fetchRequiredUserStrings(),stringGradeMap=await this.fetchRequiredGradeStrings(),customFieldMap=this.fetchCustomFieldValues();this.stringMap=new Map([...stringGradeMap,...stringUserMap,...customFieldMap]);const searching=filterableData.map((s=>{var _mapObj$itemname,_mapObj$category;const mapObj=this.stringMap.get(s);return void 0===mapObj?{key:s,string:s}:{key:s,string:null!==(_mapObj$itemname=mapObj.itemname)&&void 0!==_mapObj$itemname?_mapObj$itemname:this.stringMap.get(s),category:null!==(_mapObj$category=mapObj.category)&&void 0!==_mapObj$category?_mapObj$category:""}}));return""===this.getPreppedSearchTerm()?searching:searching.filter((col=>col.string.toString().toLowerCase().includes(this.getPreppedSearchTerm())))}filterMatchDataset(){this.setMatchedResults(this.getMatchedResults().map((column=>{var _column$string,_column$category;return{name:column.key,displayName:null!==(_column$string=column.string)&&void 0!==_column$string?_column$string:column.key,category:null!==(_column$category=column.category)&&void 0!==_column$category?_column$category:""}})))}updateDisplay(){this.nodes.forEach((element=>{const content=element.querySelector(selectors_content),sort=element.querySelector(selectors_sort),expandButton=element.querySelector(selectors_expandbutton),rangeRowCell=element.querySelector(selectors_rangerowcell),avgRowCell=element.querySelector(selectors_avgrowcell),nodeSet=[element.querySelector(selectors_menu),element.querySelector(selectors_icons),content];if(element.classList.contains("cell"))if(null!==sort&&(window.location=this.defaultSort),null===content){const rowCell=null!=avgRowCell?avgRowCell:rangeRowCell;null==rowCell||rowCell.classList.toggle("d-none")}else content.classList.contains("d-none")?(element.classList.remove("collapsed"),content.childNodes.length>1&&content.classList.add("d-flex"),nodeSet.forEach((node=>{null==node||node.classList.remove("d-none")})),null==expandButton||expandButton.classList.add("d-none")):(element.classList.add("collapsed"),content.classList.remove("d-flex"),nodeSet.forEach((node=>{null==node||node.classList.add("d-none")})),null==expandButton||expandButton.classList.remove("d-none"))}))}countUpdate(){countIndicator.textContent=this.getDatasetSize(),this.getDatasetSize()>0?(this.component.parentElement.classList.add("d-flex"),this.component.parentElement.classList.remove("d-none")):(this.component.parentElement.classList.remove("d-flex"),this.component.parentElement.classList.add("d-none"))}async renderDefault(){this.setMatchedResults(await this.filterDataset(this.getDataset())),this.filterMatchDataset(),this.countUpdate();const{html:html,js:js}=await(0,_templates.renderForPromise)("gradereport_grader/collapse/collapsebody",{instance:this.instance,results:this.getMatchedResults(),userid:this.userID});(0,_templates.replaceNode)(selectors_placeholder,html,js),this.updateNodes(),this.registerFormEvents(),this.registerInputEvents(),this.component.addEventListener("shown.bs.dropdown",(()=>{this.searchInput.focus({preventScroll:!0}),this.selectallEnable()}))}async renderDropdown(){const{html:html,js:js}=await(0,_templates.renderForPromise)("gradereport_grader/collapse/collapseresults",{instance:this.instance,results:this.getMatchedResults(),searchTerm:this.getSearchTerm()});(0,_templates.replaceNodeContents)(this.getHTMLElements().searchDropdown,html,js),this.selectallEnable();this.component.querySelector(selectors_formDropdown).querySelector('[data-action="'.concat(selectors_formItems.save,'"')).disabled=!0}selectallEnable(){this.component.querySelector(selectors_formDropdown).querySelector('[data-action="selectall"]').disabled=0===this.getMatchedResults().length}fetchCustomFieldValues(){return[...document.querySelectorAll("[data-collapse-name]")].map((field=>[field.parentElement.dataset.col,field.dataset.collapseName]))}fetchRequiredUserStrings(){if(!this.userStrings){const requiredStrings=["username","firstname","lastname","email","city","country","department","institution","idnumber","phone1","phone2"];this.userStrings=(0,_str.getStrings)(requiredStrings.map((key=>({key:key})))).then((stringArray=>new Map(requiredStrings.map(((key,index)=>[key,stringArray[index]])))))}return this.userStrings}fetchRequiredGradeStrings(){return this.gradeStrings||(this.gradeStrings=Repository.gradeItems(this.courseID).then((result=>new Map(result.gradeItems.map((key=>[key.id,key])))))),this.gradeStrings}}return _exports.default=ColumnSearch,_exports.default})); //# sourceMappingURL=collapse.min.js.map \ No newline at end of file diff --git a/grade/report/grader/amd/build/collapse.min.js.map b/grade/report/grader/amd/build/collapse.min.js.map index 8f85fe49dbdcb..2d73c9193a4e5 100644 --- a/grade/report/grader/amd/build/collapse.min.js.map +++ b/grade/report/grader/amd/build/collapse.min.js.map @@ -1 +1 @@ -{"version":3,"file":"collapse.min.js","sources":["../src/collapse.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 * Allow the user to show and hide columns of the report at will.\n *\n * @module gradereport_grader/collapse\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport * as Repository from 'gradereport_grader/collapse/repository';\nimport search_combobox from 'core/comboboxsearch/search_combobox';\nimport {renderForPromise, replaceNodeContents, replaceNode} from 'core/templates';\nimport {debounce} from 'core/utils';\nimport $ from 'jquery';\nimport {getStrings} from 'core/str';\nimport CustomEvents from \"core/custom_interaction_events\";\nimport storage from 'core/localstorage';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\n\n// Contain our selectors within this file until they could be of use elsewhere.\nconst selectors = {\n component: '.collapse-columns',\n formDropdown: '.columnsdropdownform',\n formItems: {\n cancel: 'cancel',\n save: 'save',\n checked: 'input[type=\"checkbox\"]:checked',\n currentlyUnchecked: 'input[type=\"checkbox\"]:not([data-action=\"selectall\"])',\n },\n hider: 'hide',\n expand: 'expand',\n colVal: '[data-col]',\n itemVal: '[data-itemid]',\n content: '[data-collapse=\"content\"]',\n sort: '[data-collapse=\"sort\"]',\n expandbutton: '[data-collapse=\"expandbutton\"]',\n rangerowcell: '[data-collapse=\"rangerowcell\"]',\n avgrowcell: '[data-collapse=\"avgrowcell\"]',\n menu: '[data-collapse=\"menu\"]',\n icons: '.data-collapse_gradeicons',\n count: '[data-collapse=\"count\"]',\n placeholder: '.collapsecolumndropdown [data-region=\"placeholder\"]',\n fullDropdown: '.collapsecolumndropdown',\n searchResultContainer: '.searchresultitemscontainer',\n};\n\nconst countIndicator = document.querySelector(selectors.count);\n\nexport default class ColumnSearch extends search_combobox {\n\n userID = -1;\n courseID = null;\n defaultSort = '';\n\n nodes = [];\n\n gradeStrings = null;\n userStrings = null;\n stringMap = [];\n\n static init(userID, courseID, defaultSort) {\n return new ColumnSearch(userID, courseID, defaultSort);\n }\n\n constructor(userID, courseID, defaultSort) {\n super();\n this.userID = userID;\n this.courseID = courseID;\n this.defaultSort = defaultSort;\n this.component = document.querySelector(selectors.component);\n\n const pendingPromise = new Pending();\n // Display a loader whilst collapsing appropriate columns (based on the locally stored state for the current user).\n addIconToContainer(document.querySelector('.gradeparent')).then((loader) => {\n setTimeout(() => {\n // Get the users' checked columns to change.\n this.getDataset().forEach((item) => {\n this.nodesUpdate(item);\n });\n this.renderDefault();\n\n // Once the grade categories have been re-collapsed, remove the loader and display the Gradebook setup content.\n loader.remove();\n document.querySelector('.gradereport-grader-table').classList.remove('d-none');\n }, 10);\n }).then(() => pendingPromise.resolve()).catch(Notification.exception);\n\n this.$component.on('hide.bs.dropdown', () => {\n const searchResultContainer = this.component.querySelector(selectors.searchResultContainer);\n searchResultContainer.scrollTop = 0;\n\n // Use setTimeout to make sure the following code is executed after the click event is handled.\n setTimeout(() => {\n if (this.searchInput.value !== '') {\n this.searchInput.value = '';\n this.searchInput.dispatchEvent(new Event('input', {bubbles: true}));\n }\n });\n });\n }\n\n /**\n * The overall div that contains the searching widget.\n *\n * @returns {string}\n */\n componentSelector() {\n return '.collapse-columns';\n }\n\n /**\n * The dropdown div that contains the searching widget result space.\n *\n * @returns {string}\n */\n dropdownSelector() {\n return '.searchresultitemscontainer';\n }\n\n /**\n * Return the dataset that we will be searching upon.\n *\n * @returns {Array}\n */\n getDataset() {\n if (!this.dataset) {\n const cols = this.fetchDataset();\n this.dataset = JSON.parse(cols) ? JSON.parse(cols).split(',') : [];\n }\n this.datasetSize = this.dataset.length;\n return this.dataset;\n }\n\n /**\n * Get the data we will be searching against in this component.\n *\n * @returns {string}\n */\n fetchDataset() {\n return storage.get(`gradereport_grader_collapseditems_${this.courseID}_${this.userID}`);\n }\n\n /**\n * Given a user performs an action, update the users' preferences.\n */\n setPreferences() {\n storage.set(`gradereport_grader_collapseditems_${this.courseID}_${this.userID}`,\n JSON.stringify(this.getDataset().join(','))\n );\n }\n\n /**\n * Register clickable event listeners.\n */\n registerClickHandlers() {\n // Register click events within the component.\n this.component.addEventListener('click', this.clickHandler.bind(this));\n\n document.addEventListener('click', this.docClickHandler.bind(this));\n }\n\n /**\n * The handler for when a user interacts with the component.\n *\n * @param {MouseEvent} e The triggering event that we are working with.\n */\n clickHandler(e) {\n super.clickHandler(e);\n // Prevent BS from closing the dropdown if they click elsewhere within the dropdown besides the form.\n if (e.target.closest(selectors.fullDropdown)) {\n e.stopPropagation();\n }\n }\n\n /**\n * Externally defined click function to improve memory handling.\n *\n * @param {MouseEvent} e\n * @returns {Promise}\n */\n async docClickHandler(e) {\n if (e.target.dataset.hider === selectors.hider) {\n e.preventDefault();\n const desiredToHide = e.target.closest(selectors.colVal) ?\n e.target.closest(selectors.colVal)?.dataset.col :\n e.target.closest(selectors.itemVal)?.dataset.itemid;\n const idx = this.getDataset().indexOf(desiredToHide);\n if (idx === -1) {\n this.getDataset().push(desiredToHide);\n }\n await this.prefcountpipe();\n\n this.nodesUpdate(desiredToHide);\n }\n\n if (e.target.closest('button')?.dataset.hider === selectors.expand) {\n e.preventDefault();\n const desiredToHide = e.target.closest(selectors.colVal) ?\n e.target.closest(selectors.colVal)?.dataset.col :\n e.target.closest(selectors.itemVal)?.dataset.itemid;\n const idx = this.getDataset().indexOf(desiredToHide);\n this.getDataset().splice(idx, 1);\n\n await this.prefcountpipe();\n\n this.nodesUpdate(e.target.closest(selectors.colVal)?.dataset.col);\n this.nodesUpdate(e.target.closest(selectors.colVal)?.dataset.itemid);\n }\n }\n\n /**\n * Handle any keyboard inputs.\n */\n registerInputEvents() {\n // Register & handle the text input.\n this.searchInput.addEventListener('input', debounce(async() => {\n if (this.getSearchTerm() === this.searchInput.value && this.searchResultsVisible()) {\n window.console.warn(`Search term matches input value - skipping`);\n // Debounce can happen multiple times quickly.\n return;\n }\n this.setSearchTerms(this.searchInput.value);\n // We can also require a set amount of input before search.\n if (this.searchInput.value === '') {\n // Hide the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.add('d-none');\n } else {\n // Display the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.remove('d-none');\n }\n const pendingPromise = new Pending();\n // User has given something for us to filter against.\n await this.filterrenderpipe().then(() => {\n pendingPromise.resolve();\n return true;\n });\n }, 300, {pending: true}));\n }\n\n /**\n * Handle the form submission within the dropdown.\n */\n registerFormEvents() {\n const form = this.component.querySelector(selectors.formDropdown);\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n CustomEvents.define(document, events);\n\n const selectall = form.querySelector('[data-action=\"selectall\"]');\n\n // Register clicks & keyboard form handling.\n events.forEach((event) => {\n const submitBtn = form.querySelector(`[data-action=\"${selectors.formItems.save}\"`);\n form.addEventListener(event, (e) => {\n // Stop Bootstrap from being clever.\n e.stopPropagation();\n const input = e.target.closest('input');\n if (input) {\n // If the user is unchecking an item, we need to uncheck the select all if it's checked.\n if (selectall.checked && !input.checked) {\n selectall.checked = false;\n }\n const checkedCount = Array.from(form.querySelectorAll(selectors.formItems.checked)).length;\n // Check if any are clicked or not then change disabled.\n submitBtn.disabled = checkedCount <= 0;\n }\n }, false);\n\n // Stop Bootstrap from being clever.\n this.searchInput.addEventListener(event, e => e.stopPropagation());\n this.clearSearchButton.addEventListener(event, async(e) => {\n e.stopPropagation();\n this.searchInput.value = '';\n this.setSearchTerms(this.searchInput.value);\n await this.filterrenderpipe();\n });\n selectall.addEventListener(event, (e) => {\n // Stop Bootstrap from being clever.\n e.stopPropagation();\n if (!selectall.checked) {\n const touncheck = Array.from(form.querySelectorAll(selectors.formItems.checked));\n touncheck.forEach(item => {\n item.checked = false;\n });\n submitBtn.disabled = true;\n } else {\n const currentUnchecked = Array.from(form.querySelectorAll(selectors.formItems.currentlyUnchecked));\n currentUnchecked.forEach(item => {\n item.checked = true;\n });\n submitBtn.disabled = false;\n }\n });\n });\n\n form.addEventListener('submit', async(e) => {\n e.preventDefault();\n if (e.submitter.dataset.action === selectors.formItems.cancel) {\n $(this.component).dropdown('toggle');\n return;\n }\n // Get the users' checked columns to change.\n const checkedItems = [...form.elements].filter(item => item.checked);\n checkedItems.forEach((item) => {\n const idx = this.getDataset().indexOf(item.dataset.collapse);\n this.getDataset().splice(idx, 1);\n this.nodesUpdate(item.dataset.collapse);\n });\n // Reset the check all & submit to false just in case.\n selectall.checked = false;\n e.submitter.disabled = true;\n await this.prefcountpipe();\n });\n }\n\n nodesUpdate(item) {\n const colNodesToHide = [...document.querySelectorAll(`[data-col=\"${item}\"]`)];\n const itemIDNodesToHide = [...document.querySelectorAll(`[data-itemid=\"${item}\"]`)];\n this.nodes = [...colNodesToHide, ...itemIDNodesToHide];\n this.updateDisplay();\n }\n\n /**\n * Update the user preferences, count display then render the results.\n *\n * @returns {Promise}\n */\n async prefcountpipe() {\n this.setPreferences();\n this.countUpdate();\n await this.filterrenderpipe();\n }\n\n /**\n * Dictate to the search component how and what we want to match upon.\n *\n * @param {Array} filterableData\n * @returns {Array} An array of objects containing the system reference and the user readable value.\n */\n async filterDataset(filterableData) {\n const stringUserMap = await this.fetchRequiredUserStrings();\n const stringGradeMap = await this.fetchRequiredGradeStrings();\n // Custom user profile fields are not in our string map and need a bit of extra love.\n const customFieldMap = this.fetchCustomFieldValues();\n this.stringMap = new Map([...stringGradeMap, ...stringUserMap, ...customFieldMap]);\n\n const searching = filterableData.map(s => {\n const mapObj = this.stringMap.get(s);\n if (mapObj === undefined) {\n return {key: s, string: s};\n }\n return {\n key: s,\n string: mapObj.itemname ?? this.stringMap.get(s),\n category: mapObj.category ?? '',\n };\n });\n // Sometimes we just want to show everything.\n if (this.getPreppedSearchTerm() === '') {\n return searching;\n }\n // Other times we want to actually filter the content.\n return searching.filter((col) => {\n return col.string.toString().toLowerCase().includes(this.getPreppedSearchTerm());\n });\n }\n\n /**\n * Given we have a subset of the dataset, set the field that we matched upon to inform the end user.\n */\n filterMatchDataset() {\n this.setMatchedResults(\n this.getMatchedResults().map((column) => {\n return {\n name: column.key,\n displayName: column.string ?? column.key,\n category: column.category ?? '',\n };\n })\n );\n }\n\n /**\n * With an array of nodes, switch their classes and values.\n */\n updateDisplay() {\n this.nodes.forEach((element) => {\n const content = element.querySelector(selectors.content);\n const sort = element.querySelector(selectors.sort);\n const expandButton = element.querySelector(selectors.expandbutton);\n const rangeRowCell = element.querySelector(selectors.rangerowcell);\n const avgRowCell = element.querySelector(selectors.avgrowcell);\n const nodeSet = [\n element.querySelector(selectors.menu),\n element.querySelector(selectors.icons),\n content\n ];\n\n // This can be further improved to reduce redundant similar calls.\n if (element.classList.contains('cell')) {\n // The column is actively being sorted, lets reset that and reload the page.\n if (sort !== null) {\n window.location = this.defaultSort;\n }\n if (content === null) {\n // If it's not a content cell, it must be an overall average or a range cell.\n const rowCell = avgRowCell ?? rangeRowCell;\n\n rowCell?.classList.toggle('d-none');\n } else if (content.classList.contains('d-none')) {\n // We should always have content but some cells do not contain menus or other actions.\n element.classList.remove('collapsed');\n // If there are many nodes, apply the following.\n if (content.childNodes.length > 1) {\n content.classList.add('d-flex');\n }\n nodeSet.forEach(node => {\n node?.classList.remove('d-none');\n });\n expandButton?.classList.add('d-none');\n } else {\n element.classList.add('collapsed');\n content.classList.remove('d-flex');\n nodeSet.forEach(node => {\n node?.classList.add('d-none');\n });\n expandButton?.classList.remove('d-none');\n }\n }\n });\n }\n\n /**\n * Update the visual count of collapsed columns or hide the count all together.\n */\n countUpdate() {\n countIndicator.textContent = this.getDatasetSize();\n if (this.getDatasetSize() > 0) {\n this.component.parentElement.classList.add('d-flex');\n this.component.parentElement.classList.remove('d-none');\n } else {\n this.component.parentElement.classList.remove('d-flex');\n this.component.parentElement.classList.add('d-none');\n }\n }\n\n /**\n * Build the content then replace the node by default we want our form to exist.\n */\n async renderDefault() {\n this.setMatchedResults(await this.filterDataset(this.getDataset()));\n this.filterMatchDataset();\n\n // Update the collapsed button pill.\n this.countUpdate();\n const {html, js} = await renderForPromise('gradereport_grader/collapse/collapsebody', {\n 'instance': this.instance,\n 'results': this.getMatchedResults(),\n 'userid': this.userID,\n });\n replaceNode(selectors.placeholder, html, js);\n this.updateNodes();\n\n // Given we now have the body, we can set up more triggers.\n this.registerFormEvents();\n this.registerInputEvents();\n\n // Add a small BS listener so that we can set the focus correctly on open.\n this.$component.on('shown.bs.dropdown', () => {\n this.searchInput.focus({preventScroll: true});\n this.selectallEnable();\n });\n }\n\n /**\n * Build the content then replace the node.\n */\n async renderDropdown() {\n const {html, js} = await renderForPromise('gradereport_grader/collapse/collapseresults', {\n instance: this.instance,\n 'results': this.getMatchedResults(),\n 'searchTerm': this.getSearchTerm(),\n });\n replaceNodeContents(this.getHTMLElements().searchDropdown, html, js);\n this.selectallEnable();\n // Reset the expand button to be disabled as we have re-rendered the dropdown.\n const form = this.component.querySelector(selectors.formDropdown);\n const expandButton = form.querySelector(`[data-action=\"${selectors.formItems.save}\"`);\n expandButton.disabled = true;\n }\n\n /**\n * Given we render the dropdown, Determine if we want to enable the select all checkbox.\n */\n selectallEnable() {\n const form = this.component.querySelector(selectors.formDropdown);\n const selectall = form.querySelector('[data-action=\"selectall\"]');\n selectall.disabled = this.getMatchedResults().length === 0;\n }\n\n /**\n * If we have any custom user profile fields, grab their system & readable names to add to our string map.\n *\n * @returns {array} An array of associated string arrays ready for our map.\n */\n fetchCustomFieldValues() {\n const customFields = document.querySelectorAll('[data-collapse-name]');\n // Cast from NodeList to array to grab all the values.\n return [...customFields].map(field => [field.parentElement.dataset.col, field.dataset.collapseName]);\n }\n\n /**\n * Given the set of profile fields we can possibly search, fetch their strings,\n * so we can report to screen readers the field that matched.\n *\n * @returns {Promise}\n */\n fetchRequiredUserStrings() {\n if (!this.userStrings) {\n const requiredStrings = [\n 'username',\n 'firstname',\n 'lastname',\n 'email',\n 'city',\n 'country',\n 'department',\n 'institution',\n 'idnumber',\n 'phone1',\n 'phone2',\n ];\n this.userStrings = getStrings(requiredStrings.map((key) => ({key})))\n .then((stringArray) => new Map(\n requiredStrings.map((key, index) => ([key, stringArray[index]]))\n ));\n }\n return this.userStrings;\n }\n\n /**\n * Given the set of gradable items we can possibly search, fetch their strings,\n * so we can report to screen readers the field that matched.\n *\n * @returns {Promise}\n */\n fetchRequiredGradeStrings() {\n if (!this.gradeStrings) {\n this.gradeStrings = Repository.gradeItems(this.courseID)\n .then((result) => new Map(\n result.gradeItems.map(key => ([key.id, key]))\n ));\n }\n return this.gradeStrings;\n }\n}\n"],"names":["selectors","cancel","save","checked","currentlyUnchecked","countIndicator","document","querySelector","ColumnSearch","search_combobox","userID","courseID","defaultSort","constructor","component","pendingPromise","Pending","then","loader","setTimeout","getDataset","forEach","item","nodesUpdate","renderDefault","remove","classList","resolve","catch","Notification","exception","$component","on","this","scrollTop","searchInput","value","dispatchEvent","Event","bubbles","componentSelector","dropdownSelector","dataset","cols","fetchDataset","JSON","parse","split","datasetSize","length","storage","get","setPreferences","set","stringify","join","registerClickHandlers","addEventListener","clickHandler","bind","docClickHandler","e","target","closest","stopPropagation","hider","preventDefault","desiredToHide","_e$target$closest","col","_e$target$closest2","itemid","indexOf","push","prefcountpipe","_e$target$closest4","_e$target$closest5","idx","splice","_e$target$closest6","_e$target$closest7","registerInputEvents","async","getSearchTerm","searchResultsVisible","window","console","warn","setSearchTerms","clearSearchButton","add","filterrenderpipe","pending","registerFormEvents","form","events","CustomEvents","activate","keyboardActivate","define","selectall","event","submitBtn","input","checkedCount","Array","from","querySelectorAll","disabled","submitter","action","dropdown","elements","filter","collapse","colNodesToHide","itemIDNodesToHide","nodes","updateDisplay","countUpdate","filterableData","stringUserMap","fetchRequiredUserStrings","stringGradeMap","fetchRequiredGradeStrings","customFieldMap","fetchCustomFieldValues","stringMap","Map","searching","map","s","mapObj","undefined","key","string","itemname","category","getPreppedSearchTerm","toString","toLowerCase","includes","filterMatchDataset","setMatchedResults","getMatchedResults","column","name","displayName","element","content","sort","expandButton","rangeRowCell","avgRowCell","nodeSet","contains","location","rowCell","toggle","childNodes","node","textContent","getDatasetSize","parentElement","filterDataset","html","js","instance","updateNodes","focus","preventScroll","selectallEnable","getHTMLElements","searchDropdown","field","collapseName","userStrings","requiredStrings","stringArray","index","gradeStrings","Repository","gradeItems","result","id"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"collapse.min.js","sources":["../src/collapse.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 * Allow the user to show and hide columns of the report at will.\n *\n * @module gradereport_grader/collapse\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport * as Repository from 'gradereport_grader/collapse/repository';\nimport search_combobox from 'core/comboboxsearch/search_combobox';\nimport {renderForPromise, replaceNodeContents, replaceNode} from 'core/templates';\nimport {debounce} from 'core/utils';\nimport Dropdown from 'theme_boost/bootstrap/dropdown';\nimport {getStrings} from 'core/str';\nimport CustomEvents from \"core/custom_interaction_events\";\nimport storage from 'core/localstorage';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\n\n// Contain our selectors within this file until they could be of use elsewhere.\nconst selectors = {\n component: '.collapse-columns',\n formDropdown: '.columnsdropdownform',\n formItems: {\n cancel: 'cancel',\n save: 'save',\n checked: 'input[type=\"checkbox\"]:checked',\n currentlyUnchecked: 'input[type=\"checkbox\"]:not([data-action=\"selectall\"])',\n },\n hider: 'hide',\n expand: 'expand',\n colVal: '[data-col]',\n itemVal: '[data-itemid]',\n content: '[data-collapse=\"content\"]',\n sort: '[data-collapse=\"sort\"]',\n expandbutton: '[data-collapse=\"expandbutton\"]',\n rangerowcell: '[data-collapse=\"rangerowcell\"]',\n avgrowcell: '[data-collapse=\"avgrowcell\"]',\n menu: '[data-collapse=\"menu\"]',\n icons: '.data-collapse_gradeicons',\n count: '[data-collapse=\"count\"]',\n placeholder: '.collapsecolumndropdown [data-region=\"placeholder\"]',\n fullDropdown: '.collapsecolumndropdown',\n searchResultContainer: '.searchresultitemscontainer',\n};\n\nconst countIndicator = document.querySelector(selectors.count);\n\nexport default class ColumnSearch extends search_combobox {\n\n userID = -1;\n courseID = null;\n defaultSort = '';\n\n nodes = [];\n\n gradeStrings = null;\n userStrings = null;\n stringMap = [];\n\n static init(userID, courseID, defaultSort) {\n return new ColumnSearch(userID, courseID, defaultSort);\n }\n\n constructor(userID, courseID, defaultSort) {\n super();\n this.userID = userID;\n this.courseID = courseID;\n this.defaultSort = defaultSort;\n this.component = document.querySelector(selectors.component);\n\n const pendingPromise = new Pending();\n // Display a loader whilst collapsing appropriate columns (based on the locally stored state for the current user).\n addIconToContainer(document.querySelector('.gradeparent')).then((loader) => {\n setTimeout(() => {\n // Get the users' checked columns to change.\n this.getDataset().forEach((item) => {\n this.nodesUpdate(item);\n });\n this.renderDefault();\n\n // Once the grade categories have been re-collapsed, remove the loader and display the Gradebook setup content.\n loader.remove();\n document.querySelector('.gradereport-grader-table').classList.remove('d-none');\n }, 10);\n }).then(() => pendingPromise.resolve()).catch(Notification.exception);\n\n this.component.addEventListener('hide.bs.dropdown', () => {\n const searchResultContainer = this.component.querySelector(selectors.searchResultContainer);\n searchResultContainer.scrollTop = 0;\n\n // Use setTimeout to make sure the following code is executed after the click event is handled.\n setTimeout(() => {\n if (this.searchInput.value !== '') {\n this.searchInput.value = '';\n this.searchInput.dispatchEvent(new Event('input', {bubbles: true}));\n }\n });\n });\n }\n\n /**\n * The overall div that contains the searching widget.\n *\n * @returns {string}\n */\n componentSelector() {\n return '.collapse-columns';\n }\n\n /**\n * The dropdown div that contains the searching widget result space.\n *\n * @returns {string}\n */\n dropdownSelector() {\n return '.searchresultitemscontainer';\n }\n\n /**\n * Return the dataset that we will be searching upon.\n *\n * @returns {Array}\n */\n getDataset() {\n if (!this.dataset) {\n const cols = this.fetchDataset();\n this.dataset = JSON.parse(cols) ? JSON.parse(cols).split(',') : [];\n }\n this.datasetSize = this.dataset.length;\n return this.dataset;\n }\n\n /**\n * Get the data we will be searching against in this component.\n *\n * @returns {string}\n */\n fetchDataset() {\n return storage.get(`gradereport_grader_collapseditems_${this.courseID}_${this.userID}`);\n }\n\n /**\n * Given a user performs an action, update the users' preferences.\n */\n setPreferences() {\n storage.set(`gradereport_grader_collapseditems_${this.courseID}_${this.userID}`,\n JSON.stringify(this.getDataset().join(','))\n );\n }\n\n /**\n * Register clickable event listeners.\n */\n registerClickHandlers() {\n // Register click events within the component.\n this.component.addEventListener('click', this.clickHandler.bind(this));\n\n document.addEventListener('click', this.docClickHandler.bind(this));\n }\n\n /**\n * The handler for when a user interacts with the component.\n *\n * @param {MouseEvent} e The triggering event that we are working with.\n */\n clickHandler(e) {\n super.clickHandler(e);\n // Prevent BS from closing the dropdown if they click elsewhere within the dropdown besides the form.\n if (e.target.closest(selectors.fullDropdown)) {\n e.stopPropagation();\n }\n }\n\n /**\n * Externally defined click function to improve memory handling.\n *\n * @param {MouseEvent} e\n * @returns {Promise}\n */\n async docClickHandler(e) {\n if (e.target.dataset.hider === selectors.hider) {\n e.preventDefault();\n const desiredToHide = e.target.closest(selectors.colVal) ?\n e.target.closest(selectors.colVal)?.dataset.col :\n e.target.closest(selectors.itemVal)?.dataset.itemid;\n const idx = this.getDataset().indexOf(desiredToHide);\n if (idx === -1) {\n this.getDataset().push(desiredToHide);\n }\n await this.prefcountpipe();\n\n this.nodesUpdate(desiredToHide);\n }\n\n if (e.target.closest('button')?.dataset.hider === selectors.expand) {\n e.preventDefault();\n const desiredToHide = e.target.closest(selectors.colVal) ?\n e.target.closest(selectors.colVal)?.dataset.col :\n e.target.closest(selectors.itemVal)?.dataset.itemid;\n const idx = this.getDataset().indexOf(desiredToHide);\n this.getDataset().splice(idx, 1);\n\n await this.prefcountpipe();\n\n this.nodesUpdate(e.target.closest(selectors.colVal)?.dataset.col);\n this.nodesUpdate(e.target.closest(selectors.colVal)?.dataset.itemid);\n }\n }\n\n /**\n * Handle any keyboard inputs.\n */\n registerInputEvents() {\n // Register & handle the text input.\n this.searchInput.addEventListener('input', debounce(async() => {\n if (this.getSearchTerm() === this.searchInput.value && this.searchResultsVisible()) {\n window.console.warn(`Search term matches input value - skipping`);\n // Debounce can happen multiple times quickly.\n return;\n }\n this.setSearchTerms(this.searchInput.value);\n // We can also require a set amount of input before search.\n if (this.searchInput.value === '') {\n // Hide the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.add('d-none');\n } else {\n // Display the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.remove('d-none');\n }\n const pendingPromise = new Pending();\n // User has given something for us to filter against.\n await this.filterrenderpipe().then(() => {\n pendingPromise.resolve();\n return true;\n });\n }, 300, {pending: true}));\n }\n\n /**\n * Handle the form submission within the dropdown.\n */\n registerFormEvents() {\n const form = this.component.querySelector(selectors.formDropdown);\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n CustomEvents.define(document, events);\n\n const selectall = form.querySelector('[data-action=\"selectall\"]');\n\n // Register clicks & keyboard form handling.\n events.forEach((event) => {\n const submitBtn = form.querySelector(`[data-action=\"${selectors.formItems.save}\"`);\n form.addEventListener(event, (e) => {\n // Stop Bootstrap from being clever.\n e.stopPropagation();\n const input = e.target.closest('input');\n if (input) {\n // If the user is unchecking an item, we need to uncheck the select all if it's checked.\n if (selectall.checked && !input.checked) {\n selectall.checked = false;\n }\n const checkedCount = Array.from(form.querySelectorAll(selectors.formItems.checked)).length;\n // Check if any are clicked or not then change disabled.\n submitBtn.disabled = checkedCount <= 0;\n }\n }, false);\n\n // Stop Bootstrap from being clever.\n this.searchInput.addEventListener(event, e => e.stopPropagation());\n this.clearSearchButton.addEventListener(event, async(e) => {\n e.stopPropagation();\n this.searchInput.value = '';\n this.setSearchTerms(this.searchInput.value);\n await this.filterrenderpipe();\n });\n selectall.addEventListener(event, (e) => {\n // Stop Bootstrap from being clever.\n e.stopPropagation();\n if (!selectall.checked) {\n const touncheck = Array.from(form.querySelectorAll(selectors.formItems.checked));\n touncheck.forEach(item => {\n item.checked = false;\n });\n submitBtn.disabled = true;\n } else {\n const currentUnchecked = Array.from(form.querySelectorAll(selectors.formItems.currentlyUnchecked));\n currentUnchecked.forEach(item => {\n item.checked = true;\n });\n submitBtn.disabled = false;\n }\n });\n });\n\n form.addEventListener('submit', async(e) => {\n e.preventDefault();\n if (e.submitter.dataset.action === selectors.formItems.cancel) {\n Dropdown.getOrCreateInstance(this.component).toggle();\n return;\n }\n // Get the users' checked columns to change.\n const checkedItems = [...form.elements].filter(item => item.checked);\n checkedItems.forEach((item) => {\n const idx = this.getDataset().indexOf(item.dataset.collapse);\n this.getDataset().splice(idx, 1);\n this.nodesUpdate(item.dataset.collapse);\n });\n // Reset the check all & submit to false just in case.\n selectall.checked = false;\n e.submitter.disabled = true;\n await this.prefcountpipe();\n });\n }\n\n nodesUpdate(item) {\n const colNodesToHide = [...document.querySelectorAll(`[data-col=\"${item}\"]`)];\n const itemIDNodesToHide = [...document.querySelectorAll(`[data-itemid=\"${item}\"]`)];\n this.nodes = [...colNodesToHide, ...itemIDNodesToHide];\n this.updateDisplay();\n }\n\n /**\n * Update the user preferences, count display then render the results.\n *\n * @returns {Promise}\n */\n async prefcountpipe() {\n this.setPreferences();\n this.countUpdate();\n await this.filterrenderpipe();\n }\n\n /**\n * Dictate to the search component how and what we want to match upon.\n *\n * @param {Array} filterableData\n * @returns {Array} An array of objects containing the system reference and the user readable value.\n */\n async filterDataset(filterableData) {\n const stringUserMap = await this.fetchRequiredUserStrings();\n const stringGradeMap = await this.fetchRequiredGradeStrings();\n // Custom user profile fields are not in our string map and need a bit of extra love.\n const customFieldMap = this.fetchCustomFieldValues();\n this.stringMap = new Map([...stringGradeMap, ...stringUserMap, ...customFieldMap]);\n\n const searching = filterableData.map(s => {\n const mapObj = this.stringMap.get(s);\n if (mapObj === undefined) {\n return {key: s, string: s};\n }\n return {\n key: s,\n string: mapObj.itemname ?? this.stringMap.get(s),\n category: mapObj.category ?? '',\n };\n });\n // Sometimes we just want to show everything.\n if (this.getPreppedSearchTerm() === '') {\n return searching;\n }\n // Other times we want to actually filter the content.\n return searching.filter((col) => {\n return col.string.toString().toLowerCase().includes(this.getPreppedSearchTerm());\n });\n }\n\n /**\n * Given we have a subset of the dataset, set the field that we matched upon to inform the end user.\n */\n filterMatchDataset() {\n this.setMatchedResults(\n this.getMatchedResults().map((column) => {\n return {\n name: column.key,\n displayName: column.string ?? column.key,\n category: column.category ?? '',\n };\n })\n );\n }\n\n /**\n * With an array of nodes, switch their classes and values.\n */\n updateDisplay() {\n this.nodes.forEach((element) => {\n const content = element.querySelector(selectors.content);\n const sort = element.querySelector(selectors.sort);\n const expandButton = element.querySelector(selectors.expandbutton);\n const rangeRowCell = element.querySelector(selectors.rangerowcell);\n const avgRowCell = element.querySelector(selectors.avgrowcell);\n const nodeSet = [\n element.querySelector(selectors.menu),\n element.querySelector(selectors.icons),\n content\n ];\n\n // This can be further improved to reduce redundant similar calls.\n if (element.classList.contains('cell')) {\n // The column is actively being sorted, lets reset that and reload the page.\n if (sort !== null) {\n window.location = this.defaultSort;\n }\n if (content === null) {\n // If it's not a content cell, it must be an overall average or a range cell.\n const rowCell = avgRowCell ?? rangeRowCell;\n\n rowCell?.classList.toggle('d-none');\n } else if (content.classList.contains('d-none')) {\n // We should always have content but some cells do not contain menus or other actions.\n element.classList.remove('collapsed');\n // If there are many nodes, apply the following.\n if (content.childNodes.length > 1) {\n content.classList.add('d-flex');\n }\n nodeSet.forEach(node => {\n node?.classList.remove('d-none');\n });\n expandButton?.classList.add('d-none');\n } else {\n element.classList.add('collapsed');\n content.classList.remove('d-flex');\n nodeSet.forEach(node => {\n node?.classList.add('d-none');\n });\n expandButton?.classList.remove('d-none');\n }\n }\n });\n }\n\n /**\n * Update the visual count of collapsed columns or hide the count all together.\n */\n countUpdate() {\n countIndicator.textContent = this.getDatasetSize();\n if (this.getDatasetSize() > 0) {\n this.component.parentElement.classList.add('d-flex');\n this.component.parentElement.classList.remove('d-none');\n } else {\n this.component.parentElement.classList.remove('d-flex');\n this.component.parentElement.classList.add('d-none');\n }\n }\n\n /**\n * Build the content then replace the node by default we want our form to exist.\n */\n async renderDefault() {\n this.setMatchedResults(await this.filterDataset(this.getDataset()));\n this.filterMatchDataset();\n\n // Update the collapsed button pill.\n this.countUpdate();\n const {html, js} = await renderForPromise('gradereport_grader/collapse/collapsebody', {\n 'instance': this.instance,\n 'results': this.getMatchedResults(),\n 'userid': this.userID,\n });\n replaceNode(selectors.placeholder, html, js);\n this.updateNodes();\n\n // Given we now have the body, we can set up more triggers.\n this.registerFormEvents();\n this.registerInputEvents();\n\n // Add a small BS listener so that we can set the focus correctly on open.\n this.component.addEventListener('shown.bs.dropdown', () => {\n this.searchInput.focus({preventScroll: true});\n this.selectallEnable();\n });\n }\n\n /**\n * Build the content then replace the node.\n */\n async renderDropdown() {\n const {html, js} = await renderForPromise('gradereport_grader/collapse/collapseresults', {\n instance: this.instance,\n 'results': this.getMatchedResults(),\n 'searchTerm': this.getSearchTerm(),\n });\n replaceNodeContents(this.getHTMLElements().searchDropdown, html, js);\n this.selectallEnable();\n // Reset the expand button to be disabled as we have re-rendered the dropdown.\n const form = this.component.querySelector(selectors.formDropdown);\n const expandButton = form.querySelector(`[data-action=\"${selectors.formItems.save}\"`);\n expandButton.disabled = true;\n }\n\n /**\n * Given we render the dropdown, Determine if we want to enable the select all checkbox.\n */\n selectallEnable() {\n const form = this.component.querySelector(selectors.formDropdown);\n const selectall = form.querySelector('[data-action=\"selectall\"]');\n selectall.disabled = this.getMatchedResults().length === 0;\n }\n\n /**\n * If we have any custom user profile fields, grab their system & readable names to add to our string map.\n *\n * @returns {array} An array of associated string arrays ready for our map.\n */\n fetchCustomFieldValues() {\n const customFields = document.querySelectorAll('[data-collapse-name]');\n // Cast from NodeList to array to grab all the values.\n return [...customFields].map(field => [field.parentElement.dataset.col, field.dataset.collapseName]);\n }\n\n /**\n * Given the set of profile fields we can possibly search, fetch their strings,\n * so we can report to screen readers the field that matched.\n *\n * @returns {Promise}\n */\n fetchRequiredUserStrings() {\n if (!this.userStrings) {\n const requiredStrings = [\n 'username',\n 'firstname',\n 'lastname',\n 'email',\n 'city',\n 'country',\n 'department',\n 'institution',\n 'idnumber',\n 'phone1',\n 'phone2',\n ];\n this.userStrings = getStrings(requiredStrings.map((key) => ({key})))\n .then((stringArray) => new Map(\n requiredStrings.map((key, index) => ([key, stringArray[index]]))\n ));\n }\n return this.userStrings;\n }\n\n /**\n * Given the set of gradable items we can possibly search, fetch their strings,\n * so we can report to screen readers the field that matched.\n *\n * @returns {Promise}\n */\n fetchRequiredGradeStrings() {\n if (!this.gradeStrings) {\n this.gradeStrings = Repository.gradeItems(this.courseID)\n .then((result) => new Map(\n result.gradeItems.map(key => ([key.id, key]))\n ));\n }\n return this.gradeStrings;\n }\n}\n"],"names":["selectors","cancel","save","checked","currentlyUnchecked","countIndicator","document","querySelector","ColumnSearch","search_combobox","userID","courseID","defaultSort","constructor","component","pendingPromise","Pending","then","loader","setTimeout","getDataset","forEach","item","nodesUpdate","renderDefault","remove","classList","resolve","catch","Notification","exception","addEventListener","this","scrollTop","searchInput","value","dispatchEvent","Event","bubbles","componentSelector","dropdownSelector","dataset","cols","fetchDataset","JSON","parse","split","datasetSize","length","storage","get","setPreferences","set","stringify","join","registerClickHandlers","clickHandler","bind","docClickHandler","e","target","closest","stopPropagation","hider","preventDefault","desiredToHide","_e$target$closest","col","_e$target$closest2","itemid","indexOf","push","prefcountpipe","_e$target$closest4","_e$target$closest5","idx","splice","_e$target$closest6","_e$target$closest7","registerInputEvents","async","getSearchTerm","searchResultsVisible","window","console","warn","setSearchTerms","clearSearchButton","add","filterrenderpipe","pending","registerFormEvents","form","events","CustomEvents","activate","keyboardActivate","define","selectall","event","submitBtn","input","checkedCount","Array","from","querySelectorAll","disabled","submitter","action","getOrCreateInstance","toggle","elements","filter","collapse","colNodesToHide","itemIDNodesToHide","nodes","updateDisplay","countUpdate","filterableData","stringUserMap","fetchRequiredUserStrings","stringGradeMap","fetchRequiredGradeStrings","customFieldMap","fetchCustomFieldValues","stringMap","Map","searching","map","s","mapObj","undefined","key","string","itemname","category","getPreppedSearchTerm","toString","toLowerCase","includes","filterMatchDataset","setMatchedResults","getMatchedResults","column","name","displayName","element","content","sort","expandButton","rangeRowCell","avgRowCell","nodeSet","contains","location","rowCell","childNodes","node","textContent","getDatasetSize","parentElement","filterDataset","html","js","instance","updateNodes","focus","preventScroll","selectallEnable","getHTMLElements","searchDropdown","field","collapseName","userStrings","requiredStrings","stringArray","index","gradeStrings","Repository","gradeItems","result","id"],"mappings":"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"} \ No newline at end of file diff --git a/grade/report/grader/amd/build/stickycolspan.min.js b/grade/report/grader/amd/build/stickycolspan.min.js index 844801ab723a6..42a8dbebed4d1 100644 --- a/grade/report/grader/amd/build/stickycolspan.min.js +++ b/grade/report/grader/amd/build/stickycolspan.min.js @@ -1,10 +1,11 @@ -define("gradereport_grader/stickycolspan",["exports","jquery","core/sticky-footer"],(function(_exports,_jquery,_stickyFooter){var obj; +define("gradereport_grader/stickycolspan",["exports","core/sticky-footer"],(function(_exports,_stickyFooter){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0; /** * Javascript module for fixing the position of sticky headers with multiple colspans * * @module gradereport_grader/stickycolspan * @copyright 2022 Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj};const SELECTORS_GRADEPARENT=".gradeparent",SELECTORS_STUDENTHEADER="#studentheader",SELECTORS_TABLEHEADER="th.header",SELECTORS_BEHAT="body.behat-site",SELECTORS_USERDROPDOWN=".userrow th .dropdown",SELECTORS_LASTROW=".lastrow";_exports.init=()=>{if((0,_jquery.default)(SELECTORS_USERDROPDOWN).on("show.bs.dropdown hide.bs.dropdown",(e=>{e.target.closest(SELECTORS_TABLEHEADER).classList.toggle("actions-menu-active")})),defineLastRowIntersectionObserver(!0),document.addEventListener(_stickyFooter.eventTypes.stickyFooterStateChanged,(e=>{defineLastRowIntersectionObserver(e.detail.enabled)})),!document.querySelector(SELECTORS_BEHAT)){const grader=document.querySelector(SELECTORS_GRADEPARENT),tableHeaders=grader.querySelectorAll(SELECTORS_TABLEHEADER),studentHeader=grader.querySelector(SELECTORS_STUDENTHEADER),leftOffset=getComputedStyle(studentHeader).getPropertyValue("left"),rightOffset=getComputedStyle(studentHeader).getPropertyValue("right");tableHeaders.forEach((tableHeader=>{if(tableHeader.colSpan>1){const addOffset=tableHeader.offsetWidth-studentHeader.offsetWidth;window.right_to_left()?tableHeader.style.right="calc("+rightOffset+" - "+addOffset+"px )":tableHeader.style.left="calc("+leftOffset+" - "+addOffset+"px )"}}))}};const defineLastRowIntersectionObserver=stickyFooterEnabled=>{const lastRow=document.querySelector(SELECTORS_LASTROW);if(!lastRow.classList.contains("userrow")){const stickyFooterHeight=stickyFooterEnabled?document.querySelector(_stickyFooter.SELECTORS.STICKYFOOTER).offsetHeight:null;new IntersectionObserver((_ref=>{let[e]=_ref;return lastRow.classList.toggle("pinned",e.intersectionRatio<1)}),{rootMargin:stickyFooterHeight?"0px 0px -".concat(stickyFooterHeight,"px 0px"):"0px",threshold:[1]}).observe(lastRow.querySelector("th"))}}})); + */ +const SELECTORS_GRADEPARENT=".gradeparent",SELECTORS_STUDENTHEADER="#studentheader",SELECTORS_TABLEHEADER="th.header",SELECTORS_BEHAT="body.behat-site",SELECTORS_USERDROPDOWN=".userrow th .dropdown",SELECTORS_LASTROW=".lastrow";_exports.init=()=>{if(document.querySelectorAll(SELECTORS_USERDROPDOWN).forEach((dropdown=>{dropdown.addEventListener("show.bs.dropdown",(e=>{e.target.closest(SELECTORS_TABLEHEADER).classList.toggle("actions-menu-active")}))})),defineLastRowIntersectionObserver(!0),document.addEventListener(_stickyFooter.eventTypes.stickyFooterStateChanged,(e=>{defineLastRowIntersectionObserver(e.detail.enabled)})),!document.querySelector(SELECTORS_BEHAT)){const grader=document.querySelector(SELECTORS_GRADEPARENT),tableHeaders=grader.querySelectorAll(SELECTORS_TABLEHEADER),studentHeader=grader.querySelector(SELECTORS_STUDENTHEADER),leftOffset=getComputedStyle(studentHeader).getPropertyValue("left"),rightOffset=getComputedStyle(studentHeader).getPropertyValue("right");tableHeaders.forEach((tableHeader=>{if(tableHeader.colSpan>1){const addOffset=tableHeader.offsetWidth-studentHeader.offsetWidth;window.right_to_left()?tableHeader.style.right="calc("+rightOffset+" - "+addOffset+"px )":tableHeader.style.left="calc("+leftOffset+" - "+addOffset+"px )"}}))}};const defineLastRowIntersectionObserver=stickyFooterEnabled=>{const lastRow=document.querySelector(SELECTORS_LASTROW);if(!lastRow.classList.contains("userrow")){const stickyFooterHeight=stickyFooterEnabled?document.querySelector(_stickyFooter.SELECTORS.STICKYFOOTER).offsetHeight:null;new IntersectionObserver((_ref=>{let[e]=_ref;return lastRow.classList.toggle("pinned",e.intersectionRatio<1)}),{rootMargin:stickyFooterHeight?"0px 0px -".concat(stickyFooterHeight,"px 0px"):"0px",threshold:[1]}).observe(lastRow.querySelector("th"))}}})); //# sourceMappingURL=stickycolspan.min.js.map \ No newline at end of file diff --git a/grade/report/grader/amd/build/stickycolspan.min.js.map b/grade/report/grader/amd/build/stickycolspan.min.js.map index c5d7ea8de1e5d..c935564d850ee 100644 --- a/grade/report/grader/amd/build/stickycolspan.min.js.map +++ b/grade/report/grader/amd/build/stickycolspan.min.js.map @@ -1 +1 @@ -{"version":3,"file":"stickycolspan.min.js","sources":["../src/stickycolspan.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 module for fixing the position of sticky headers with multiple colspans\n *\n * @module gradereport_grader/stickycolspan\n * @copyright 2022 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport {SELECTORS as stickyFooterSelectors, eventTypes as stickyFooterEvents} from 'core/sticky-footer';\n\nconst SELECTORS = {\n GRADEPARENT: '.gradeparent',\n STUDENTHEADER: '#studentheader',\n TABLEHEADER: 'th.header',\n BEHAT: 'body.behat-site',\n USERDROPDOWN: '.userrow th .dropdown',\n LASTROW: '.lastrow',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n // The sticky positioning attributed to the user column cells affects the stacking context and makes the dropdowns\n // within these cells to be cut off. To solve this problem, whenever one of these action menus (dropdowns) is opened\n // we need to manually bump up the z-index value of the parent container element and revert once closed.\n $(SELECTORS.USERDROPDOWN).on('show.bs.dropdown hide.bs.dropdown', (e) => {\n // The closest heading element has sticky positioning which affects the stacking context in this case.\n e.target.closest(SELECTORS.TABLEHEADER).classList.toggle('actions-menu-active');\n });\n\n defineLastRowIntersectionObserver(true);\n // Add an event listener to the sticky footer toggled event to re-define the average row intersection observer\n // accordingly. This is needed as on narrow screens when scrolling vertically the sticky footer is enabled and\n // disabled dynamically.\n document.addEventListener(stickyFooterEvents.stickyFooterStateChanged, (e) => {\n defineLastRowIntersectionObserver(e.detail.enabled);\n });\n\n if (!document.querySelector(SELECTORS.BEHAT)) {\n const grader = document.querySelector(SELECTORS.GRADEPARENT);\n const tableHeaders = grader.querySelectorAll(SELECTORS.TABLEHEADER);\n const studentHeader = grader.querySelector(SELECTORS.STUDENTHEADER);\n const leftOffset = getComputedStyle(studentHeader).getPropertyValue('left');\n const rightOffset = getComputedStyle(studentHeader).getPropertyValue('right');\n\n tableHeaders.forEach((tableHeader) => {\n if (tableHeader.colSpan > 1) {\n const addOffset = (tableHeader.offsetWidth - studentHeader.offsetWidth);\n if (window.right_to_left()) {\n tableHeader.style.right = 'calc(' + rightOffset + ' - ' + addOffset + 'px )';\n } else {\n tableHeader.style.left = 'calc(' + leftOffset + ' - ' + addOffset + 'px )';\n }\n }\n });\n }\n};\n\n/**\n * Define the intersection observer that will make sure that the last row is properly pinned.\n *\n * In certain scenarios, such as when both 'Overall average' and 'Range' are set not to be shown in the Grader report,\n * a user row will end up being the last row in the Grader report table. In this particular case, we want to avoid\n * pinning the last row.\n *\n * @param {boolean} stickyFooterEnabled Whether the page shows a sticky footer or not.\n */\nconst defineLastRowIntersectionObserver = (stickyFooterEnabled) => {\n const lastRow = document.querySelector(SELECTORS.LASTROW);\n // Ensure that the last row is not a user row before defining the intersection observer.\n if (!lastRow.classList.contains('userrow')) {\n const stickyFooterHeight = stickyFooterEnabled ?\n document.querySelector(stickyFooterSelectors.STICKYFOOTER).offsetHeight : null;\n // Register an observer that will bump up the z-index value of the last row when it's pinned to prevent the row\n // being cut-off by the user column cells or other components within the report table that have higher z-index\n // values. If the page has a sticky footer, we need to make sure that the bottom root margin of the observer\n // subtracts the height of the sticky footer to prevent the row being cut-off by the footer.\n const intersectionObserver = new IntersectionObserver(\n ([e]) => lastRow.classList.toggle('pinned', e.intersectionRatio < 1),\n {\n rootMargin: stickyFooterHeight ? `0px 0px -${stickyFooterHeight}px 0px` : \"0px\",\n threshold: [1]\n }\n );\n intersectionObserver.observe(lastRow.querySelector('th'));\n }\n};\n"],"names":["SELECTORS","on","e","target","closest","classList","toggle","defineLastRowIntersectionObserver","document","addEventListener","stickyFooterEvents","stickyFooterStateChanged","detail","enabled","querySelector","grader","tableHeaders","querySelectorAll","studentHeader","leftOffset","getComputedStyle","getPropertyValue","rightOffset","forEach","tableHeader","colSpan","addOffset","offsetWidth","window","right_to_left","style","right","left","stickyFooterEnabled","lastRow","contains","stickyFooterHeight","stickyFooterSelectors","STICKYFOOTER","offsetHeight","IntersectionObserver","_ref","intersectionRatio","rootMargin","threshold","observe"],"mappings":";;;;;;;gJA0BMA,sBACW,eADXA,wBAEa,iBAFbA,sBAGW,YAHXA,gBAIK,kBAJLA,uBAKY,wBALZA,kBAMO,yBAMO,4BAIdA,wBAAwBC,GAAG,qCAAsCC,IAE/DA,EAAEC,OAAOC,QAAQJ,uBAAuBK,UAAUC,OAAO,0BAG7DC,mCAAkC,GAIlCC,SAASC,iBAAiBC,yBAAmBC,0BAA2BT,IACpEK,kCAAkCL,EAAEU,OAAOC,aAG1CL,SAASM,cAAcd,iBAAkB,OACpCe,OAASP,SAASM,cAAcd,uBAChCgB,aAAeD,OAAOE,iBAAiBjB,uBACvCkB,cAAgBH,OAAOD,cAAcd,yBACrCmB,WAAaC,iBAAiBF,eAAeG,iBAAiB,QAC9DC,YAAcF,iBAAiBF,eAAeG,iBAAiB,SAErEL,aAAaO,SAASC,iBACdA,YAAYC,QAAU,EAAG,OACnBC,UAAaF,YAAYG,YAAcT,cAAcS,YACvDC,OAAOC,gBACPL,YAAYM,MAAMC,MAAQ,QAAUT,YAAc,MAAQI,UAAY,OAEtEF,YAAYM,MAAME,KAAO,QAAUb,WAAa,MAAQO,UAAY,mBAgBlFnB,kCAAqC0B,4BACjCC,QAAU1B,SAASM,cAAcd,uBAElCkC,QAAQ7B,UAAU8B,SAAS,WAAY,OAClCC,mBAAqBH,oBACvBzB,SAASM,cAAcuB,wBAAsBC,cAAcC,aAAe,KAKjD,IAAIC,sBAC7BC,WAAEvC,eAAOgC,QAAQ7B,UAAUC,OAAO,SAAUJ,EAAEwC,kBAAoB,KAClE,CACIC,WAAYP,sCAAiCA,6BAA6B,MAC1EQ,UAAW,CAAC,KAGCC,QAAQX,QAAQpB,cAAc"} \ No newline at end of file +{"version":3,"file":"stickycolspan.min.js","sources":["../src/stickycolspan.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 module for fixing the position of sticky headers with multiple colspans\n *\n * @module gradereport_grader/stickycolspan\n * @copyright 2022 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {SELECTORS as stickyFooterSelectors, eventTypes as stickyFooterEvents} from 'core/sticky-footer';\n\nconst SELECTORS = {\n GRADEPARENT: '.gradeparent',\n STUDENTHEADER: '#studentheader',\n TABLEHEADER: 'th.header',\n BEHAT: 'body.behat-site',\n USERDROPDOWN: '.userrow th .dropdown',\n LASTROW: '.lastrow',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n // The sticky positioning attributed to the user column cells affects the stacking context and makes the dropdowns\n // within these cells to be cut off. To solve this problem, whenever one of these action menus (dropdowns) is opened\n // we need to manually bump up the z-index value of the parent container element and revert once closed.\n document.querySelectorAll(SELECTORS.USERDROPDOWN).forEach((dropdown) => {\n dropdown.addEventListener('show.bs.dropdown', (e) => {\n // The closest heading element has sticky positioning which affects the stacking context in this case.\n e.target.closest(SELECTORS.TABLEHEADER).classList.toggle('actions-menu-active');\n });\n });\n\n defineLastRowIntersectionObserver(true);\n // Add an event listener to the sticky footer toggled event to re-define the average row intersection observer\n // accordingly. This is needed as on narrow screens when scrolling vertically the sticky footer is enabled and\n // disabled dynamically.\n document.addEventListener(stickyFooterEvents.stickyFooterStateChanged, (e) => {\n defineLastRowIntersectionObserver(e.detail.enabled);\n });\n\n if (!document.querySelector(SELECTORS.BEHAT)) {\n const grader = document.querySelector(SELECTORS.GRADEPARENT);\n const tableHeaders = grader.querySelectorAll(SELECTORS.TABLEHEADER);\n const studentHeader = grader.querySelector(SELECTORS.STUDENTHEADER);\n const leftOffset = getComputedStyle(studentHeader).getPropertyValue('left');\n const rightOffset = getComputedStyle(studentHeader).getPropertyValue('right');\n\n tableHeaders.forEach((tableHeader) => {\n if (tableHeader.colSpan > 1) {\n const addOffset = (tableHeader.offsetWidth - studentHeader.offsetWidth);\n if (window.right_to_left()) {\n tableHeader.style.right = 'calc(' + rightOffset + ' - ' + addOffset + 'px )';\n } else {\n tableHeader.style.left = 'calc(' + leftOffset + ' - ' + addOffset + 'px )';\n }\n }\n });\n }\n};\n\n/**\n * Define the intersection observer that will make sure that the last row is properly pinned.\n *\n * In certain scenarios, such as when both 'Overall average' and 'Range' are set not to be shown in the Grader report,\n * a user row will end up being the last row in the Grader report table. In this particular case, we want to avoid\n * pinning the last row.\n *\n * @param {boolean} stickyFooterEnabled Whether the page shows a sticky footer or not.\n */\nconst defineLastRowIntersectionObserver = (stickyFooterEnabled) => {\n const lastRow = document.querySelector(SELECTORS.LASTROW);\n // Ensure that the last row is not a user row before defining the intersection observer.\n if (!lastRow.classList.contains('userrow')) {\n const stickyFooterHeight = stickyFooterEnabled ?\n document.querySelector(stickyFooterSelectors.STICKYFOOTER).offsetHeight : null;\n // Register an observer that will bump up the z-index value of the last row when it's pinned to prevent the row\n // being cut-off by the user column cells or other components within the report table that have higher z-index\n // values. If the page has a sticky footer, we need to make sure that the bottom root margin of the observer\n // subtracts the height of the sticky footer to prevent the row being cut-off by the footer.\n const intersectionObserver = new IntersectionObserver(\n ([e]) => lastRow.classList.toggle('pinned', e.intersectionRatio < 1),\n {\n rootMargin: stickyFooterHeight ? `0px 0px -${stickyFooterHeight}px 0px` : \"0px\",\n threshold: [1]\n }\n );\n intersectionObserver.observe(lastRow.querySelector('th'));\n }\n};\n"],"names":["SELECTORS","document","querySelectorAll","forEach","dropdown","addEventListener","e","target","closest","classList","toggle","defineLastRowIntersectionObserver","stickyFooterEvents","stickyFooterStateChanged","detail","enabled","querySelector","grader","tableHeaders","studentHeader","leftOffset","getComputedStyle","getPropertyValue","rightOffset","tableHeader","colSpan","addOffset","offsetWidth","window","right_to_left","style","right","left","stickyFooterEnabled","lastRow","contains","stickyFooterHeight","stickyFooterSelectors","STICKYFOOTER","offsetHeight","IntersectionObserver","_ref","intersectionRatio","rootMargin","threshold","observe"],"mappings":";;;;;;;;MAyBMA,sBACW,eADXA,wBAEa,iBAFbA,sBAGW,YAHXA,gBAIK,kBAJLA,uBAKY,wBALZA,kBAMO,yBAMO,QAIhBC,SAASC,iBAAiBF,wBAAwBG,SAASC,WACvDA,SAASC,iBAAiB,oBAAqBC,IAE3CA,EAAEC,OAAOC,QAAQR,uBAAuBS,UAAUC,OAAO,6BAIjEC,mCAAkC,GAIlCV,SAASI,iBAAiBO,yBAAmBC,0BAA2BP,IACpEK,kCAAkCL,EAAEQ,OAAOC,aAG1Cd,SAASe,cAAchB,iBAAkB,OACpCiB,OAAShB,SAASe,cAAchB,uBAChCkB,aAAeD,OAAOf,iBAAiBF,uBACvCmB,cAAgBF,OAAOD,cAAchB,yBACrCoB,WAAaC,iBAAiBF,eAAeG,iBAAiB,QAC9DC,YAAcF,iBAAiBF,eAAeG,iBAAiB,SAErEJ,aAAaf,SAASqB,iBACdA,YAAYC,QAAU,EAAG,OACnBC,UAAaF,YAAYG,YAAcR,cAAcQ,YACvDC,OAAOC,gBACPL,YAAYM,MAAMC,MAAQ,QAAUR,YAAc,MAAQG,UAAY,OAEtEF,YAAYM,MAAME,KAAO,QAAUZ,WAAa,MAAQM,UAAY,mBAgBlFf,kCAAqCsB,4BACjCC,QAAUjC,SAASe,cAAchB,uBAElCkC,QAAQzB,UAAU0B,SAAS,WAAY,OAClCC,mBAAqBH,oBACvBhC,SAASe,cAAcqB,wBAAsBC,cAAcC,aAAe,KAKjD,IAAIC,sBAC7BC,WAAEnC,eAAO4B,QAAQzB,UAAUC,OAAO,SAAUJ,EAAEoC,kBAAoB,KAClE,CACIC,WAAYP,sCAAiCA,6BAA6B,MAC1EQ,UAAW,CAAC,KAGCC,QAAQX,QAAQlB,cAAc"} \ No newline at end of file diff --git a/grade/report/grader/amd/src/collapse.js b/grade/report/grader/amd/src/collapse.js index 2435fb666171b..ae132525186b7 100644 --- a/grade/report/grader/amd/src/collapse.js +++ b/grade/report/grader/amd/src/collapse.js @@ -24,7 +24,7 @@ import * as Repository from 'gradereport_grader/collapse/repository'; import search_combobox from 'core/comboboxsearch/search_combobox'; import {renderForPromise, replaceNodeContents, replaceNode} from 'core/templates'; import {debounce} from 'core/utils'; -import $ from 'jquery'; +import Dropdown from 'theme_boost/bootstrap/dropdown'; import {getStrings} from 'core/str'; import CustomEvents from "core/custom_interaction_events"; import storage from 'core/localstorage'; @@ -100,7 +100,7 @@ export default class ColumnSearch extends search_combobox { }, 10); }).then(() => pendingPromise.resolve()).catch(Notification.exception); - this.$component.on('hide.bs.dropdown', () => { + this.component.addEventListener('hide.bs.dropdown', () => { const searchResultContainer = this.component.querySelector(selectors.searchResultContainer); searchResultContainer.scrollTop = 0; @@ -314,7 +314,7 @@ export default class ColumnSearch extends search_combobox { form.addEventListener('submit', async(e) => { e.preventDefault(); if (e.submitter.dataset.action === selectors.formItems.cancel) { - $(this.component).dropdown('toggle'); + Dropdown.getOrCreateInstance(this.component).toggle(); return; } // Get the users' checked columns to change. @@ -484,7 +484,7 @@ export default class ColumnSearch extends search_combobox { this.registerInputEvents(); // Add a small BS listener so that we can set the focus correctly on open. - this.$component.on('shown.bs.dropdown', () => { + this.component.addEventListener('shown.bs.dropdown', () => { this.searchInput.focus({preventScroll: true}); this.selectallEnable(); }); diff --git a/grade/report/grader/amd/src/stickycolspan.js b/grade/report/grader/amd/src/stickycolspan.js index ebeac852da009..e6f8df27f3bf5 100644 --- a/grade/report/grader/amd/src/stickycolspan.js +++ b/grade/report/grader/amd/src/stickycolspan.js @@ -21,7 +21,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import $ from 'jquery'; import {SELECTORS as stickyFooterSelectors, eventTypes as stickyFooterEvents} from 'core/sticky-footer'; const SELECTORS = { @@ -40,9 +39,11 @@ export const init = () => { // The sticky positioning attributed to the user column cells affects the stacking context and makes the dropdowns // within these cells to be cut off. To solve this problem, whenever one of these action menus (dropdowns) is opened // we need to manually bump up the z-index value of the parent container element and revert once closed. - $(SELECTORS.USERDROPDOWN).on('show.bs.dropdown hide.bs.dropdown', (e) => { - // The closest heading element has sticky positioning which affects the stacking context in this case. - e.target.closest(SELECTORS.TABLEHEADER).classList.toggle('actions-menu-active'); + document.querySelectorAll(SELECTORS.USERDROPDOWN).forEach((dropdown) => { + dropdown.addEventListener('show.bs.dropdown', (e) => { + // The closest heading element has sticky positioning which affects the stacking context in this case. + e.target.closest(SELECTORS.TABLEHEADER).classList.toggle('actions-menu-active'); + }); }); defineLastRowIntersectionObserver(true); diff --git a/group/amd/build/comboboxsearch/group.min.js b/group/amd/build/comboboxsearch/group.min.js index ee1a6951c64ba..522cab10779aa 100644 --- a/group/amd/build/comboboxsearch/group.min.js +++ b/group/amd/build/comboboxsearch/group.min.js @@ -1,3 +1,3 @@ -define("core_group/comboboxsearch/group",["exports","core/comboboxsearch/search_combobox","core_group/comboboxsearch/repository","core/templates","core/utils","core/notification"],(function(_exports,_search_combobox,_repository,_templates,_utils,_notification){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,_search_combobox=_interopRequireDefault(_search_combobox),_notification=_interopRequireDefault(_notification);class GroupSearch extends _search_combobox.default{constructor(){let cmid=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;super(),_defineProperty(this,"courseID",void 0),_defineProperty(this,"cmID",void 0),_defineProperty(this,"bannedFilterFields",["id","link","groupimageurl"]),this.selectors={...this.selectors,courseid:'[data-region="courseid"]',placeholder:'.groupsearchdropdown [data-region="searchplaceholder"]'};const component=document.querySelector(this.componentSelector());this.courseID=component.querySelector(this.selectors.courseid).dataset.courseid,this.instance=component.querySelector(this.selectors.instance).dataset.instance,this.cmID=cmid;const searchValueElement=this.component.querySelector("#".concat(this.searchInput.dataset.inputElement));searchValueElement.addEventListener("change",(()=>{this.toggleDropdown();const valueElement=this.component.querySelector("#".concat(this.combobox.dataset.inputElement));valueElement.value!==searchValueElement.value&&(valueElement.value=searchValueElement.value,valueElement.dispatchEvent(new Event("change",{bubbles:!0}))),searchValueElement.value=""})),this.$component.on("hide.bs.dropdown",(()=>{this.searchInput.removeAttribute("aria-activedescendant");const listbox=document.querySelector("#".concat(this.searchInput.getAttribute("aria-controls"),'[role="listbox"]'));listbox.querySelectorAll('.active[role="option"]').forEach((option=>{option.classList.remove("active")})),listbox.scrollTop=0,setTimeout((()=>{""!==this.searchInput.value&&(this.searchInput.value="",this.searchInput.dispatchEvent(new Event("input",{bubbles:!0})))}))})),this.renderDefault().catch(_notification.default.exception)}static init(){return new GroupSearch(arguments.length>0&&void 0!==arguments[0]?arguments[0]:null)}componentSelector(){return".group-search"}dropdownSelector(){return".groupsearchdropdown"}async renderDropdown(){const{html:html,js:js}=await(0,_templates.renderForPromise)("core_group/comboboxsearch/resultset",{groups:this.getMatchedResults(),hasresults:this.getMatchedResults().length>0,instance:this.instance,searchterm:this.getSearchTerm()});(0,_templates.replaceNodeContents)(this.selectors.placeholder,html,js),this.searchInput.removeAttribute("aria-activedescendant")}async renderDefault(){this.setMatchedResults(await this.filterDataset(await this.getDataset())),this.filterMatchDataset(),await this.renderDropdown(),this.updateNodes()}async fetchDataset(){return await(0,_repository.groupFetch)(this.courseID,this.cmID).then((r=>r.groups))}async filterDataset(filterableData){return""===this.getPreppedSearchTerm()?filterableData:filterableData.filter((group=>Object.keys(group).some((key=>""!==group[key]&&!this.bannedFilterFields.includes(key)&&group[key].toString().toLowerCase().includes(this.getPreppedSearchTerm())))))}filterMatchDataset(){this.setMatchedResults(this.getMatchedResults().map((group=>({id:group.id,name:group.name,groupimageurl:group.groupimageurl}))))}async clickHandler(e){e.target.closest(this.selectors.clearSearch)&&(e.stopPropagation(),this.searchInput.value="",this.setSearchTerms(this.searchInput.value),this.searchInput.focus(),this.clearSearchButton.classList.add("d-none"),await this.filterrenderpipe())}changeHandler(e){window.location=this.selectOneLink(e.target.value)}registerInputHandlers(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{this.setSearchTerms(this.searchInput.value),""===this.getSearchTerm()?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none"),await this.filterrenderpipe()}),300))}selectOneLink(groupID){throw new Error("selectOneLink(".concat(groupID,") must be implemented in ").concat(this.constructor.name))}}return _exports.default=GroupSearch,_exports.default})); +define("core_group/comboboxsearch/group",["exports","core/comboboxsearch/search_combobox","core_group/comboboxsearch/repository","core/templates","core/utils","core/notification"],(function(_exports,_search_combobox,_repository,_templates,_utils,_notification){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,_search_combobox=_interopRequireDefault(_search_combobox),_notification=_interopRequireDefault(_notification);class GroupSearch extends _search_combobox.default{constructor(){let cmid=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;super(),_defineProperty(this,"courseID",void 0),_defineProperty(this,"cmID",void 0),_defineProperty(this,"bannedFilterFields",["id","link","groupimageurl"]),this.selectors={...this.selectors,courseid:'[data-region="courseid"]',placeholder:'.groupsearchdropdown [data-region="searchplaceholder"]'};const component=document.querySelector(this.componentSelector());this.courseID=component.querySelector(this.selectors.courseid).dataset.courseid,this.instance=component.querySelector(this.selectors.instance).dataset.instance,this.cmID=cmid;const searchValueElement=this.component.querySelector("#".concat(this.searchInput.dataset.inputElement));searchValueElement.addEventListener("change",(()=>{this.toggleDropdown();const valueElement=this.component.querySelector("#".concat(this.combobox.dataset.inputElement));valueElement.value!==searchValueElement.value&&(valueElement.value=searchValueElement.value,valueElement.dispatchEvent(new Event("change",{bubbles:!0}))),searchValueElement.value=""})),this.component.addEventListener("hide.bs.dropdown",(()=>{this.searchInput.removeAttribute("aria-activedescendant");const listbox=document.querySelector("#".concat(this.searchInput.getAttribute("aria-controls"),'[role="listbox"]'));listbox.querySelectorAll('.active[role="option"]').forEach((option=>{option.classList.remove("active")})),listbox.scrollTop=0,setTimeout((()=>{""!==this.searchInput.value&&(this.searchInput.value="",this.searchInput.dispatchEvent(new Event("input",{bubbles:!0})))}))})),this.renderDefault().catch(_notification.default.exception)}static init(){return new GroupSearch(arguments.length>0&&void 0!==arguments[0]?arguments[0]:null)}componentSelector(){return".group-search"}dropdownSelector(){return".groupsearchdropdown"}async renderDropdown(){const{html:html,js:js}=await(0,_templates.renderForPromise)("core_group/comboboxsearch/resultset",{groups:this.getMatchedResults(),hasresults:this.getMatchedResults().length>0,instance:this.instance,searchterm:this.getSearchTerm()});(0,_templates.replaceNodeContents)(this.selectors.placeholder,html,js),this.searchInput.removeAttribute("aria-activedescendant")}async renderDefault(){this.setMatchedResults(await this.filterDataset(await this.getDataset())),this.filterMatchDataset(),await this.renderDropdown(),this.updateNodes()}async fetchDataset(){return await(0,_repository.groupFetch)(this.courseID,this.cmID).then((r=>r.groups))}async filterDataset(filterableData){return""===this.getPreppedSearchTerm()?filterableData:filterableData.filter((group=>Object.keys(group).some((key=>""!==group[key]&&!this.bannedFilterFields.includes(key)&&group[key].toString().toLowerCase().includes(this.getPreppedSearchTerm())))))}filterMatchDataset(){this.setMatchedResults(this.getMatchedResults().map((group=>({id:group.id,name:group.name,groupimageurl:group.groupimageurl}))))}async clickHandler(e){e.target.closest(this.selectors.clearSearch)&&(e.stopPropagation(),this.searchInput.value="",this.setSearchTerms(this.searchInput.value),this.searchInput.focus(),this.clearSearchButton.classList.add("d-none"),await this.filterrenderpipe())}changeHandler(e){window.location=this.selectOneLink(e.target.value)}registerInputHandlers(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{this.setSearchTerms(this.searchInput.value),""===this.getSearchTerm()?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none"),await this.filterrenderpipe()}),300))}selectOneLink(groupID){throw new Error("selectOneLink(".concat(groupID,") must be implemented in ").concat(this.constructor.name))}}return _exports.default=GroupSearch,_exports.default})); //# sourceMappingURL=group.min.js.map \ No newline at end of file diff --git a/group/amd/build/comboboxsearch/group.min.js.map b/group/amd/build/comboboxsearch/group.min.js.map index 5a5c17602e515..6d43dfa720a2c 100644 --- a/group/amd/build/comboboxsearch/group.min.js.map +++ b/group/amd/build/comboboxsearch/group.min.js.map @@ -1 +1 @@ -{"version":3,"file":"group.min.js","sources":["../../src/comboboxsearch/group.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 * Allow the user to search for groups.\n *\n * @module core_group/comboboxsearch/group\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport search_combobox from 'core/comboboxsearch/search_combobox';\nimport {groupFetch} from 'core_group/comboboxsearch/repository';\nimport {renderForPromise, replaceNodeContents} from 'core/templates';\nimport {debounce} from 'core/utils';\nimport Notification from 'core/notification';\n\nexport default class GroupSearch extends search_combobox {\n\n courseID;\n cmID;\n bannedFilterFields = ['id', 'link', 'groupimageurl'];\n\n /**\n * Construct the class.\n *\n * @param {int|null} cmid ID of the course module initiating the group search (optional).\n */\n constructor(cmid = null) {\n super();\n this.selectors = {...this.selectors,\n courseid: '[data-region=\"courseid\"]',\n placeholder: '.groupsearchdropdown [data-region=\"searchplaceholder\"]',\n };\n const component = document.querySelector(this.componentSelector());\n this.courseID = component.querySelector(this.selectors.courseid).dataset.courseid;\n // Override the instance since the body is built outside the constructor for the combobox.\n this.instance = component.querySelector(this.selectors.instance).dataset.instance;\n this.cmID = cmid;\n\n const searchValueElement = this.component.querySelector(`#${this.searchInput.dataset.inputElement}`);\n searchValueElement.addEventListener('change', () => {\n this.toggleDropdown(); // Otherwise the dropdown stays open when user choose an option using keyboard.\n\n const valueElement = this.component.querySelector(`#${this.combobox.dataset.inputElement}`);\n if (valueElement.value !== searchValueElement.value) {\n valueElement.value = searchValueElement.value;\n valueElement.dispatchEvent(new Event('change', {bubbles: true}));\n }\n\n searchValueElement.value = '';\n });\n\n this.$component.on('hide.bs.dropdown', () => {\n this.searchInput.removeAttribute('aria-activedescendant');\n\n const listbox = document.querySelector(`#${this.searchInput.getAttribute('aria-controls')}[role=\"listbox\"]`);\n listbox.querySelectorAll('.active[role=\"option\"]').forEach(option => {\n option.classList.remove('active');\n });\n listbox.scrollTop = 0;\n\n // Use setTimeout to make sure the following code is executed after the click event is handled.\n setTimeout(() => {\n if (this.searchInput.value !== '') {\n this.searchInput.value = '';\n this.searchInput.dispatchEvent(new Event('input', {bubbles: true}));\n }\n });\n });\n\n this.renderDefault().catch(Notification.exception);\n }\n\n /**\n * Initialise an instance of the class.\n *\n * @param {int|null} cmid ID of the course module initiating the group search (optional).\n */\n static init(cmid = null) {\n return new GroupSearch(cmid);\n }\n\n /**\n * The overall div that contains the searching widget.\n *\n * @returns {string}\n */\n componentSelector() {\n return '.group-search';\n }\n\n /**\n * The dropdown div that contains the searching widget result space.\n *\n * @returns {string}\n */\n dropdownSelector() {\n return '.groupsearchdropdown';\n }\n\n /**\n * Build the content then replace the node.\n */\n async renderDropdown() {\n const {html, js} = await renderForPromise('core_group/comboboxsearch/resultset', {\n groups: this.getMatchedResults(),\n hasresults: this.getMatchedResults().length > 0,\n instance: this.instance,\n searchterm: this.getSearchTerm(),\n });\n replaceNodeContents(this.selectors.placeholder, html, js);\n // Remove aria-activedescendant when the available options change.\n this.searchInput.removeAttribute('aria-activedescendant');\n }\n\n /**\n * Build the content then replace the node by default we want our form to exist.\n */\n async renderDefault() {\n this.setMatchedResults(await this.filterDataset(await this.getDataset()));\n this.filterMatchDataset();\n\n await this.renderDropdown();\n\n this.updateNodes();\n }\n\n /**\n * Get the data we will be searching against in this component.\n *\n * @returns {Promise<*>}\n */\n async fetchDataset() {\n return await groupFetch(this.courseID, this.cmID).then((r) => r.groups);\n }\n\n /**\n * Dictate to the search component how and what we want to match upon.\n *\n * @param {Array} filterableData\n * @returns {Array} The users that match the given criteria.\n */\n async filterDataset(filterableData) {\n // Sometimes we just want to show everything.\n if (this.getPreppedSearchTerm() === '') {\n return filterableData;\n }\n return filterableData.filter((group) => Object.keys(group).some((key) => {\n if (group[key] === \"\" || this.bannedFilterFields.includes(key)) {\n return false;\n }\n return group[key].toString().toLowerCase().includes(this.getPreppedSearchTerm());\n }));\n }\n\n /**\n * Given we have a subset of the dataset, set the field that we matched upon to inform the end user.\n */\n filterMatchDataset() {\n this.setMatchedResults(\n this.getMatchedResults().map((group) => {\n return {\n id: group.id,\n name: group.name,\n groupimageurl: group.groupimageurl,\n };\n })\n );\n }\n\n /**\n * The handler for when a user interacts with the component.\n *\n * @param {MouseEvent} e The triggering event that we are working with.\n */\n async clickHandler(e) {\n if (e.target.closest(this.selectors.clearSearch)) {\n e.stopPropagation();\n // Clear the entered search query in the search bar.\n this.searchInput.value = '';\n this.setSearchTerms(this.searchInput.value);\n this.searchInput.focus();\n this.clearSearchButton.classList.add('d-none');\n // Display results.\n await this.filterrenderpipe();\n }\n }\n\n /**\n * The handler for when a user changes the value of the component (selects an option from the dropdown).\n *\n * @param {Event} e The change event.\n */\n changeHandler(e) {\n window.location = this.selectOneLink(e.target.value);\n }\n\n /**\n * Override the input event listener for the text input area.\n */\n registerInputHandlers() {\n // Register & handle the text input.\n this.searchInput.addEventListener('input', debounce(async() => {\n this.setSearchTerms(this.searchInput.value);\n // We can also require a set amount of input before search.\n if (this.getSearchTerm() === '') {\n // Hide the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.add('d-none');\n } else {\n // Display the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.remove('d-none');\n }\n // User has given something for us to filter against.\n await this.filterrenderpipe();\n }, 300));\n }\n\n /**\n * Build up the view all link that is dedicated to a particular result.\n * We will call this function when a user interacts with the combobox to redirect them to show their results in the page.\n *\n * @param {Number} groupID The ID of the group selected.\n */\n selectOneLink(groupID) {\n throw new Error(`selectOneLink(${groupID}) must be implemented in ${this.constructor.name}`);\n }\n}\n"],"names":["GroupSearch","search_combobox","constructor","cmid","selectors","this","courseid","placeholder","component","document","querySelector","componentSelector","courseID","dataset","instance","cmID","searchValueElement","searchInput","inputElement","addEventListener","toggleDropdown","valueElement","combobox","value","dispatchEvent","Event","bubbles","$component","on","removeAttribute","listbox","getAttribute","querySelectorAll","forEach","option","classList","remove","scrollTop","setTimeout","renderDefault","catch","Notification","exception","dropdownSelector","html","js","groups","getMatchedResults","hasresults","length","searchterm","getSearchTerm","setMatchedResults","filterDataset","getDataset","filterMatchDataset","renderDropdown","updateNodes","then","r","filterableData","getPreppedSearchTerm","filter","group","Object","keys","some","key","bannedFilterFields","includes","toString","toLowerCase","map","id","name","groupimageurl","e","target","closest","clearSearch","stopPropagation","setSearchTerms","focus","clearSearchButton","add","filterrenderpipe","changeHandler","window","location","selectOneLink","registerInputHandlers","async","groupID","Error"],"mappings":"+rBA4BqBA,oBAAoBC,yBAWrCC,kBAAYC,4DAAO,mIAPE,CAAC,KAAM,OAAQ,uBAS3BC,UAAY,IAAIC,KAAKD,UACtBE,SAAU,2BACVC,YAAa,gEAEXC,UAAYC,SAASC,cAAcL,KAAKM,0BACzCC,SAAWJ,UAAUE,cAAcL,KAAKD,UAAUE,UAAUO,QAAQP,cAEpEQ,SAAWN,UAAUE,cAAcL,KAAKD,UAAUU,UAAUD,QAAQC,cACpEC,KAAOZ,WAENa,mBAAqBX,KAAKG,UAAUE,yBAAkBL,KAAKY,YAAYJ,QAAQK,eACrFF,mBAAmBG,iBAAiB,UAAU,UACrCC,uBAECC,aAAehB,KAAKG,UAAUE,yBAAkBL,KAAKiB,SAAST,QAAQK,eACxEG,aAAaE,QAAUP,mBAAmBO,QAC1CF,aAAaE,MAAQP,mBAAmBO,MACxCF,aAAaG,cAAc,IAAIC,MAAM,SAAU,CAACC,SAAS,MAG7DV,mBAAmBO,MAAQ,WAG1BI,WAAWC,GAAG,oBAAoB,UAC9BX,YAAYY,gBAAgB,+BAE3BC,QAAUrB,SAASC,yBAAkBL,KAAKY,YAAYc,aAAa,sCACzED,QAAQE,iBAAiB,0BAA0BC,SAAQC,SACvDA,OAAOC,UAAUC,OAAO,aAE5BN,QAAQO,UAAY,EAGpBC,YAAW,KACwB,KAA3BjC,KAAKY,YAAYM,aACZN,YAAYM,MAAQ,QACpBN,YAAYO,cAAc,IAAIC,MAAM,QAAS,CAACC,SAAS,iBAKnEa,gBAAgBC,MAAMC,sBAAaC,gCASjC,IAAI1C,mEADI,MASnBW,0BACW,gBAQXgC,yBACW,oDAODC,KAACA,KAADC,GAAOA,UAAY,+BAAiB,sCAAuC,CAC7EC,OAAQzC,KAAK0C,oBACbC,WAAY3C,KAAK0C,oBAAoBE,OAAS,EAC9CnC,SAAUT,KAAKS,SACfoC,WAAY7C,KAAK8C,qDAED9C,KAAKD,UAAUG,YAAaqC,KAAMC,SAEjD5B,YAAYY,gBAAgB,oDAO5BuB,wBAAwB/C,KAAKgD,oBAAoBhD,KAAKiD,oBACtDC,2BAEClD,KAAKmD,sBAENC,gDASQ,0BAAWpD,KAAKO,SAAUP,KAAKU,MAAM2C,MAAMC,GAAMA,EAAEb,6BAShDc,sBAEoB,KAAhCvD,KAAKwD,uBACED,eAEJA,eAAeE,QAAQC,OAAUC,OAAOC,KAAKF,OAAOG,MAAMC,KAC1C,KAAfJ,MAAMI,OAAe9D,KAAK+D,mBAAmBC,SAASF,MAGnDJ,MAAMI,KAAKG,WAAWC,cAAcF,SAAShE,KAAKwD,4BAOjEN,0BACSH,kBACD/C,KAAK0C,oBAAoByB,KAAKT,QACnB,CACHU,GAAIV,MAAMU,GACVC,KAAMX,MAAMW,KACZC,cAAeZ,MAAMY,sCAWlBC,GACXA,EAAEC,OAAOC,QAAQzE,KAAKD,UAAU2E,eAChCH,EAAEI,uBAEG/D,YAAYM,MAAQ,QACpB0D,eAAe5E,KAAKY,YAAYM,YAChCN,YAAYiE,aACZC,kBAAkBhD,UAAUiD,IAAI,gBAE/B/E,KAAKgF,oBASnBC,cAAcV,GACVW,OAAOC,SAAWnF,KAAKoF,cAAcb,EAAEC,OAAOtD,OAMlDmE,6BAESzE,YAAYE,iBAAiB,SAAS,oBAASwE,eAC3CV,eAAe5E,KAAKY,YAAYM,OAER,KAAzBlB,KAAK8C,qBAEAgC,kBAAkBhD,UAAUiD,IAAI,eAGhCD,kBAAkBhD,UAAUC,OAAO,gBAGtC/B,KAAKgF,qBACZ,MASPI,cAAcG,eACJ,IAAIC,8BAAuBD,4CAAmCvF,KAAKH,YAAYwE"} \ No newline at end of file +{"version":3,"file":"group.min.js","sources":["../../src/comboboxsearch/group.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 * Allow the user to search for groups.\n *\n * @module core_group/comboboxsearch/group\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport search_combobox from 'core/comboboxsearch/search_combobox';\nimport {groupFetch} from 'core_group/comboboxsearch/repository';\nimport {renderForPromise, replaceNodeContents} from 'core/templates';\nimport {debounce} from 'core/utils';\nimport Notification from 'core/notification';\n\nexport default class GroupSearch extends search_combobox {\n\n courseID;\n cmID;\n bannedFilterFields = ['id', 'link', 'groupimageurl'];\n\n /**\n * Construct the class.\n *\n * @param {int|null} cmid ID of the course module initiating the group search (optional).\n */\n constructor(cmid = null) {\n super();\n this.selectors = {...this.selectors,\n courseid: '[data-region=\"courseid\"]',\n placeholder: '.groupsearchdropdown [data-region=\"searchplaceholder\"]',\n };\n const component = document.querySelector(this.componentSelector());\n this.courseID = component.querySelector(this.selectors.courseid).dataset.courseid;\n // Override the instance since the body is built outside the constructor for the combobox.\n this.instance = component.querySelector(this.selectors.instance).dataset.instance;\n this.cmID = cmid;\n\n const searchValueElement = this.component.querySelector(`#${this.searchInput.dataset.inputElement}`);\n searchValueElement.addEventListener('change', () => {\n this.toggleDropdown(); // Otherwise the dropdown stays open when user choose an option using keyboard.\n\n const valueElement = this.component.querySelector(`#${this.combobox.dataset.inputElement}`);\n if (valueElement.value !== searchValueElement.value) {\n valueElement.value = searchValueElement.value;\n valueElement.dispatchEvent(new Event('change', {bubbles: true}));\n }\n\n searchValueElement.value = '';\n });\n\n this.component.addEventListener('hide.bs.dropdown', () => {\n this.searchInput.removeAttribute('aria-activedescendant');\n\n const listbox = document.querySelector(`#${this.searchInput.getAttribute('aria-controls')}[role=\"listbox\"]`);\n listbox.querySelectorAll('.active[role=\"option\"]').forEach(option => {\n option.classList.remove('active');\n });\n listbox.scrollTop = 0;\n\n // Use setTimeout to make sure the following code is executed after the click event is handled.\n setTimeout(() => {\n if (this.searchInput.value !== '') {\n this.searchInput.value = '';\n this.searchInput.dispatchEvent(new Event('input', {bubbles: true}));\n }\n });\n });\n\n this.renderDefault().catch(Notification.exception);\n }\n\n /**\n * Initialise an instance of the class.\n *\n * @param {int|null} cmid ID of the course module initiating the group search (optional).\n */\n static init(cmid = null) {\n return new GroupSearch(cmid);\n }\n\n /**\n * The overall div that contains the searching widget.\n *\n * @returns {string}\n */\n componentSelector() {\n return '.group-search';\n }\n\n /**\n * The dropdown div that contains the searching widget result space.\n *\n * @returns {string}\n */\n dropdownSelector() {\n return '.groupsearchdropdown';\n }\n\n /**\n * Build the content then replace the node.\n */\n async renderDropdown() {\n const {html, js} = await renderForPromise('core_group/comboboxsearch/resultset', {\n groups: this.getMatchedResults(),\n hasresults: this.getMatchedResults().length > 0,\n instance: this.instance,\n searchterm: this.getSearchTerm(),\n });\n replaceNodeContents(this.selectors.placeholder, html, js);\n // Remove aria-activedescendant when the available options change.\n this.searchInput.removeAttribute('aria-activedescendant');\n }\n\n /**\n * Build the content then replace the node by default we want our form to exist.\n */\n async renderDefault() {\n this.setMatchedResults(await this.filterDataset(await this.getDataset()));\n this.filterMatchDataset();\n\n await this.renderDropdown();\n\n this.updateNodes();\n }\n\n /**\n * Get the data we will be searching against in this component.\n *\n * @returns {Promise<*>}\n */\n async fetchDataset() {\n return await groupFetch(this.courseID, this.cmID).then((r) => r.groups);\n }\n\n /**\n * Dictate to the search component how and what we want to match upon.\n *\n * @param {Array} filterableData\n * @returns {Array} The users that match the given criteria.\n */\n async filterDataset(filterableData) {\n // Sometimes we just want to show everything.\n if (this.getPreppedSearchTerm() === '') {\n return filterableData;\n }\n return filterableData.filter((group) => Object.keys(group).some((key) => {\n if (group[key] === \"\" || this.bannedFilterFields.includes(key)) {\n return false;\n }\n return group[key].toString().toLowerCase().includes(this.getPreppedSearchTerm());\n }));\n }\n\n /**\n * Given we have a subset of the dataset, set the field that we matched upon to inform the end user.\n */\n filterMatchDataset() {\n this.setMatchedResults(\n this.getMatchedResults().map((group) => {\n return {\n id: group.id,\n name: group.name,\n groupimageurl: group.groupimageurl,\n };\n })\n );\n }\n\n /**\n * The handler for when a user interacts with the component.\n *\n * @param {MouseEvent} e The triggering event that we are working with.\n */\n async clickHandler(e) {\n if (e.target.closest(this.selectors.clearSearch)) {\n e.stopPropagation();\n // Clear the entered search query in the search bar.\n this.searchInput.value = '';\n this.setSearchTerms(this.searchInput.value);\n this.searchInput.focus();\n this.clearSearchButton.classList.add('d-none');\n // Display results.\n await this.filterrenderpipe();\n }\n }\n\n /**\n * The handler for when a user changes the value of the component (selects an option from the dropdown).\n *\n * @param {Event} e The change event.\n */\n changeHandler(e) {\n window.location = this.selectOneLink(e.target.value);\n }\n\n /**\n * Override the input event listener for the text input area.\n */\n registerInputHandlers() {\n // Register & handle the text input.\n this.searchInput.addEventListener('input', debounce(async() => {\n this.setSearchTerms(this.searchInput.value);\n // We can also require a set amount of input before search.\n if (this.getSearchTerm() === '') {\n // Hide the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.add('d-none');\n } else {\n // Display the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.remove('d-none');\n }\n // User has given something for us to filter against.\n await this.filterrenderpipe();\n }, 300));\n }\n\n /**\n * Build up the view all link that is dedicated to a particular result.\n * We will call this function when a user interacts with the combobox to redirect them to show their results in the page.\n *\n * @param {Number} groupID The ID of the group selected.\n */\n selectOneLink(groupID) {\n throw new Error(`selectOneLink(${groupID}) must be implemented in ${this.constructor.name}`);\n }\n}\n"],"names":["GroupSearch","search_combobox","constructor","cmid","selectors","this","courseid","placeholder","component","document","querySelector","componentSelector","courseID","dataset","instance","cmID","searchValueElement","searchInput","inputElement","addEventListener","toggleDropdown","valueElement","combobox","value","dispatchEvent","Event","bubbles","removeAttribute","listbox","getAttribute","querySelectorAll","forEach","option","classList","remove","scrollTop","setTimeout","renderDefault","catch","Notification","exception","dropdownSelector","html","js","groups","getMatchedResults","hasresults","length","searchterm","getSearchTerm","setMatchedResults","filterDataset","getDataset","filterMatchDataset","renderDropdown","updateNodes","then","r","filterableData","getPreppedSearchTerm","filter","group","Object","keys","some","key","bannedFilterFields","includes","toString","toLowerCase","map","id","name","groupimageurl","e","target","closest","clearSearch","stopPropagation","setSearchTerms","focus","clearSearchButton","add","filterrenderpipe","changeHandler","window","location","selectOneLink","registerInputHandlers","async","groupID","Error"],"mappings":"+rBA4BqBA,oBAAoBC,yBAWrCC,kBAAYC,4DAAO,mIAPE,CAAC,KAAM,OAAQ,uBAS3BC,UAAY,IAAIC,KAAKD,UACtBE,SAAU,2BACVC,YAAa,gEAEXC,UAAYC,SAASC,cAAcL,KAAKM,0BACzCC,SAAWJ,UAAUE,cAAcL,KAAKD,UAAUE,UAAUO,QAAQP,cAEpEQ,SAAWN,UAAUE,cAAcL,KAAKD,UAAUU,UAAUD,QAAQC,cACpEC,KAAOZ,WAENa,mBAAqBX,KAAKG,UAAUE,yBAAkBL,KAAKY,YAAYJ,QAAQK,eACrFF,mBAAmBG,iBAAiB,UAAU,UACrCC,uBAECC,aAAehB,KAAKG,UAAUE,yBAAkBL,KAAKiB,SAAST,QAAQK,eACxEG,aAAaE,QAAUP,mBAAmBO,QAC1CF,aAAaE,MAAQP,mBAAmBO,MACxCF,aAAaG,cAAc,IAAIC,MAAM,SAAU,CAACC,SAAS,MAG7DV,mBAAmBO,MAAQ,WAG1Bf,UAAUW,iBAAiB,oBAAoB,UAC3CF,YAAYU,gBAAgB,+BAE3BC,QAAUnB,SAASC,yBAAkBL,KAAKY,YAAYY,aAAa,sCACzED,QAAQE,iBAAiB,0BAA0BC,SAAQC,SACvDA,OAAOC,UAAUC,OAAO,aAE5BN,QAAQO,UAAY,EAGpBC,YAAW,KACwB,KAA3B/B,KAAKY,YAAYM,aACZN,YAAYM,MAAQ,QACpBN,YAAYO,cAAc,IAAIC,MAAM,QAAS,CAACC,SAAS,iBAKnEW,gBAAgBC,MAAMC,sBAAaC,gCASjC,IAAIxC,mEADI,MASnBW,0BACW,gBAQX8B,yBACW,oDAODC,KAACA,KAADC,GAAOA,UAAY,+BAAiB,sCAAuC,CAC7EC,OAAQvC,KAAKwC,oBACbC,WAAYzC,KAAKwC,oBAAoBE,OAAS,EAC9CjC,SAAUT,KAAKS,SACfkC,WAAY3C,KAAK4C,qDAED5C,KAAKD,UAAUG,YAAamC,KAAMC,SAEjD1B,YAAYU,gBAAgB,oDAO5BuB,wBAAwB7C,KAAK8C,oBAAoB9C,KAAK+C,oBACtDC,2BAEChD,KAAKiD,sBAENC,gDASQ,0BAAWlD,KAAKO,SAAUP,KAAKU,MAAMyC,MAAMC,GAAMA,EAAEb,6BAShDc,sBAEoB,KAAhCrD,KAAKsD,uBACED,eAEJA,eAAeE,QAAQC,OAAUC,OAAOC,KAAKF,OAAOG,MAAMC,KAC1C,KAAfJ,MAAMI,OAAe5D,KAAK6D,mBAAmBC,SAASF,MAGnDJ,MAAMI,KAAKG,WAAWC,cAAcF,SAAS9D,KAAKsD,4BAOjEN,0BACSH,kBACD7C,KAAKwC,oBAAoByB,KAAKT,QACnB,CACHU,GAAIV,MAAMU,GACVC,KAAMX,MAAMW,KACZC,cAAeZ,MAAMY,sCAWlBC,GACXA,EAAEC,OAAOC,QAAQvE,KAAKD,UAAUyE,eAChCH,EAAEI,uBAEG7D,YAAYM,MAAQ,QACpBwD,eAAe1E,KAAKY,YAAYM,YAChCN,YAAY+D,aACZC,kBAAkBhD,UAAUiD,IAAI,gBAE/B7E,KAAK8E,oBASnBC,cAAcV,GACVW,OAAOC,SAAWjF,KAAKkF,cAAcb,EAAEC,OAAOpD,OAMlDiE,6BAESvE,YAAYE,iBAAiB,SAAS,oBAASsE,eAC3CV,eAAe1E,KAAKY,YAAYM,OAER,KAAzBlB,KAAK4C,qBAEAgC,kBAAkBhD,UAAUiD,IAAI,eAGhCD,kBAAkBhD,UAAUC,OAAO,gBAGtC7B,KAAK8E,qBACZ,MASPI,cAAcG,eACJ,IAAIC,8BAAuBD,4CAAmCrF,KAAKH,YAAYsE"} \ No newline at end of file diff --git a/group/amd/src/comboboxsearch/group.js b/group/amd/src/comboboxsearch/group.js index 7789ebbd79f04..d21cd94c8d57b 100644 --- a/group/amd/src/comboboxsearch/group.js +++ b/group/amd/src/comboboxsearch/group.js @@ -62,7 +62,7 @@ export default class GroupSearch extends search_combobox { searchValueElement.value = ''; }); - this.$component.on('hide.bs.dropdown', () => { + this.component.addEventListener('hide.bs.dropdown', () => { this.searchInput.removeAttribute('aria-activedescendant'); const listbox = document.querySelector(`#${this.searchInput.getAttribute('aria-controls')}[role="listbox"]`); diff --git a/lib/amd/build/comboboxsearch/search_combobox.min.js b/lib/amd/build/comboboxsearch/search_combobox.min.js index d9a692968a618..a51862232c7e3 100644 --- a/lib/amd/build/comboboxsearch/search_combobox.min.js +++ b/lib/amd/build/comboboxsearch/search_combobox.min.js @@ -1,10 +1,10 @@ -define("core/comboboxsearch/search_combobox",["exports","jquery","core/utils","core/pending"],(function(_exports,_jquery,_utils,_pending){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} +define("core/comboboxsearch/search_combobox",["exports","jquery","theme_boost/bootstrap/dropdown","core/utils","core/pending"],(function(_exports,_jquery,_dropdown,_utils,_pending){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} /** * The class that manages the state of the search within a combobox. * * @module core/comboboxsearch/search_combobox * @copyright 2023 Mathew May * @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),_pending=_interopRequireDefault(_pending);return _exports.default=class{constructor(){var _this$searchInput$val,_this$searchInput;_defineProperty(this,"selectors",{component:this.componentSelector(),toggle:'[data-bs-toggle="dropdown"]',instance:'[data-region="instance"]',input:'[data-action="search"]',clearSearch:'[data-action="clearsearch"]',dropdown:this.dropdownSelector(),resultitems:'[role="option"]',viewall:"#select-all",combobox:'[role="combobox"]'}),_defineProperty(this,"matchedResults",[]),_defineProperty(this,"searchTerm",""),_defineProperty(this,"preppedSearchTerm",null),_defineProperty(this,"resultNodes",[]),_defineProperty(this,"currentNode",null),_defineProperty(this,"currentViewAll",null),_defineProperty(this,"dataset",null),_defineProperty(this,"datasetSize",0),_defineProperty(this,"component",document.querySelector(this.selectors.component)),_defineProperty(this,"instance",this.component.dataset.instance),_defineProperty(this,"toggle",this.component.querySelector(this.selectors.toggle)),_defineProperty(this,"searchInput",this.component.querySelector(this.selectors.input)),_defineProperty(this,"searchDropdown",this.component.querySelector(this.selectors.dropdown)),_defineProperty(this,"clearSearchButton",this.component.querySelector(this.selectors.clearSearch)),_defineProperty(this,"combobox",this.component.querySelector(this.selectors.combobox)),_defineProperty(this,"$component",(0,_jquery.default)(this.component)),this.setSearchTerms(null!==(_this$searchInput$val=null===(_this$searchInput=this.searchInput)||void 0===_this$searchInput?void 0:_this$searchInput.value)&&void 0!==_this$searchInput$val?_this$searchInput$val:""),this.registerClickHandlers(),null!==this.searchInput&&(this.registerInputHandlers(),this.registerChangeHandlers()),""!==this.getSearchTerm()&&this.clearSearchButton.classList.remove("d-none")}fetchDataset(){throw new Error("fetchDataset() must be implemented in ".concat(this.constructor.name))}filterDataset(dataset){throw new Error("filterDataset(".concat(dataset,") must be implemented in ").concat(this.constructor.name))}filterMatchDataset(){throw new Error("filterMatchDataset() must be implemented in ".concat(this.constructor.name))}renderDropdown(){throw new Error("renderDropdown() must be implemented in ".concat(this.constructor.name))}componentSelector(){throw new Error("componentSelector() must be implemented in ".concat(this.constructor.name))}dropdownSelector(){throw new Error("dropdownSelector() must be implemented in ".concat(this.constructor.name))}triggerSelector(){window.console.warning("triggerSelector() is deprecated. Consider using this.selectors.toggle")}async getDataset(){return this.dataset||(this.dataset=await this.fetchDataset()),this.datasetSize=this.dataset.length,this.dataset}getDatasetSize(){return this.datasetSize}getMatchedResults(){return this.matchedResults}setMatchedResults(result){this.matchedResults=result}getSearchTerm(){return this.searchTerm}getPreppedSearchTerm(){return this.preppedSearchTerm}setSearchTerms(result){this.searchTerm=result,this.preppedSearchTerm=result.toLowerCase()}getHTMLElements(){return this.updateNodes(),{searchDropdown:this.searchDropdown,currentViewAll:this.currentViewAll,searchInput:this.searchInput,clearSearchButton:this.clearSearchButton,trigger:this.component.querySelector(this.selectors.trigger)}}closeSearch(){let clear=arguments.length>0&&void 0!==arguments[0]&&arguments[0];this.toggleDropdown(),clear&&(this.clearSearchButton.classList.add("d-none"),this.setSearchTerms(""),this.searchInput.value="")}searchResultsVisible(){const{searchDropdown:searchDropdown}=this.getHTMLElements();return null!==searchDropdown.offsetParent}toggleDropdown(){arguments.length>0&&void 0!==arguments[0]&&arguments[0]?(0,_jquery.default)(this.toggle).dropdown("show"):(0,_jquery.default)(this.toggle).dropdown("hide")}updateNodes(){this.resultNodes=[...this.component.querySelectorAll(this.selectors.resultitems)],this.currentNode=this.resultNodes.find((r=>r.id===document.activeElement.id)),this.currentViewAll=this.component.querySelector(this.selectors.viewall),this.clearSearchButton=this.component.querySelector(this.selectors.clearSearch),this.searchInput=this.component.querySelector(this.selectors.input),this.searchDropdown=this.component.querySelector(this.selectors.dropdown)}registerClickHandlers(){this.component.addEventListener("click",this.clickHandler.bind(this))}registerChangeHandlers(){this.component.querySelector("#".concat(this.combobox.dataset.inputElement)).addEventListener("change",this.changeHandler.bind(this))}registerInputHandlers(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{if(this.getSearchTerm()===this.searchInput.value&&this.searchResultsVisible())return void window.console.warn("Search term matches input value - skipping");this.setSearchTerms(this.searchInput.value);const pendingPromise=new _pending.default;""===this.getSearchTerm()?(this.toggleDropdown(),this.clearSearchButton.classList.add("d-none"),await this.filterrenderpipe()):(this.clearSearchButton.classList.remove("d-none"),await this.renderAndShow()),pendingPromise.resolve()}),300,{pending:!0}))}async filterrenderpipe(){this.updateNodes(),this.setMatchedResults(await this.filterDataset(await this.getDataset())),this.filterMatchDataset(),await this.renderDropdown()}async renderAndShow(){this.setMatchedResults(await this.filterDataset(await this.getDataset())),await this.filterMatchDataset(),await this.renderDropdown(),this.toggleDropdown(!0)}async clickHandler(e){this.updateNodes(),e.target.closest(this.selectors.clearSearch)&&(this.closeSearch(!0),this.searchInput.focus(),this.searchInput.removeAttribute("aria-activedescendant")),""!==this.getSearchTerm()&&!this.getHTMLElements().searchDropdown.classList.contains("show")&&e.target.closest(this.selectors.input)&&await this.renderAndShow()}changeHandler(e){}},_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),_dropdown=_interopRequireDefault(_dropdown),_pending=_interopRequireDefault(_pending);return _exports.default=class{constructor(){var _this$searchInput$val,_this$searchInput;_defineProperty(this,"selectors",{component:this.componentSelector(),toggle:'[data-bs-toggle="dropdown"]',instance:'[data-region="instance"]',input:'[data-action="search"]',clearSearch:'[data-action="clearsearch"]',dropdown:this.dropdownSelector(),resultitems:'[role="option"]',viewall:"#select-all",combobox:'[role="combobox"]'}),_defineProperty(this,"matchedResults",[]),_defineProperty(this,"searchTerm",""),_defineProperty(this,"preppedSearchTerm",null),_defineProperty(this,"resultNodes",[]),_defineProperty(this,"currentNode",null),_defineProperty(this,"currentViewAll",null),_defineProperty(this,"dataset",null),_defineProperty(this,"datasetSize",0),_defineProperty(this,"component",document.querySelector(this.selectors.component)),_defineProperty(this,"instance",this.component.dataset.instance),_defineProperty(this,"toggle",this.component.querySelector(this.selectors.toggle)),_defineProperty(this,"searchInput",this.component.querySelector(this.selectors.input)),_defineProperty(this,"searchDropdown",this.component.querySelector(this.selectors.dropdown)),_defineProperty(this,"clearSearchButton",this.component.querySelector(this.selectors.clearSearch)),_defineProperty(this,"combobox",this.component.querySelector(this.selectors.combobox)),_defineProperty(this,"$component",(0,_jquery.default)(this.component)),this.setSearchTerms(null!==(_this$searchInput$val=null===(_this$searchInput=this.searchInput)||void 0===_this$searchInput?void 0:_this$searchInput.value)&&void 0!==_this$searchInput$val?_this$searchInput$val:""),this.registerClickHandlers(),null!==this.searchInput&&(this.registerInputHandlers(),this.registerChangeHandlers()),""!==this.getSearchTerm()&&this.clearSearchButton.classList.remove("d-none")}fetchDataset(){throw new Error("fetchDataset() must be implemented in ".concat(this.constructor.name))}filterDataset(dataset){throw new Error("filterDataset(".concat(dataset,") must be implemented in ").concat(this.constructor.name))}filterMatchDataset(){throw new Error("filterMatchDataset() must be implemented in ".concat(this.constructor.name))}renderDropdown(){throw new Error("renderDropdown() must be implemented in ".concat(this.constructor.name))}componentSelector(){throw new Error("componentSelector() must be implemented in ".concat(this.constructor.name))}dropdownSelector(){throw new Error("dropdownSelector() must be implemented in ".concat(this.constructor.name))}triggerSelector(){window.console.warning("triggerSelector() is deprecated. Consider using this.selectors.toggle")}async getDataset(){return this.dataset||(this.dataset=await this.fetchDataset()),this.datasetSize=this.dataset.length,this.dataset}getDatasetSize(){return this.datasetSize}getMatchedResults(){return this.matchedResults}setMatchedResults(result){this.matchedResults=result}getSearchTerm(){return this.searchTerm}getPreppedSearchTerm(){return this.preppedSearchTerm}setSearchTerms(result){this.searchTerm=result,this.preppedSearchTerm=result.toLowerCase()}getHTMLElements(){return this.updateNodes(),{searchDropdown:this.searchDropdown,currentViewAll:this.currentViewAll,searchInput:this.searchInput,clearSearchButton:this.clearSearchButton,trigger:this.component.querySelector(this.selectors.trigger)}}closeSearch(){let clear=arguments.length>0&&void 0!==arguments[0]&&arguments[0];this.toggleDropdown(),clear&&(this.clearSearchButton.classList.add("d-none"),this.setSearchTerms(""),this.searchInput.value="")}searchResultsVisible(){const{searchDropdown:searchDropdown}=this.getHTMLElements();return null!==searchDropdown.offsetParent}toggleDropdown(){arguments.length>0&&void 0!==arguments[0]&&arguments[0]?_dropdown.default.getOrCreateInstance(this.toggle).show():_dropdown.default.getOrCreateInstance(this.toggle).hide()}updateNodes(){this.resultNodes=[...this.component.querySelectorAll(this.selectors.resultitems)],this.currentNode=this.resultNodes.find((r=>r.id===document.activeElement.id)),this.currentViewAll=this.component.querySelector(this.selectors.viewall),this.clearSearchButton=this.component.querySelector(this.selectors.clearSearch),this.searchInput=this.component.querySelector(this.selectors.input),this.searchDropdown=this.component.querySelector(this.selectors.dropdown)}registerClickHandlers(){this.component.addEventListener("click",this.clickHandler.bind(this))}registerChangeHandlers(){this.component.querySelector("#".concat(this.combobox.dataset.inputElement)).addEventListener("change",this.changeHandler.bind(this))}registerInputHandlers(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{if(this.getSearchTerm()===this.searchInput.value&&this.searchResultsVisible())return void window.console.warn("Search term matches input value - skipping");this.setSearchTerms(this.searchInput.value);const pendingPromise=new _pending.default;""===this.getSearchTerm()?(this.toggleDropdown(),this.clearSearchButton.classList.add("d-none"),await this.filterrenderpipe()):(this.clearSearchButton.classList.remove("d-none"),await this.renderAndShow()),pendingPromise.resolve()}),300,{pending:!0}))}async filterrenderpipe(){this.updateNodes(),this.setMatchedResults(await this.filterDataset(await this.getDataset())),this.filterMatchDataset(),await this.renderDropdown()}async renderAndShow(){this.setMatchedResults(await this.filterDataset(await this.getDataset())),await this.filterMatchDataset(),await this.renderDropdown(),this.toggleDropdown(!0)}async clickHandler(e){this.updateNodes(),e.target.closest(this.selectors.clearSearch)&&(this.closeSearch(!0),this.searchInput.focus(),this.searchInput.removeAttribute("aria-activedescendant")),""!==this.getSearchTerm()&&!this.getHTMLElements().searchDropdown.classList.contains("show")&&e.target.closest(this.selectors.input)&&await this.renderAndShow()}changeHandler(e){}},_exports.default})); //# sourceMappingURL=search_combobox.min.js.map \ No newline at end of file diff --git a/lib/amd/build/comboboxsearch/search_combobox.min.js.map b/lib/amd/build/comboboxsearch/search_combobox.min.js.map index 5a3ac8c1d78cb..0ad01b7b2fd3b 100644 --- a/lib/amd/build/comboboxsearch/search_combobox.min.js.map +++ b/lib/amd/build/comboboxsearch/search_combobox.min.js.map @@ -1 +1 @@ -{"version":3,"file":"search_combobox.min.js","sources":["../../src/comboboxsearch/search_combobox.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\nimport $ from 'jquery';\nimport {debounce} from 'core/utils';\nimport Pending from 'core/pending';\n\n/**\n * The class that manages the state of the search within a combobox.\n *\n * @module core/comboboxsearch/search_combobox\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default class {\n // Define our standard lookups.\n selectors = {\n component: this.componentSelector(),\n toggle: '[data-bs-toggle=\"dropdown\"]',\n instance: '[data-region=\"instance\"]',\n input: '[data-action=\"search\"]',\n clearSearch: '[data-action=\"clearsearch\"]',\n dropdown: this.dropdownSelector(),\n resultitems: '[role=\"option\"]',\n viewall: '#select-all',\n combobox: '[role=\"combobox\"]',\n };\n\n // The results from the called filter function.\n matchedResults = [];\n\n // What did the user search for?\n searchTerm = '';\n\n // What the user searched for as a lowercase.\n preppedSearchTerm = null;\n\n // The DOM nodes after the dropdown render.\n resultNodes = [];\n\n // Where does the user currently have focus?\n currentNode = null;\n\n // The current node for the view all link.\n currentViewAll = null;\n\n dataset = null;\n\n datasetSize = 0;\n\n // DOM nodes that persist.\n component = document.querySelector(this.selectors.component);\n instance = this.component.dataset.instance;\n toggle = this.component.querySelector(this.selectors.toggle);\n searchInput = this.component.querySelector(this.selectors.input);\n searchDropdown = this.component.querySelector(this.selectors.dropdown);\n clearSearchButton = this.component.querySelector(this.selectors.clearSearch);\n combobox = this.component.querySelector(this.selectors.combobox);\n $component = $(this.component);\n\n constructor() {\n // If we have a search input, try to get the value otherwise fallback.\n this.setSearchTerms(this.searchInput?.value ?? '');\n // Begin handling the base search component.\n this.registerClickHandlers();\n\n // Conditionally set up the input handler since we don't know exactly how we were called.\n // If the combobox is rendered later, then you'll need to call this.registerInputHandlers() manually.\n // An example of this is the collapse columns in the gradebook.\n if (this.searchInput !== null) {\n this.registerInputHandlers();\n this.registerChangeHandlers();\n }\n\n // If we have a search term, show the clear button.\n if (this.getSearchTerm() !== '') {\n this.clearSearchButton.classList.remove('d-none');\n }\n }\n\n /**\n * Stub out a required function.\n */\n fetchDataset() {\n throw new Error(`fetchDataset() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Stub out a required function.\n * @param {Array} dataset\n */\n filterDataset(dataset) {\n throw new Error(`filterDataset(${dataset}) must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Stub out a required function.\n */\n filterMatchDataset() {\n throw new Error(`filterMatchDataset() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Stub out a required function.\n */\n renderDropdown() {\n throw new Error(`renderDropdown() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Stub out a required function.\n */\n componentSelector() {\n throw new Error(`componentSelector() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Stub out a required function.\n */\n dropdownSelector() {\n throw new Error(`dropdownSelector() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Stub out a required function.\n * @deprecated since Moodle 4.4\n */\n triggerSelector() {\n window.console.warning('triggerSelector() is deprecated. Consider using this.selectors.toggle');\n }\n\n /**\n * Return the dataset that we will be searching upon.\n *\n * @returns {Promise}\n */\n async getDataset() {\n if (!this.dataset) {\n this.dataset = await this.fetchDataset();\n }\n this.datasetSize = this.dataset.length;\n return this.dataset;\n }\n\n /**\n * Return the size of the dataset.\n *\n * @returns {number}\n */\n getDatasetSize() {\n return this.datasetSize;\n }\n\n /**\n * Return the results of the filter upon the dataset.\n *\n * @returns {Array}\n */\n getMatchedResults() {\n return this.matchedResults;\n }\n\n /**\n * Given a filter has been run across the dataset, store the matched results.\n *\n * @param {Array} result\n */\n setMatchedResults(result) {\n this.matchedResults = result;\n }\n\n /**\n * Get the value that the user entered.\n *\n * @returns {string}\n */\n getSearchTerm() {\n return this.searchTerm;\n }\n\n /**\n * Get the transformed search value.\n *\n * @returns {string}\n */\n getPreppedSearchTerm() {\n return this.preppedSearchTerm;\n }\n\n /**\n * When a user searches for something, set our variable to manage it.\n *\n * @param {string} result\n */\n setSearchTerms(result) {\n this.searchTerm = result;\n this.preppedSearchTerm = result.toLowerCase();\n }\n\n /**\n * Return an object containing a handfull of dom nodes that we sometimes need the value of.\n *\n * @returns {object}\n */\n getHTMLElements() {\n this.updateNodes();\n return {\n searchDropdown: this.searchDropdown,\n currentViewAll: this.currentViewAll,\n searchInput: this.searchInput,\n clearSearchButton: this.clearSearchButton,\n trigger: this.component.querySelector(this.selectors.trigger),\n };\n }\n\n /**\n * When called, close the dropdown and reset the input field attributes.\n *\n * @param {Boolean} clear Conditionality clear the input box.\n */\n closeSearch(clear = false) {\n this.toggleDropdown();\n if (clear) {\n // Hide the \"clear\" search button search bar.\n this.clearSearchButton.classList.add('d-none');\n // Clear the entered search query in the search bar and hide the search results container.\n this.setSearchTerms('');\n this.searchInput.value = \"\";\n }\n }\n\n /**\n * Check whether search results are currently visible.\n *\n * @returns {Boolean}\n */\n searchResultsVisible() {\n const {searchDropdown} = this.getHTMLElements();\n // If a Node is not visible, then the offsetParent is null.\n return searchDropdown.offsetParent !== null;\n }\n\n /**\n * When called, update the dropdown fields.\n *\n * @param {Boolean} on Flag to toggle hiding or showing values.\n */\n toggleDropdown(on = false) {\n if (on) {\n $(this.toggle).dropdown('show');\n } else {\n $(this.toggle).dropdown('hide');\n }\n }\n\n /**\n * These class members change when a new result set is rendered. So update for fresh data.\n */\n updateNodes() {\n this.resultNodes = [...this.component.querySelectorAll(this.selectors.resultitems)];\n this.currentNode = this.resultNodes.find(r => r.id === document.activeElement.id);\n this.currentViewAll = this.component.querySelector(this.selectors.viewall);\n this.clearSearchButton = this.component.querySelector(this.selectors.clearSearch);\n this.searchInput = this.component.querySelector(this.selectors.input);\n this.searchDropdown = this.component.querySelector(this.selectors.dropdown);\n }\n\n /**\n * Register clickable event listeners.\n */\n registerClickHandlers() {\n // Register click events within the component.\n this.component.addEventListener('click', this.clickHandler.bind(this));\n }\n\n /**\n * Register change event listeners.\n */\n registerChangeHandlers() {\n const valueElement = this.component.querySelector(`#${this.combobox.dataset.inputElement}`);\n valueElement.addEventListener('change', this.changeHandler.bind(this));\n }\n\n /**\n * Register input event listener for the text input area.\n */\n registerInputHandlers() {\n // Register & handle the text input.\n this.searchInput.addEventListener('input', debounce(async() => {\n if (this.getSearchTerm() === this.searchInput.value && this.searchResultsVisible()) {\n window.console.warn(`Search term matches input value - skipping`);\n // The debounce canhappen multiple times quickly. GRrargh\n return;\n }\n this.setSearchTerms(this.searchInput.value);\n\n const pendingPromise = new Pending();\n if (this.getSearchTerm() === '') {\n this.toggleDropdown();\n this.clearSearchButton.classList.add('d-none');\n await this.filterrenderpipe();\n } else {\n this.clearSearchButton.classList.remove('d-none');\n await this.renderAndShow();\n }\n pendingPromise.resolve();\n }, 300, {pending: true}));\n }\n\n /**\n * Update any changeable nodes, filter and then render the result.\n *\n * @returns {Promise}\n */\n async filterrenderpipe() {\n this.updateNodes();\n this.setMatchedResults(await this.filterDataset(await this.getDataset()));\n this.filterMatchDataset();\n await this.renderDropdown();\n }\n\n /**\n * A combo method to take the matching fields and render out the results.\n *\n * @returns {Promise}\n */\n async renderAndShow() {\n // User has given something for us to filter against.\n this.setMatchedResults(await this.filterDataset(await this.getDataset()));\n await this.filterMatchDataset();\n // Replace the dropdown node contents and show the results.\n await this.renderDropdown();\n // Set the dropdown to open.\n this.toggleDropdown(true);\n }\n\n /**\n * The handler for when a user interacts with the component.\n *\n * @param {MouseEvent} e The triggering event that we are working with.\n */\n async clickHandler(e) {\n this.updateNodes();\n // The \"clear search\" button is triggered.\n if (e.target.closest(this.selectors.clearSearch)) {\n this.closeSearch(true);\n this.searchInput.focus();\n // Remove aria-activedescendant when the available options change.\n this.searchInput.removeAttribute('aria-activedescendant');\n }\n // User may have accidentally clicked off the dropdown and wants to reopen it.\n if (\n this.getSearchTerm() !== ''\n && !this.getHTMLElements().searchDropdown.classList.contains('show')\n && e.target.closest(this.selectors.input)\n ) {\n await this.renderAndShow();\n }\n }\n\n /**\n * The handler for when a user changes the value of the component (selects an option from the dropdown).\n *\n * @param {Event} e The change event.\n */\n // eslint-disable-next-line no-unused-vars\n changeHandler(e) {\n // Components may override this method to do something.\n }\n}\n"],"names":["constructor","component","this","componentSelector","toggle","instance","input","clearSearch","dropdown","dropdownSelector","resultitems","viewall","combobox","document","querySelector","selectors","dataset","setSearchTerms","searchInput","_this$searchInput","value","registerClickHandlers","registerInputHandlers","registerChangeHandlers","getSearchTerm","clearSearchButton","classList","remove","fetchDataset","Error","name","filterDataset","filterMatchDataset","renderDropdown","triggerSelector","window","console","warning","datasetSize","length","getDatasetSize","getMatchedResults","matchedResults","setMatchedResults","result","searchTerm","getPreppedSearchTerm","preppedSearchTerm","toLowerCase","getHTMLElements","updateNodes","searchDropdown","currentViewAll","trigger","closeSearch","clear","toggleDropdown","add","searchResultsVisible","offsetParent","resultNodes","querySelectorAll","currentNode","find","r","id","activeElement","addEventListener","clickHandler","bind","inputElement","changeHandler","async","warn","pendingPromise","Pending","filterrenderpipe","renderAndShow","resolve","pending","getDataset","e","target","closest","focus","removeAttribute","contains"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"search_combobox.min.js","sources":["../../src/comboboxsearch/search_combobox.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\nimport $ from 'jquery';\nimport Dropdown from 'theme_boost/bootstrap/dropdown';\nimport {debounce} from 'core/utils';\nimport Pending from 'core/pending';\n\n/**\n * The class that manages the state of the search within a combobox.\n *\n * @module core/comboboxsearch/search_combobox\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default class {\n // Define our standard lookups.\n selectors = {\n component: this.componentSelector(),\n toggle: '[data-bs-toggle=\"dropdown\"]',\n instance: '[data-region=\"instance\"]',\n input: '[data-action=\"search\"]',\n clearSearch: '[data-action=\"clearsearch\"]',\n dropdown: this.dropdownSelector(),\n resultitems: '[role=\"option\"]',\n viewall: '#select-all',\n combobox: '[role=\"combobox\"]',\n };\n\n // The results from the called filter function.\n matchedResults = [];\n\n // What did the user search for?\n searchTerm = '';\n\n // What the user searched for as a lowercase.\n preppedSearchTerm = null;\n\n // The DOM nodes after the dropdown render.\n resultNodes = [];\n\n // Where does the user currently have focus?\n currentNode = null;\n\n // The current node for the view all link.\n currentViewAll = null;\n\n dataset = null;\n\n datasetSize = 0;\n\n // DOM nodes that persist.\n component = document.querySelector(this.selectors.component);\n instance = this.component.dataset.instance;\n toggle = this.component.querySelector(this.selectors.toggle);\n searchInput = this.component.querySelector(this.selectors.input);\n searchDropdown = this.component.querySelector(this.selectors.dropdown);\n clearSearchButton = this.component.querySelector(this.selectors.clearSearch);\n combobox = this.component.querySelector(this.selectors.combobox);\n $component = $(this.component);\n\n constructor() {\n // If we have a search input, try to get the value otherwise fallback.\n this.setSearchTerms(this.searchInput?.value ?? '');\n // Begin handling the base search component.\n this.registerClickHandlers();\n\n // Conditionally set up the input handler since we don't know exactly how we were called.\n // If the combobox is rendered later, then you'll need to call this.registerInputHandlers() manually.\n // An example of this is the collapse columns in the gradebook.\n if (this.searchInput !== null) {\n this.registerInputHandlers();\n this.registerChangeHandlers();\n }\n\n // If we have a search term, show the clear button.\n if (this.getSearchTerm() !== '') {\n this.clearSearchButton.classList.remove('d-none');\n }\n }\n\n /**\n * Stub out a required function.\n */\n fetchDataset() {\n throw new Error(`fetchDataset() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Stub out a required function.\n * @param {Array} dataset\n */\n filterDataset(dataset) {\n throw new Error(`filterDataset(${dataset}) must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Stub out a required function.\n */\n filterMatchDataset() {\n throw new Error(`filterMatchDataset() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Stub out a required function.\n */\n renderDropdown() {\n throw new Error(`renderDropdown() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Stub out a required function.\n */\n componentSelector() {\n throw new Error(`componentSelector() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Stub out a required function.\n */\n dropdownSelector() {\n throw new Error(`dropdownSelector() must be implemented in ${this.constructor.name}`);\n }\n\n /**\n * Stub out a required function.\n * @deprecated since Moodle 4.4\n */\n triggerSelector() {\n window.console.warning('triggerSelector() is deprecated. Consider using this.selectors.toggle');\n }\n\n /**\n * Return the dataset that we will be searching upon.\n *\n * @returns {Promise}\n */\n async getDataset() {\n if (!this.dataset) {\n this.dataset = await this.fetchDataset();\n }\n this.datasetSize = this.dataset.length;\n return this.dataset;\n }\n\n /**\n * Return the size of the dataset.\n *\n * @returns {number}\n */\n getDatasetSize() {\n return this.datasetSize;\n }\n\n /**\n * Return the results of the filter upon the dataset.\n *\n * @returns {Array}\n */\n getMatchedResults() {\n return this.matchedResults;\n }\n\n /**\n * Given a filter has been run across the dataset, store the matched results.\n *\n * @param {Array} result\n */\n setMatchedResults(result) {\n this.matchedResults = result;\n }\n\n /**\n * Get the value that the user entered.\n *\n * @returns {string}\n */\n getSearchTerm() {\n return this.searchTerm;\n }\n\n /**\n * Get the transformed search value.\n *\n * @returns {string}\n */\n getPreppedSearchTerm() {\n return this.preppedSearchTerm;\n }\n\n /**\n * When a user searches for something, set our variable to manage it.\n *\n * @param {string} result\n */\n setSearchTerms(result) {\n this.searchTerm = result;\n this.preppedSearchTerm = result.toLowerCase();\n }\n\n /**\n * Return an object containing a handfull of dom nodes that we sometimes need the value of.\n *\n * @returns {object}\n */\n getHTMLElements() {\n this.updateNodes();\n return {\n searchDropdown: this.searchDropdown,\n currentViewAll: this.currentViewAll,\n searchInput: this.searchInput,\n clearSearchButton: this.clearSearchButton,\n trigger: this.component.querySelector(this.selectors.trigger),\n };\n }\n\n /**\n * When called, close the dropdown and reset the input field attributes.\n *\n * @param {Boolean} clear Conditionality clear the input box.\n */\n closeSearch(clear = false) {\n this.toggleDropdown();\n if (clear) {\n // Hide the \"clear\" search button search bar.\n this.clearSearchButton.classList.add('d-none');\n // Clear the entered search query in the search bar and hide the search results container.\n this.setSearchTerms('');\n this.searchInput.value = \"\";\n }\n }\n\n /**\n * Check whether search results are currently visible.\n *\n * @returns {Boolean}\n */\n searchResultsVisible() {\n const {searchDropdown} = this.getHTMLElements();\n // If a Node is not visible, then the offsetParent is null.\n return searchDropdown.offsetParent !== null;\n }\n\n /**\n * When called, update the dropdown fields.\n *\n * @param {Boolean} on Flag to toggle hiding or showing values.\n */\n toggleDropdown(on = false) {\n if (on) {\n Dropdown.getOrCreateInstance(this.toggle).show();\n } else {\n Dropdown.getOrCreateInstance(this.toggle).hide();\n }\n }\n\n /**\n * These class members change when a new result set is rendered. So update for fresh data.\n */\n updateNodes() {\n this.resultNodes = [...this.component.querySelectorAll(this.selectors.resultitems)];\n this.currentNode = this.resultNodes.find(r => r.id === document.activeElement.id);\n this.currentViewAll = this.component.querySelector(this.selectors.viewall);\n this.clearSearchButton = this.component.querySelector(this.selectors.clearSearch);\n this.searchInput = this.component.querySelector(this.selectors.input);\n this.searchDropdown = this.component.querySelector(this.selectors.dropdown);\n }\n\n /**\n * Register clickable event listeners.\n */\n registerClickHandlers() {\n // Register click events within the component.\n this.component.addEventListener('click', this.clickHandler.bind(this));\n }\n\n /**\n * Register change event listeners.\n */\n registerChangeHandlers() {\n const valueElement = this.component.querySelector(`#${this.combobox.dataset.inputElement}`);\n valueElement.addEventListener('change', this.changeHandler.bind(this));\n }\n\n /**\n * Register input event listener for the text input area.\n */\n registerInputHandlers() {\n // Register & handle the text input.\n this.searchInput.addEventListener('input', debounce(async() => {\n if (this.getSearchTerm() === this.searchInput.value && this.searchResultsVisible()) {\n window.console.warn(`Search term matches input value - skipping`);\n // The debounce canhappen multiple times quickly. GRrargh\n return;\n }\n this.setSearchTerms(this.searchInput.value);\n\n const pendingPromise = new Pending();\n if (this.getSearchTerm() === '') {\n this.toggleDropdown();\n this.clearSearchButton.classList.add('d-none');\n await this.filterrenderpipe();\n } else {\n this.clearSearchButton.classList.remove('d-none');\n await this.renderAndShow();\n }\n pendingPromise.resolve();\n }, 300, {pending: true}));\n }\n\n /**\n * Update any changeable nodes, filter and then render the result.\n *\n * @returns {Promise}\n */\n async filterrenderpipe() {\n this.updateNodes();\n this.setMatchedResults(await this.filterDataset(await this.getDataset()));\n this.filterMatchDataset();\n await this.renderDropdown();\n }\n\n /**\n * A combo method to take the matching fields and render out the results.\n *\n * @returns {Promise}\n */\n async renderAndShow() {\n // User has given something for us to filter against.\n this.setMatchedResults(await this.filterDataset(await this.getDataset()));\n await this.filterMatchDataset();\n // Replace the dropdown node contents and show the results.\n await this.renderDropdown();\n // Set the dropdown to open.\n this.toggleDropdown(true);\n }\n\n /**\n * The handler for when a user interacts with the component.\n *\n * @param {MouseEvent} e The triggering event that we are working with.\n */\n async clickHandler(e) {\n this.updateNodes();\n // The \"clear search\" button is triggered.\n if (e.target.closest(this.selectors.clearSearch)) {\n this.closeSearch(true);\n this.searchInput.focus();\n // Remove aria-activedescendant when the available options change.\n this.searchInput.removeAttribute('aria-activedescendant');\n }\n // User may have accidentally clicked off the dropdown and wants to reopen it.\n if (\n this.getSearchTerm() !== ''\n && !this.getHTMLElements().searchDropdown.classList.contains('show')\n && e.target.closest(this.selectors.input)\n ) {\n await this.renderAndShow();\n }\n }\n\n /**\n * The handler for when a user changes the value of the component (selects an option from the dropdown).\n *\n * @param {Event} e The change event.\n */\n // eslint-disable-next-line no-unused-vars\n changeHandler(e) {\n // Components may override this method to do something.\n }\n}\n"],"names":["constructor","component","this","componentSelector","toggle","instance","input","clearSearch","dropdown","dropdownSelector","resultitems","viewall","combobox","document","querySelector","selectors","dataset","setSearchTerms","searchInput","_this$searchInput","value","registerClickHandlers","registerInputHandlers","registerChangeHandlers","getSearchTerm","clearSearchButton","classList","remove","fetchDataset","Error","name","filterDataset","filterMatchDataset","renderDropdown","triggerSelector","window","console","warning","datasetSize","length","getDatasetSize","getMatchedResults","matchedResults","setMatchedResults","result","searchTerm","getPreppedSearchTerm","preppedSearchTerm","toLowerCase","getHTMLElements","updateNodes","searchDropdown","currentViewAll","trigger","closeSearch","clear","toggleDropdown","add","searchResultsVisible","offsetParent","getOrCreateInstance","show","hide","resultNodes","querySelectorAll","currentNode","find","r","id","activeElement","addEventListener","clickHandler","bind","inputElement","changeHandler","async","warn","pendingPromise","Pending","filterrenderpipe","renderAndShow","resolve","pending","getDataset","e","target","closest","focus","removeAttribute","contains"],"mappings":"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"} \ No newline at end of file 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/local/action_menu/subpanel.min.js b/lib/amd/build/local/action_menu/subpanel.min.js index bd054ad014a0e..2ef8659b5e387 100644 --- a/lib/amd/build/local/action_menu/subpanel.min.js +++ b/lib/amd/build/local/action_menu/subpanel.min.js @@ -1,10 +1,10 @@ -define("core/local/action_menu/subpanel",["exports","jquery","core/utils","core/pagehelpers","core/pending","core/aria"],(function(_exports,_jquery,_utils,_pagehelpers,_pending,_aria){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("core/local/action_menu/subpanel",["exports","core/utils","core/pagehelpers","core/pending","core/aria"],(function(_exports,_utils,_pagehelpers,_pending,_aria){var obj; /** * Action menu subpanel JS controls. * * @module core/local/action_menu/subpanel * @copyright 2023 Mikel Martín * @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);const Selectors_mainMenu='[role="menu"]',Selectors_dropdownRight=".dropdown-menu-end",Selectors_subPanel=".dropdown-subpanel",Selectors_subPanelMenuItem=".dropdown-subpanel > .dropdown-item",Selectors_subPanelContent=".dropdown-subpanel > .dropdown-menu",Selectors_drawer='[data-region="fixed-drawer"]',Selectors_blockColumn=".blockcolumn",Selectors_columnLeft=".columnleft",Classes_dropRight="dropend",Classes_dropLeft="dropstart",Classes_dropDown="dropdown",Classes_forceLeft="downleft",Classes_contentDisplayed="content-displayed",BootstrapEvents_hideDropdown="hidden.bs.dropdown";let initialized=!1;const updateAllPanelsPosition=()=>{document.querySelectorAll(Selectors_subPanel).forEach((dropdown=>{new SubPanel(dropdown).updatePosition()}))};class SubPanel{constructor(element){this.element=element,this.menuItem=element.querySelector(Selectors_subPanelMenuItem),this.panelContent=element.querySelector(Selectors_subPanelContent),this.showPreviewOnFocus=!0}init(){this.element.dataset.subPanelInitialized||(this.updatePosition(),this.element.addEventListener("focusin",this._mainElementFocusInHandler.bind(this)),this.menuItem.addEventListener("click",this._menuItemClickHandler.bind(this)),this.menuItem.addEventListener("keydown",this._menuItemKeyHandler.bind(this)),(0,_pagehelpers.isBehatSite)()||(this.menuItem.addEventListener("mouseover",this._menuItemHoverHandler.bind(this)),this.menuItem.addEventListener("mouseout",this._menuItemHoverOutHandler.bind(this))),this.panelContent.addEventListener("keydown",this._panelContentKeyHandler.bind(this)),this.element.dataset.subPanelInitialized=!0)}_needSmallSpaceBehaviour(){return(0,_pagehelpers.isExtraSmall)()||null!==this.element.closest(Selectors_drawer)||null!==this.element.closest(Selectors_blockColumn)}_needDropdownRight(){return null===this.element.closest(Selectors_columnLeft)&&null!==this.element.closest(Selectors_dropdownRight)}_mainElementFocusInHandler(){!this._needSmallSpaceBehaviour()&&this.showPreviewOnFocus?this.setVisibility(!0):this.showPreviewOnFocus=!0}_menuItemClickHandler(event){event.stopPropagation(),event.preventDefault(),this._needSmallSpaceBehaviour()&&this.setVisibility(!this.getVisibility())}_menuItemHoverHandler(){this._needSmallSpaceBehaviour()||this.setVisibility(!0)}_menuItemHoverOutHandler(){this._needSmallSpaceBehaviour()||this._hideOtherSubPanels()}_menuItemKeyHandler(event){if("ArrowUp"===event.key||"ArrowDown"===event.key&&!this._needSmallSpaceBehaviour())return void this.setVisibility(!1);let focusPanel=!1;("ArrowRight"===event.key||"ArrowLeft"===event.key||"Tab"===event.key&&!event.shiftKey)&&(focusPanel=!0),"Enter"!==event.key&&" "!==event.key||(focusPanel=!0),"ArrowDown"===event.key&&this._needSmallSpaceBehaviour()&&this.getVisibility()&&(focusPanel=!0),focusPanel&&(event.stopPropagation(),event.preventDefault(),this.setVisibility(!0),this._focusPanelContent())}_panelContentKeyHandler(event){const canLoop=!this._needSmallSpaceBehaviour();let isBrowsingSubPanel=!1,newFocus=null;"ArrowRight"!==event.key&&"ArrowLeft"!==event.key||(newFocus=this.menuItem),("Escape"===event.key||"Tab"===event.key&&event.shiftKey)&&(newFocus=this.menuItem,this.setVisibility(!1),this.showPreviewOnFocus=!1),"ArrowUp"===event.key&&(newFocus=(0,_pagehelpers.previousFocusableElement)(this.panelContent,canLoop),isBrowsingSubPanel=!0),"ArrowDown"===event.key&&(newFocus=(0,_pagehelpers.nextFocusableElement)(this.panelContent,canLoop),isBrowsingSubPanel=!0),"Home"===event.key&&(newFocus=(0,_pagehelpers.firstFocusableElement)(this.panelContent),isBrowsingSubPanel=!0),"End"===event.key&&(newFocus=(0,_pagehelpers.lastFocusableElement)(this.panelContent),isBrowsingSubPanel=!0),null===newFocus&&isBrowsingSubPanel&&!canLoop&&(newFocus=this.menuItem),null!==newFocus&&(event.stopPropagation(),event.preventDefault(),newFocus.focus())}_focusPanelContent(){const pendingPromise=new _pending.default("core/action_menu/subpanel:focuscontent");setTimeout((()=>{const firstFocusable=(0,_pagehelpers.firstFocusableElement)(this.panelContent);firstFocusable&&firstFocusable.focus(),pendingPromise.resolve()}),100)}setVisibility(visible){visible&&this._hideOtherSubPanels(),!visible&&this.getVisibility&&(0,_aria.hide)(this.panelContent),visible&&!this.getVisibility&&(0,_aria.unhide)(this.panelContent),this.menuItem.setAttribute("aria-expanded",visible?"true":"false"),this.panelContent.classList.toggle("show",visible),this.element.classList.toggle(Classes_contentDisplayed,visible)}_hideOtherSubPanels(){this.element.closest(Selectors_mainMenu).querySelectorAll("".concat(Selectors_subPanelContent,".show")).forEach((visibleSubPanel=>{const dropdownSubPanel=visibleSubPanel.closest(Selectors_subPanel);if(dropdownSubPanel===this.element)return;new SubPanel(dropdownSubPanel).setVisibility(!1)}))}getVisibility(){return"true"===this.menuItem.getAttribute("aria-expanded")}updatePosition(){const dropdownRight=this._needDropdownRight();if(this._needSmallSpaceBehaviour())return this.element.classList.remove(Classes_dropRight),this.element.classList.remove(Classes_dropLeft),this.element.classList.add(Classes_dropDown),void this.element.classList.toggle(Classes_forceLeft,dropdownRight);this.element.classList.remove(Classes_dropDown),this.element.classList.remove(Classes_forceLeft),this.element.classList.toggle(Classes_dropRight,!dropdownRight),this.element.classList.toggle(Classes_dropLeft,dropdownRight)}}_exports.init=selector=>{initialized||((0,_jquery.default)(document).on(BootstrapEvents_hideDropdown,(()=>{document.querySelectorAll("".concat(Selectors_subPanelContent,".show")).forEach((visibleSubPanel=>{const dropdownSubPanel=visibleSubPanel.closest(Selectors_subPanel);new SubPanel(dropdownSubPanel).setVisibility(!1)}))})),window.addEventListener("resize",(0,_utils.debounce)(updateAllPanelsPosition,400)),initialized=!0);const subMenu=document.querySelector(selector);if(!subMenu)throw new Error("Sub panel element not found: ".concat(selector));new SubPanel(subMenu).init()}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=(obj=_pending)&&obj.__esModule?obj:{default:obj};const Selectors_mainMenu='[role="menu"]',Selectors_dropdownRight=".dropdown-menu-end",Selectors_subPanel=".dropdown-subpanel",Selectors_subPanelMenuItem=".dropdown-subpanel > .dropdown-item",Selectors_subPanelContent=".dropdown-subpanel > .dropdown-menu",Selectors_drawer='[data-region="fixed-drawer"]',Selectors_blockColumn=".blockcolumn",Selectors_columnLeft=".columnleft",Classes_dropRight="dropend",Classes_dropLeft="dropstart",Classes_dropDown="dropdown",Classes_forceLeft="downleft",Classes_contentDisplayed="content-displayed",BootstrapEvents_hideDropdown="hidden.bs.dropdown";let initialized=!1;const updateAllPanelsPosition=()=>{document.querySelectorAll(Selectors_subPanel).forEach((dropdown=>{new SubPanel(dropdown).updatePosition()}))};class SubPanel{constructor(element){this.element=element,this.menuItem=element.querySelector(Selectors_subPanelMenuItem),this.panelContent=element.querySelector(Selectors_subPanelContent),this.showPreviewOnFocus=!0}init(){this.element.dataset.subPanelInitialized||(this.updatePosition(),this.element.addEventListener("focusin",this._mainElementFocusInHandler.bind(this)),this.menuItem.addEventListener("click",this._menuItemClickHandler.bind(this)),this.menuItem.addEventListener("keydown",this._menuItemKeyHandler.bind(this)),(0,_pagehelpers.isBehatSite)()||(this.menuItem.addEventListener("mouseover",this._menuItemHoverHandler.bind(this)),this.menuItem.addEventListener("mouseout",this._menuItemHoverOutHandler.bind(this))),this.panelContent.addEventListener("keydown",this._panelContentKeyHandler.bind(this)),this.element.dataset.subPanelInitialized=!0)}_needSmallSpaceBehaviour(){return(0,_pagehelpers.isExtraSmall)()||null!==this.element.closest(Selectors_drawer)||null!==this.element.closest(Selectors_blockColumn)}_needDropdownRight(){return null===this.element.closest(Selectors_columnLeft)&&null!==this.element.closest(Selectors_dropdownRight)}_mainElementFocusInHandler(){!this._needSmallSpaceBehaviour()&&this.showPreviewOnFocus?this.setVisibility(!0):this.showPreviewOnFocus=!0}_menuItemClickHandler(event){event.stopPropagation(),event.preventDefault(),this._needSmallSpaceBehaviour()&&this.setVisibility(!this.getVisibility())}_menuItemHoverHandler(){this._needSmallSpaceBehaviour()||this.setVisibility(!0)}_menuItemHoverOutHandler(){this._needSmallSpaceBehaviour()||this._hideOtherSubPanels()}_menuItemKeyHandler(event){if("ArrowUp"===event.key||"ArrowDown"===event.key&&!this._needSmallSpaceBehaviour())return void this.setVisibility(!1);let focusPanel=!1;("ArrowRight"===event.key||"ArrowLeft"===event.key||"Tab"===event.key&&!event.shiftKey)&&(focusPanel=!0),"Enter"!==event.key&&" "!==event.key||(focusPanel=!0),"ArrowDown"===event.key&&this._needSmallSpaceBehaviour()&&this.getVisibility()&&(focusPanel=!0),focusPanel&&(event.stopPropagation(),event.preventDefault(),this.setVisibility(!0),this._focusPanelContent())}_panelContentKeyHandler(event){const canLoop=!this._needSmallSpaceBehaviour();let isBrowsingSubPanel=!1,newFocus=null;"ArrowRight"!==event.key&&"ArrowLeft"!==event.key||(newFocus=this.menuItem),("Escape"===event.key||"Tab"===event.key&&event.shiftKey)&&(newFocus=this.menuItem,this.setVisibility(!1),this.showPreviewOnFocus=!1),"ArrowUp"===event.key&&(newFocus=(0,_pagehelpers.previousFocusableElement)(this.panelContent,canLoop),isBrowsingSubPanel=!0),"ArrowDown"===event.key&&(newFocus=(0,_pagehelpers.nextFocusableElement)(this.panelContent,canLoop),isBrowsingSubPanel=!0),"Home"===event.key&&(newFocus=(0,_pagehelpers.firstFocusableElement)(this.panelContent),isBrowsingSubPanel=!0),"End"===event.key&&(newFocus=(0,_pagehelpers.lastFocusableElement)(this.panelContent),isBrowsingSubPanel=!0),null===newFocus&&isBrowsingSubPanel&&!canLoop&&(newFocus=this.menuItem),null!==newFocus&&(event.stopPropagation(),event.preventDefault(),newFocus.focus())}_focusPanelContent(){const pendingPromise=new _pending.default("core/action_menu/subpanel:focuscontent");setTimeout((()=>{const firstFocusable=(0,_pagehelpers.firstFocusableElement)(this.panelContent);firstFocusable&&firstFocusable.focus(),pendingPromise.resolve()}),100)}setVisibility(visible){visible&&this._hideOtherSubPanels(),!visible&&this.getVisibility&&(0,_aria.hide)(this.panelContent),visible&&!this.getVisibility&&(0,_aria.unhide)(this.panelContent),this.menuItem.setAttribute("aria-expanded",visible?"true":"false"),this.panelContent.classList.toggle("show",visible),this.element.classList.toggle(Classes_contentDisplayed,visible)}_hideOtherSubPanels(){this.element.closest(Selectors_mainMenu).querySelectorAll("".concat(Selectors_subPanelContent,".show")).forEach((visibleSubPanel=>{const dropdownSubPanel=visibleSubPanel.closest(Selectors_subPanel);if(dropdownSubPanel===this.element)return;new SubPanel(dropdownSubPanel).setVisibility(!1)}))}getVisibility(){return"true"===this.menuItem.getAttribute("aria-expanded")}updatePosition(){const dropdownRight=this._needDropdownRight();if(this._needSmallSpaceBehaviour())return this.element.classList.remove(Classes_dropRight),this.element.classList.remove(Classes_dropLeft),this.element.classList.add(Classes_dropDown),void this.element.classList.toggle(Classes_forceLeft,dropdownRight);this.element.classList.remove(Classes_dropDown),this.element.classList.remove(Classes_forceLeft),this.element.classList.toggle(Classes_dropRight,!dropdownRight),this.element.classList.toggle(Classes_dropLeft,dropdownRight)}}_exports.init=selector=>{initialized||(document.addEventListener(BootstrapEvents_hideDropdown,(()=>{document.querySelectorAll("".concat(Selectors_subPanelContent,".show")).forEach((visibleSubPanel=>{const dropdownSubPanel=visibleSubPanel.closest(Selectors_subPanel);new SubPanel(dropdownSubPanel).setVisibility(!1)}))})),window.addEventListener("resize",(0,_utils.debounce)(updateAllPanelsPosition,400)),initialized=!0);const subMenu=document.querySelector(selector);if(!subMenu)throw new Error("Sub panel element not found: ".concat(selector));new SubPanel(subMenu).init()}})); //# sourceMappingURL=subpanel.min.js.map \ No newline at end of file diff --git a/lib/amd/build/local/action_menu/subpanel.min.js.map b/lib/amd/build/local/action_menu/subpanel.min.js.map index 7de36d81f189a..55d2911812f05 100644 --- a/lib/amd/build/local/action_menu/subpanel.min.js.map +++ b/lib/amd/build/local/action_menu/subpanel.min.js.map @@ -1 +1 @@ -{"version":3,"file":"subpanel.min.js","sources":["../../../src/local/action_menu/subpanel.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 * Action menu subpanel JS controls.\n *\n * @module core/local/action_menu/subpanel\n * @copyright 2023 Mikel Martín \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport jQuery from 'jquery';\nimport {debounce} from 'core/utils';\nimport {\n isBehatSite,\n isExtraSmall,\n firstFocusableElement,\n lastFocusableElement,\n previousFocusableElement,\n nextFocusableElement,\n} from 'core/pagehelpers';\nimport Pending from 'core/pending';\nimport {\n hide,\n unhide,\n} from 'core/aria';\n\nconst Selectors = {\n mainMenu: '[role=\"menu\"]',\n dropdownRight: '.dropdown-menu-end',\n subPanel: '.dropdown-subpanel',\n subPanelMenuItem: '.dropdown-subpanel > .dropdown-item',\n subPanelContent: '.dropdown-subpanel > .dropdown-menu',\n // Drawer selector.\n drawer: '[data-region=\"fixed-drawer\"]',\n // Lateral blocks columns selectors.\n blockColumn: '.blockcolumn',\n columnLeft: '.columnleft',\n};\n\nconst Classes = {\n dropRight: 'dropend',\n dropLeft: 'dropstart',\n dropDown: 'dropdown',\n forceLeft: 'downleft',\n contentDisplayed: 'content-displayed',\n};\n\nconst BootstrapEvents = {\n hideDropdown: 'hidden.bs.dropdown',\n};\n\nlet initialized = false;\n\n/**\n * Initialize all delegated events into the page.\n */\nconst initPageEvents = () => {\n if (initialized) {\n return;\n }\n // Hide all subpanels when hidind a dropdown.\n // This is using JQuery because of BS4 events. JQuery won't be needed with BS5.\n jQuery(document).on(BootstrapEvents.hideDropdown, () => {\n document.querySelectorAll(`${Selectors.subPanelContent}.show`).forEach(visibleSubPanel => {\n const dropdownSubPanel = visibleSubPanel.closest(Selectors.subPanel);\n const subPanel = new SubPanel(dropdownSubPanel);\n subPanel.setVisibility(false);\n });\n });\n\n window.addEventListener('resize', debounce(updateAllPanelsPosition, 400));\n\n initialized = true;\n};\n\n/**\n * Update all the panels position.\n */\nconst updateAllPanelsPosition = () => {\n document.querySelectorAll(Selectors.subPanel).forEach(dropdown => {\n const subpanel = new SubPanel(dropdown);\n subpanel.updatePosition();\n });\n};\n\n/**\n * Subpanel class.\n * @private\n */\nclass SubPanel {\n /**\n * Constructor.\n * @param {HTMLElement} element The element to initialize.\n */\n constructor(element) {\n this.element = element;\n this.menuItem = element.querySelector(Selectors.subPanelMenuItem);\n this.panelContent = element.querySelector(Selectors.subPanelContent);\n /**\n * Enable preview when the menu item has focus.\n *\n * This is disabled when the user press ESC or shift+TAB to force closing\n *\n * @type {Boolean}\n * @private\n */\n this.showPreviewOnFocus = true;\n }\n\n /**\n * Initialize the subpanel element.\n *\n * This method adds the event listeners to the subpanel and the position classes.\n */\n init() {\n if (this.element.dataset.subPanelInitialized) {\n return;\n }\n\n this.updatePosition();\n\n // Full element events.\n this.element.addEventListener('focusin', this._mainElementFocusInHandler.bind(this));\n // Menu Item events.\n this.menuItem.addEventListener('click', this._menuItemClickHandler.bind(this));\n this.menuItem.addEventListener('keydown', this._menuItemKeyHandler.bind(this));\n if (!isBehatSite()) {\n // Behat in Chrome usually move the mouse over the page when trying clicking a subpanel element.\n // If the menu has more than one subpanel this could cause closing the subpanel by mistake.\n this.menuItem.addEventListener('mouseover', this._menuItemHoverHandler.bind(this));\n this.menuItem.addEventListener('mouseout', this._menuItemHoverOutHandler.bind(this));\n }\n // Subpanel content events.\n this.panelContent.addEventListener('keydown', this._panelContentKeyHandler.bind(this));\n\n this.element.dataset.subPanelInitialized = true;\n }\n\n /**\n * Checks if the subpanel has enough space.\n *\n * In general there are two scenarios were the subpanel must be interacted differently:\n * - Extra small screens: The subpanel is displayed below the menu item.\n * - Drawer: The subpanel is displayed one of the drawers.\n * - Block columns: for classic based themes.\n *\n * @returns {Boolean} true if the subpanel should be displayed in small screens.\n */\n _needSmallSpaceBehaviour() {\n return isExtraSmall() ||\n this.element.closest(Selectors.drawer) !== null ||\n this.element.closest(Selectors.blockColumn) !== null;\n }\n\n /**\n * Check if the subpanel should be displayed on the right.\n *\n * This is defined by the drop right boostrap class. However, if the menu is\n * displayed in a block column on the right, the subpanel should be forced\n * to the right.\n *\n * @returns {Boolean} true if the subpanel should be displayed on the right.\n */\n _needDropdownRight() {\n if (this.element.closest(Selectors.columnLeft) !== null) {\n return false;\n }\n return this.element.closest(Selectors.dropdownRight) !== null;\n }\n\n /**\n * Main element focus in handler.\n */\n _mainElementFocusInHandler() {\n if (this._needSmallSpaceBehaviour() || !this.showPreviewOnFocus) {\n // Preview is disabled when the user press ESC or shift+TAB to force closing\n // but if the continue navigating with keyboard the preview is enabled again.\n this.showPreviewOnFocus = true;\n return;\n }\n this.setVisibility(true);\n }\n\n /**\n * Menu item click handler.\n * @param {Event} event\n */\n _menuItemClickHandler(event) {\n // Avoid dropdowns being closed after clicking a subemnu.\n // This won't be needed with BS5 (data-bs-auto-close handles it).\n event.stopPropagation();\n event.preventDefault();\n if (this._needSmallSpaceBehaviour()) {\n this.setVisibility(!this.getVisibility());\n }\n }\n\n /**\n * Menu item hover handler.\n * @private\n */\n _menuItemHoverHandler() {\n if (this._needSmallSpaceBehaviour()) {\n return;\n }\n this.setVisibility(true);\n }\n\n /**\n * Menu item hover out handler.\n * @private\n */\n _menuItemHoverOutHandler() {\n if (this._needSmallSpaceBehaviour()) {\n return;\n }\n this._hideOtherSubPanels();\n }\n\n /**\n * Menu item key handler.\n * @param {Event} event\n * @private\n */\n _menuItemKeyHandler(event) {\n // In small sizes te down key will focus on the panel.\n if (event.key === 'ArrowUp' || (event.key === 'ArrowDown' && !this._needSmallSpaceBehaviour())) {\n this.setVisibility(false);\n return;\n }\n\n // Keys to move focus to the panel.\n let focusPanel = false;\n\n if (event.key === 'ArrowRight' || event.key === 'ArrowLeft' || (event.key === 'Tab' && !event.shiftKey)) {\n focusPanel = true;\n }\n if ((event.key === 'Enter' || event.key === ' ')) {\n focusPanel = true;\n }\n // In extra small screen the panel is shown below the item.\n if (event.key === 'ArrowDown' && this._needSmallSpaceBehaviour() && this.getVisibility()) {\n focusPanel = true;\n }\n if (focusPanel) {\n event.stopPropagation();\n event.preventDefault();\n this.setVisibility(true);\n this._focusPanelContent();\n }\n\n }\n\n /**\n * Sub panel content key handler.\n * @param {Event} event\n * @private\n */\n _panelContentKeyHandler(event) {\n // In extra small devices the panel is displayed under the menu item\n // so the arrow up/down switch between subpanel and the menu item.\n const canLoop = !this._needSmallSpaceBehaviour();\n let isBrowsingSubPanel = false;\n let newFocus = null;\n if (event.key === 'ArrowRight' || event.key === 'ArrowLeft') {\n newFocus = this.menuItem;\n }\n // Acording to WCAG Esc and Tab are similar to arrow navigation but they\n // force the subpanel to be closed.\n if (event.key === 'Escape' || (event.key === 'Tab' && event.shiftKey)) {\n newFocus = this.menuItem;\n this.setVisibility(false);\n this.showPreviewOnFocus = false;\n }\n if (event.key === 'ArrowUp') {\n newFocus = previousFocusableElement(this.panelContent, canLoop);\n isBrowsingSubPanel = true;\n }\n if (event.key === 'ArrowDown') {\n newFocus = nextFocusableElement(this.panelContent, canLoop);\n isBrowsingSubPanel = true;\n }\n if (event.key === 'Home') {\n newFocus = firstFocusableElement(this.panelContent);\n isBrowsingSubPanel = true;\n }\n if (event.key === 'End') {\n newFocus = lastFocusableElement(this.panelContent);\n isBrowsingSubPanel = true;\n }\n // If the user cannot loop and arrive to the start/end of the subpanel\n // we focus on the menu item.\n if (newFocus === null && isBrowsingSubPanel && !canLoop) {\n newFocus = this.menuItem;\n }\n if (newFocus !== null) {\n event.stopPropagation();\n event.preventDefault();\n newFocus.focus();\n }\n }\n\n /**\n * Focus on the first focusable element of the subpanel.\n * @private\n */\n _focusPanelContent() {\n const pendingPromise = new Pending('core/action_menu/subpanel:focuscontent');\n // Some Bootstrap events are triggered after the click event.\n // To prevent this from affecting the focus we wait a bit.\n setTimeout(() => {\n const firstFocusable = firstFocusableElement(this.panelContent);\n if (firstFocusable) {\n firstFocusable.focus();\n }\n pendingPromise.resolve();\n }, 100);\n }\n\n /**\n * Set the visibility of a subpanel.\n * @param {Boolean} visible true if the subpanel should be visible.\n */\n setVisibility(visible) {\n if (visible) {\n this._hideOtherSubPanels();\n }\n // Aria hidden/unhidden can alter the focus, we only want to do it when needed.\n if (!visible && this.getVisibility) {\n hide(this.panelContent);\n }\n if (visible && !this.getVisibility) {\n unhide(this.panelContent);\n }\n this.menuItem.setAttribute('aria-expanded', visible ? 'true' : 'false');\n this.panelContent.classList.toggle('show', visible);\n this.element.classList.toggle(Classes.contentDisplayed, visible);\n }\n\n /**\n * Hide all other subpanels in the parent menu.\n * @private\n */\n _hideOtherSubPanels() {\n const dropdown = this.element.closest(Selectors.mainMenu);\n dropdown.querySelectorAll(`${Selectors.subPanelContent}.show`).forEach(visibleSubPanel => {\n const dropdownSubPanel = visibleSubPanel.closest(Selectors.subPanel);\n if (dropdownSubPanel === this.element) {\n return;\n }\n const subPanel = new SubPanel(dropdownSubPanel);\n subPanel.setVisibility(false);\n });\n }\n\n /**\n * Get the visibility of a subpanel.\n * @returns {Boolean} true if the subpanel is visible.\n */\n getVisibility() {\n return this.menuItem.getAttribute('aria-expanded') === 'true';\n }\n\n /**\n * Update the panels position depending on the screen size and panel position.\n */\n updatePosition() {\n const dropdownRight = this._needDropdownRight();\n if (this._needSmallSpaceBehaviour()) {\n this.element.classList.remove(Classes.dropRight);\n this.element.classList.remove(Classes.dropLeft);\n this.element.classList.add(Classes.dropDown);\n this.element.classList.toggle(Classes.forceLeft, dropdownRight);\n return;\n }\n this.element.classList.remove(Classes.dropDown);\n this.element.classList.remove(Classes.forceLeft);\n this.element.classList.toggle(Classes.dropRight, !dropdownRight);\n this.element.classList.toggle(Classes.dropLeft, dropdownRight);\n }\n}\n\n/**\n * Initialise module for given report\n *\n * @method\n * @param {string} selector The query selector to init.\n */\nexport const init = (selector) => {\n initPageEvents();\n const subMenu = document.querySelector(selector);\n if (!subMenu) {\n throw new Error(`Sub panel element not found: ${selector}`);\n }\n const subPanel = new SubPanel(subMenu);\n subPanel.init();\n};\n"],"names":["Selectors","Classes","BootstrapEvents","initialized","updateAllPanelsPosition","document","querySelectorAll","forEach","dropdown","SubPanel","updatePosition","constructor","element","menuItem","querySelector","panelContent","showPreviewOnFocus","init","this","dataset","subPanelInitialized","addEventListener","_mainElementFocusInHandler","bind","_menuItemClickHandler","_menuItemKeyHandler","_menuItemHoverHandler","_menuItemHoverOutHandler","_panelContentKeyHandler","_needSmallSpaceBehaviour","closest","_needDropdownRight","setVisibility","event","stopPropagation","preventDefault","getVisibility","_hideOtherSubPanels","key","focusPanel","shiftKey","_focusPanelContent","canLoop","isBrowsingSubPanel","newFocus","focus","pendingPromise","Pending","setTimeout","firstFocusable","resolve","visible","setAttribute","classList","toggle","visibleSubPanel","dropdownSubPanel","getAttribute","dropdownRight","remove","add","selector","on","window","subMenu","Error"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"subpanel.min.js","sources":["../../../src/local/action_menu/subpanel.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 * Action menu subpanel JS controls.\n *\n * @module core/local/action_menu/subpanel\n * @copyright 2023 Mikel Martín \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {debounce} from 'core/utils';\nimport {\n isBehatSite,\n isExtraSmall,\n firstFocusableElement,\n lastFocusableElement,\n previousFocusableElement,\n nextFocusableElement,\n} from 'core/pagehelpers';\nimport Pending from 'core/pending';\nimport {\n hide,\n unhide,\n} from 'core/aria';\n\nconst Selectors = {\n mainMenu: '[role=\"menu\"]',\n dropdownRight: '.dropdown-menu-end',\n subPanel: '.dropdown-subpanel',\n subPanelMenuItem: '.dropdown-subpanel > .dropdown-item',\n subPanelContent: '.dropdown-subpanel > .dropdown-menu',\n // Drawer selector.\n drawer: '[data-region=\"fixed-drawer\"]',\n // Lateral blocks columns selectors.\n blockColumn: '.blockcolumn',\n columnLeft: '.columnleft',\n};\n\nconst Classes = {\n dropRight: 'dropend',\n dropLeft: 'dropstart',\n dropDown: 'dropdown',\n forceLeft: 'downleft',\n contentDisplayed: 'content-displayed',\n};\n\nconst BootstrapEvents = {\n hideDropdown: 'hidden.bs.dropdown',\n};\n\nlet initialized = false;\n\n/**\n * Initialize all delegated events into the page.\n */\nconst initPageEvents = () => {\n if (initialized) {\n return;\n }\n // Hide all subpanels when hiding a dropdown.\n document.addEventListener(BootstrapEvents.hideDropdown, () => {\n document.querySelectorAll(`${Selectors.subPanelContent}.show`).forEach(visibleSubPanel => {\n const dropdownSubPanel = visibleSubPanel.closest(Selectors.subPanel);\n const subPanel = new SubPanel(dropdownSubPanel);\n subPanel.setVisibility(false);\n });\n });\n\n window.addEventListener('resize', debounce(updateAllPanelsPosition, 400));\n\n initialized = true;\n};\n\n/**\n * Update all the panels position.\n */\nconst updateAllPanelsPosition = () => {\n document.querySelectorAll(Selectors.subPanel).forEach(dropdown => {\n const subpanel = new SubPanel(dropdown);\n subpanel.updatePosition();\n });\n};\n\n/**\n * Subpanel class.\n * @private\n */\nclass SubPanel {\n /**\n * Constructor.\n * @param {HTMLElement} element The element to initialize.\n */\n constructor(element) {\n this.element = element;\n this.menuItem = element.querySelector(Selectors.subPanelMenuItem);\n this.panelContent = element.querySelector(Selectors.subPanelContent);\n /**\n * Enable preview when the menu item has focus.\n *\n * This is disabled when the user press ESC or shift+TAB to force closing\n *\n * @type {Boolean}\n * @private\n */\n this.showPreviewOnFocus = true;\n }\n\n /**\n * Initialize the subpanel element.\n *\n * This method adds the event listeners to the subpanel and the position classes.\n */\n init() {\n if (this.element.dataset.subPanelInitialized) {\n return;\n }\n\n this.updatePosition();\n\n // Full element events.\n this.element.addEventListener('focusin', this._mainElementFocusInHandler.bind(this));\n // Menu Item events.\n this.menuItem.addEventListener('click', this._menuItemClickHandler.bind(this));\n this.menuItem.addEventListener('keydown', this._menuItemKeyHandler.bind(this));\n if (!isBehatSite()) {\n // Behat in Chrome usually move the mouse over the page when trying clicking a subpanel element.\n // If the menu has more than one subpanel this could cause closing the subpanel by mistake.\n this.menuItem.addEventListener('mouseover', this._menuItemHoverHandler.bind(this));\n this.menuItem.addEventListener('mouseout', this._menuItemHoverOutHandler.bind(this));\n }\n // Subpanel content events.\n this.panelContent.addEventListener('keydown', this._panelContentKeyHandler.bind(this));\n\n this.element.dataset.subPanelInitialized = true;\n }\n\n /**\n * Checks if the subpanel has enough space.\n *\n * In general there are two scenarios were the subpanel must be interacted differently:\n * - Extra small screens: The subpanel is displayed below the menu item.\n * - Drawer: The subpanel is displayed one of the drawers.\n * - Block columns: for classic based themes.\n *\n * @returns {Boolean} true if the subpanel should be displayed in small screens.\n */\n _needSmallSpaceBehaviour() {\n return isExtraSmall() ||\n this.element.closest(Selectors.drawer) !== null ||\n this.element.closest(Selectors.blockColumn) !== null;\n }\n\n /**\n * Check if the subpanel should be displayed on the right.\n *\n * This is defined by the drop right boostrap class. However, if the menu is\n * displayed in a block column on the right, the subpanel should be forced\n * to the right.\n *\n * @returns {Boolean} true if the subpanel should be displayed on the right.\n */\n _needDropdownRight() {\n if (this.element.closest(Selectors.columnLeft) !== null) {\n return false;\n }\n return this.element.closest(Selectors.dropdownRight) !== null;\n }\n\n /**\n * Main element focus in handler.\n */\n _mainElementFocusInHandler() {\n if (this._needSmallSpaceBehaviour() || !this.showPreviewOnFocus) {\n // Preview is disabled when the user press ESC or shift+TAB to force closing\n // but if the continue navigating with keyboard the preview is enabled again.\n this.showPreviewOnFocus = true;\n return;\n }\n this.setVisibility(true);\n }\n\n /**\n * Menu item click handler.\n * @param {Event} event\n */\n _menuItemClickHandler(event) {\n // Avoid dropdowns being closed after clicking a subemnu.\n // This won't be needed with BS5 (data-bs-auto-close handles it).\n event.stopPropagation();\n event.preventDefault();\n if (this._needSmallSpaceBehaviour()) {\n this.setVisibility(!this.getVisibility());\n }\n }\n\n /**\n * Menu item hover handler.\n * @private\n */\n _menuItemHoverHandler() {\n if (this._needSmallSpaceBehaviour()) {\n return;\n }\n this.setVisibility(true);\n }\n\n /**\n * Menu item hover out handler.\n * @private\n */\n _menuItemHoverOutHandler() {\n if (this._needSmallSpaceBehaviour()) {\n return;\n }\n this._hideOtherSubPanels();\n }\n\n /**\n * Menu item key handler.\n * @param {Event} event\n * @private\n */\n _menuItemKeyHandler(event) {\n // In small sizes te down key will focus on the panel.\n if (event.key === 'ArrowUp' || (event.key === 'ArrowDown' && !this._needSmallSpaceBehaviour())) {\n this.setVisibility(false);\n return;\n }\n\n // Keys to move focus to the panel.\n let focusPanel = false;\n\n if (event.key === 'ArrowRight' || event.key === 'ArrowLeft' || (event.key === 'Tab' && !event.shiftKey)) {\n focusPanel = true;\n }\n if ((event.key === 'Enter' || event.key === ' ')) {\n focusPanel = true;\n }\n // In extra small screen the panel is shown below the item.\n if (event.key === 'ArrowDown' && this._needSmallSpaceBehaviour() && this.getVisibility()) {\n focusPanel = true;\n }\n if (focusPanel) {\n event.stopPropagation();\n event.preventDefault();\n this.setVisibility(true);\n this._focusPanelContent();\n }\n\n }\n\n /**\n * Sub panel content key handler.\n * @param {Event} event\n * @private\n */\n _panelContentKeyHandler(event) {\n // In extra small devices the panel is displayed under the menu item\n // so the arrow up/down switch between subpanel and the menu item.\n const canLoop = !this._needSmallSpaceBehaviour();\n let isBrowsingSubPanel = false;\n let newFocus = null;\n if (event.key === 'ArrowRight' || event.key === 'ArrowLeft') {\n newFocus = this.menuItem;\n }\n // Acording to WCAG Esc and Tab are similar to arrow navigation but they\n // force the subpanel to be closed.\n if (event.key === 'Escape' || (event.key === 'Tab' && event.shiftKey)) {\n newFocus = this.menuItem;\n this.setVisibility(false);\n this.showPreviewOnFocus = false;\n }\n if (event.key === 'ArrowUp') {\n newFocus = previousFocusableElement(this.panelContent, canLoop);\n isBrowsingSubPanel = true;\n }\n if (event.key === 'ArrowDown') {\n newFocus = nextFocusableElement(this.panelContent, canLoop);\n isBrowsingSubPanel = true;\n }\n if (event.key === 'Home') {\n newFocus = firstFocusableElement(this.panelContent);\n isBrowsingSubPanel = true;\n }\n if (event.key === 'End') {\n newFocus = lastFocusableElement(this.panelContent);\n isBrowsingSubPanel = true;\n }\n // If the user cannot loop and arrive to the start/end of the subpanel\n // we focus on the menu item.\n if (newFocus === null && isBrowsingSubPanel && !canLoop) {\n newFocus = this.menuItem;\n }\n if (newFocus !== null) {\n event.stopPropagation();\n event.preventDefault();\n newFocus.focus();\n }\n }\n\n /**\n * Focus on the first focusable element of the subpanel.\n * @private\n */\n _focusPanelContent() {\n const pendingPromise = new Pending('core/action_menu/subpanel:focuscontent');\n // Some Bootstrap events are triggered after the click event.\n // To prevent this from affecting the focus we wait a bit.\n setTimeout(() => {\n const firstFocusable = firstFocusableElement(this.panelContent);\n if (firstFocusable) {\n firstFocusable.focus();\n }\n pendingPromise.resolve();\n }, 100);\n }\n\n /**\n * Set the visibility of a subpanel.\n * @param {Boolean} visible true if the subpanel should be visible.\n */\n setVisibility(visible) {\n if (visible) {\n this._hideOtherSubPanels();\n }\n // Aria hidden/unhidden can alter the focus, we only want to do it when needed.\n if (!visible && this.getVisibility) {\n hide(this.panelContent);\n }\n if (visible && !this.getVisibility) {\n unhide(this.panelContent);\n }\n this.menuItem.setAttribute('aria-expanded', visible ? 'true' : 'false');\n this.panelContent.classList.toggle('show', visible);\n this.element.classList.toggle(Classes.contentDisplayed, visible);\n }\n\n /**\n * Hide all other subpanels in the parent menu.\n * @private\n */\n _hideOtherSubPanels() {\n const dropdown = this.element.closest(Selectors.mainMenu);\n dropdown.querySelectorAll(`${Selectors.subPanelContent}.show`).forEach(visibleSubPanel => {\n const dropdownSubPanel = visibleSubPanel.closest(Selectors.subPanel);\n if (dropdownSubPanel === this.element) {\n return;\n }\n const subPanel = new SubPanel(dropdownSubPanel);\n subPanel.setVisibility(false);\n });\n }\n\n /**\n * Get the visibility of a subpanel.\n * @returns {Boolean} true if the subpanel is visible.\n */\n getVisibility() {\n return this.menuItem.getAttribute('aria-expanded') === 'true';\n }\n\n /**\n * Update the panels position depending on the screen size and panel position.\n */\n updatePosition() {\n const dropdownRight = this._needDropdownRight();\n if (this._needSmallSpaceBehaviour()) {\n this.element.classList.remove(Classes.dropRight);\n this.element.classList.remove(Classes.dropLeft);\n this.element.classList.add(Classes.dropDown);\n this.element.classList.toggle(Classes.forceLeft, dropdownRight);\n return;\n }\n this.element.classList.remove(Classes.dropDown);\n this.element.classList.remove(Classes.forceLeft);\n this.element.classList.toggle(Classes.dropRight, !dropdownRight);\n this.element.classList.toggle(Classes.dropLeft, dropdownRight);\n }\n}\n\n/**\n * Initialise module for given report\n *\n * @method\n * @param {string} selector The query selector to init.\n */\nexport const init = (selector) => {\n initPageEvents();\n const subMenu = document.querySelector(selector);\n if (!subMenu) {\n throw new Error(`Sub panel element not found: ${selector}`);\n }\n const subPanel = new SubPanel(subMenu);\n subPanel.init();\n};\n"],"names":["Selectors","Classes","BootstrapEvents","initialized","updateAllPanelsPosition","document","querySelectorAll","forEach","dropdown","SubPanel","updatePosition","constructor","element","menuItem","querySelector","panelContent","showPreviewOnFocus","init","this","dataset","subPanelInitialized","addEventListener","_mainElementFocusInHandler","bind","_menuItemClickHandler","_menuItemKeyHandler","_menuItemHoverHandler","_menuItemHoverOutHandler","_panelContentKeyHandler","_needSmallSpaceBehaviour","closest","_needDropdownRight","setVisibility","event","stopPropagation","preventDefault","getVisibility","_hideOtherSubPanels","key","focusPanel","shiftKey","_focusPanelContent","canLoop","isBrowsingSubPanel","newFocus","focus","pendingPromise","Pending","setTimeout","firstFocusable","resolve","visible","setAttribute","classList","toggle","visibleSubPanel","dropdownSubPanel","getAttribute","dropdownRight","remove","add","selector","window","subMenu","Error"],"mappings":"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"} \ No newline at end of file diff --git a/lib/amd/build/local/dropdown/dialog.min.js b/lib/amd/build/local/dropdown/dialog.min.js index 3b3b2542fd4fd..281a0829628fb 100644 --- a/lib/amd/build/local/dropdown/dialog.min.js +++ b/lib/amd/build/local/dropdown/dialog.min.js @@ -1,10 +1,10 @@ -define("core/local/dropdown/dialog",["exports","jquery","core/pagehelpers","core/pending"],(function(_exports,_jquery,_pagehelpers,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("core/local/dropdown/dialog",["exports","theme_boost/bootstrap/dropdown","core/pagehelpers","core/pending"],(function(_exports,_dropdown,_pagehelpers,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Dropdown status JS controls. * * @module core/local/dropdown/dialog * @copyright 2023 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=_exports.getDropdownDialog=_exports.DropdownDialog=void 0,_jquery=_interopRequireDefault(_jquery),_pending=_interopRequireDefault(_pending);const Selectors_dropdownButton='[data-for="dropdowndialog_button"]',Selectors_dropdownDialog='[data-for="dropdowndialog_dialog"]';class DropdownDialog{constructor(element){this.element=element,this.button=element.querySelector(Selectors_dropdownButton),this.panel=element.querySelector(Selectors_dropdownDialog)}init(){this.element.dataset.dropdownDialogInitialized||(this.button.addEventListener("keydown",this._buttonKeyHandler.bind(this)),this.panel.addEventListener("keydown",this._contentKeyHandler.bind(this)),this.element.dataset.dropdownDialogInitialized=!0)}_buttonKeyHandler(event){if("ArrowUp"===event.key||"ArrowLeft"===event.key)return event.stopPropagation(),event.preventDefault(),void this.setVisible(!1);"ArrowDown"!==event.key&&"ArrowRight"!==event.key||(event.stopPropagation(),event.preventDefault(),this.setVisible(!0),this._focusPanelContent())}_contentKeyHandler(event){let newFocus=null;"End"===event.key&&(newFocus=(0,_pagehelpers.lastFocusableElement)(this.panel)),"Home"===event.key&&(newFocus=(0,_pagehelpers.firstFocusableElement)(this.panel)),"ArrowUp"!==event.key&&"ArrowLeft"!==event.key||(newFocus=(0,_pagehelpers.previousFocusableElement)(this.panel,!1),newFocus||(newFocus=this.button)),"ArrowDown"!==event.key&&"ArrowRight"!==event.key||(newFocus=(0,_pagehelpers.nextFocusableElement)(this.panel,!1)),null!==newFocus&&(event.stopPropagation(),event.preventDefault(),newFocus.focus())}_focusPanelContent(){const pendingPromise=new _pending.default("core/dropdown/dialog:focuscontent");setTimeout((()=>{const firstFocusable=(0,_pagehelpers.firstFocusableElement)(this.panel);firstFocusable&&firstFocusable.focus(),pendingPromise.resolve()}),100)}setVisible(visible){visible!==this.isVisible()&&(0,_jquery.default)(this.button).dropdown("toggle")}isVisible(){return"true"===this.button.getAttribute("aria-expanded")}setButtonContent(content){this.button.innerHTML=content}setButtonDisabled(disabled){disabled?this.button.setAttribute("disabled","disabled"):this.button.removeAttribute("disabled")}getElement(){return this.element}}_exports.DropdownDialog=DropdownDialog;const getDropdownDialog=selector=>{const dropdownElement=document.querySelector(selector);return dropdownElement?new DropdownDialog(dropdownElement):null};_exports.getDropdownDialog=getDropdownDialog;_exports.init=selector=>{const dropdown=getDropdownDialog(selector);if(!dropdown)throw new Error("Dopdown dialog element not found: ".concat(selector));dropdown.init()}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=_exports.getDropdownDialog=_exports.DropdownDialog=void 0,_dropdown=_interopRequireDefault(_dropdown),_pending=_interopRequireDefault(_pending);const Selectors_dropdownButton='[data-for="dropdowndialog_button"]',Selectors_dropdownDialog='[data-for="dropdowndialog_dialog"]';class DropdownDialog{constructor(element){this.element=element,this.button=element.querySelector(Selectors_dropdownButton),this.panel=element.querySelector(Selectors_dropdownDialog)}init(){this.element.dataset.dropdownDialogInitialized||(this.button.addEventListener("keydown",this._buttonKeyHandler.bind(this)),this.panel.addEventListener("keydown",this._contentKeyHandler.bind(this)),this.element.dataset.dropdownDialogInitialized=!0)}_buttonKeyHandler(event){if("ArrowUp"===event.key||"ArrowLeft"===event.key)return event.stopPropagation(),event.preventDefault(),void this.setVisible(!1);"ArrowDown"!==event.key&&"ArrowRight"!==event.key||(event.stopPropagation(),event.preventDefault(),this.setVisible(!0),this._focusPanelContent())}_contentKeyHandler(event){let newFocus=null;"End"===event.key&&(newFocus=(0,_pagehelpers.lastFocusableElement)(this.panel)),"Home"===event.key&&(newFocus=(0,_pagehelpers.firstFocusableElement)(this.panel)),"ArrowUp"!==event.key&&"ArrowLeft"!==event.key||(newFocus=(0,_pagehelpers.previousFocusableElement)(this.panel,!1),newFocus||(newFocus=this.button)),"ArrowDown"!==event.key&&"ArrowRight"!==event.key||(newFocus=(0,_pagehelpers.nextFocusableElement)(this.panel,!1)),null!==newFocus&&(event.stopPropagation(),event.preventDefault(),newFocus.focus())}_focusPanelContent(){const pendingPromise=new _pending.default("core/dropdown/dialog:focuscontent");setTimeout((()=>{const firstFocusable=(0,_pagehelpers.firstFocusableElement)(this.panel);firstFocusable&&firstFocusable.focus(),pendingPromise.resolve()}),100)}setVisible(visible){visible!==this.isVisible()&&_dropdown.default.getOrCreateInstance(this.button).toggle()}isVisible(){return"true"===this.button.getAttribute("aria-expanded")}setButtonContent(content){this.button.innerHTML=content}setButtonDisabled(disabled){disabled?this.button.setAttribute("disabled","disabled"):this.button.removeAttribute("disabled")}getElement(){return this.element}}_exports.DropdownDialog=DropdownDialog;const getDropdownDialog=selector=>{const dropdownElement=document.querySelector(selector);return dropdownElement?new DropdownDialog(dropdownElement):null};_exports.getDropdownDialog=getDropdownDialog;_exports.init=selector=>{const dropdown=getDropdownDialog(selector);if(!dropdown)throw new Error("Dopdown dialog element not found: ".concat(selector));dropdown.init()}})); //# sourceMappingURL=dialog.min.js.map \ No newline at end of file diff --git a/lib/amd/build/local/dropdown/dialog.min.js.map b/lib/amd/build/local/dropdown/dialog.min.js.map index 2174c517b7f59..a7f7705689d6c 100644 --- a/lib/amd/build/local/dropdown/dialog.min.js.map +++ b/lib/amd/build/local/dropdown/dialog.min.js.map @@ -1 +1 @@ -{"version":3,"file":"dialog.min.js","sources":["../../../src/local/dropdown/dialog.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 * Dropdown status JS controls.\n *\n * @module core/local/dropdown/dialog\n * @copyright 2023 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n// The jQuery module is only used for interacting with Bootstrap 4. It can be removed when MDL-71979 is integrated.\nimport jQuery from 'jquery';\nimport {\n firstFocusableElement,\n lastFocusableElement,\n previousFocusableElement,\n nextFocusableElement,\n} from 'core/pagehelpers';\nimport Pending from 'core/pending';\n\nconst Selectors = {\n dropdownButton: '[data-for=\"dropdowndialog_button\"]',\n dropdownDialog: '[data-for=\"dropdowndialog_dialog\"]',\n};\n\n/**\n * Dropdown dialog class.\n * @private\n */\nexport class DropdownDialog {\n /**\n * Constructor.\n * @param {HTMLElement} element The element to initialize.\n */\n constructor(element) {\n this.element = element;\n this.button = element.querySelector(Selectors.dropdownButton);\n this.panel = element.querySelector(Selectors.dropdownDialog);\n }\n\n /**\n * Initialize the subpanel element.\n *\n * This method adds the event listeners to the subpanel and the position classes.\n */\n init() {\n if (this.element.dataset.dropdownDialogInitialized) {\n return;\n }\n\n // Menu Item events.\n this.button.addEventListener('keydown', this._buttonKeyHandler.bind(this));\n // Subpanel content events.\n this.panel.addEventListener('keydown', this._contentKeyHandler.bind(this));\n\n this.element.dataset.dropdownDialogInitialized = true;\n }\n\n /**\n * Dropdown button key handler.\n * @param {Event} event\n * @private\n */\n _buttonKeyHandler(event) {\n if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') {\n event.stopPropagation();\n event.preventDefault();\n this.setVisible(false);\n return;\n }\n\n if (event.key === 'ArrowDown' || event.key === 'ArrowRight') {\n event.stopPropagation();\n event.preventDefault();\n this.setVisible(true);\n this._focusPanelContent();\n }\n }\n\n /**\n * Sub panel content key handler.\n * @param {Event} event\n * @private\n */\n _contentKeyHandler(event) {\n let newFocus = null;\n\n if (event.key === 'End') {\n newFocus = lastFocusableElement(this.panel);\n }\n if (event.key === 'Home') {\n newFocus = firstFocusableElement(this.panel);\n }\n if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') {\n newFocus = previousFocusableElement(this.panel, false);\n if (!newFocus) {\n newFocus = this.button;\n }\n }\n if (event.key === 'ArrowDown' || event.key === 'ArrowRight') {\n newFocus = nextFocusableElement(this.panel, false);\n }\n if (newFocus !== null) {\n event.stopPropagation();\n event.preventDefault();\n newFocus.focus();\n }\n }\n\n /**\n * Focus on the first focusable element of the subpanel.\n * @private\n */\n _focusPanelContent() {\n const pendingPromise = new Pending('core/dropdown/dialog:focuscontent');\n // Some Bootstrap events are triggered after the click event.\n // To prevent this from affecting the focus we wait a bit.\n setTimeout(() => {\n const firstFocusable = firstFocusableElement(this.panel);\n if (firstFocusable) {\n firstFocusable.focus();\n }\n pendingPromise.resolve();\n }, 100);\n }\n\n /**\n * Set the visibility of a subpanel.\n * @param {Boolean} visible true if the subpanel should be visible.\n */\n setVisible(visible) {\n if (visible === this.isVisible()) {\n return;\n }\n // All jQuery in this code can be replaced when MDL-71979 is integrated.\n jQuery(this.button).dropdown('toggle');\n }\n\n /**\n * Get the visibility of a subpanel.\n * @returns {Boolean} true if the subpanel is visible.\n */\n isVisible() {\n return this.button.getAttribute('aria-expanded') === 'true';\n }\n\n /**\n * Set the content of the button.\n * @param {String} content\n */\n setButtonContent(content) {\n this.button.innerHTML = content;\n }\n\n /**\n * Set the disabled state of the button.\n * @param {Boolean} disabled\n */\n setButtonDisabled(disabled) {\n if (disabled) {\n this.button.setAttribute('disabled', 'disabled');\n } else {\n this.button.removeAttribute('disabled');\n }\n }\n\n /**\n * Return the main dropdown HTML element.\n * @returns {HTMLElement} The element.\n */\n getElement() {\n return this.element;\n }\n}\n\n/**\n * Get the dropdown dialog instance from a selector.\n * @param {string} selector The query selector to init.\n * @returns {DropdownDialog|null} The dropdown dialog instance if any.\n */\nexport const getDropdownDialog = (selector) => {\n const dropdownElement = document.querySelector(selector);\n if (!dropdownElement) {\n return null;\n }\n return new DropdownDialog(dropdownElement);\n};\n\n/**\n * Initialize module.\n *\n * @method\n * @param {string} selector The query selector to init.\n */\nexport const init = (selector) => {\n const dropdown = getDropdownDialog(selector);\n if (!dropdown) {\n throw new Error(`Dopdown dialog element not found: ${selector}`);\n }\n dropdown.init();\n};\n"],"names":["Selectors","DropdownDialog","constructor","element","button","querySelector","panel","init","this","dataset","dropdownDialogInitialized","addEventListener","_buttonKeyHandler","bind","_contentKeyHandler","event","key","stopPropagation","preventDefault","setVisible","_focusPanelContent","newFocus","focus","pendingPromise","Pending","setTimeout","firstFocusable","resolve","visible","isVisible","dropdown","getAttribute","setButtonContent","content","innerHTML","setButtonDisabled","disabled","setAttribute","removeAttribute","getElement","getDropdownDialog","selector","dropdownElement","document","Error"],"mappings":";;;;;;;6NAiCMA,yBACc,qCADdA,yBAEc,2CAOPC,eAKTC,YAAYC,cACHA,QAAUA,aACVC,OAASD,QAAQE,cAAcL,+BAC/BM,MAAQH,QAAQE,cAAcL,0BAQvCO,OACQC,KAAKL,QAAQM,QAAQC,iCAKpBN,OAAOO,iBAAiB,UAAWH,KAAKI,kBAAkBC,KAAKL,YAE/DF,MAAMK,iBAAiB,UAAWH,KAAKM,mBAAmBD,KAAKL,YAE/DL,QAAQM,QAAQC,2BAA4B,GAQrDE,kBAAkBG,UACI,YAAdA,MAAMC,KAAmC,cAAdD,MAAMC,WACjCD,MAAME,kBACNF,MAAMG,2BACDC,YAAW,GAIF,cAAdJ,MAAMC,KAAqC,eAAdD,MAAMC,MACnCD,MAAME,kBACNF,MAAMG,sBACDC,YAAW,QACXC,sBASbN,mBAAmBC,WACXM,SAAW,KAEG,QAAdN,MAAMC,MACNK,UAAW,qCAAqBb,KAAKF,QAEvB,SAAdS,MAAMC,MACNK,UAAW,sCAAsBb,KAAKF,QAExB,YAAdS,MAAMC,KAAmC,cAAdD,MAAMC,MACjCK,UAAW,yCAAyBb,KAAKF,OAAO,GAC3Ce,WACDA,SAAWb,KAAKJ,SAGN,cAAdW,MAAMC,KAAqC,eAAdD,MAAMC,MACnCK,UAAW,qCAAqBb,KAAKF,OAAO,IAE/B,OAAbe,WACAN,MAAME,kBACNF,MAAMG,iBACNG,SAASC,SAQjBF,2BACUG,eAAiB,IAAIC,iBAAQ,qCAGnCC,YAAW,WACDC,gBAAiB,sCAAsBlB,KAAKF,OAC9CoB,gBACAA,eAAeJ,QAEnBC,eAAeI,YAChB,KAOPR,WAAWS,SACHA,UAAYpB,KAAKqB,iCAIdrB,KAAKJ,QAAQ0B,SAAS,UAOjCD,kBACyD,SAA9CrB,KAAKJ,OAAO2B,aAAa,iBAOpCC,iBAAiBC,cACR7B,OAAO8B,UAAYD,QAO5BE,kBAAkBC,UACVA,cACKhC,OAAOiC,aAAa,WAAY,iBAEhCjC,OAAOkC,gBAAgB,YAQpCC,oBACW/B,KAAKL,sDASPqC,kBAAqBC,iBACxBC,gBAAkBC,SAAStC,cAAcoC,iBAC1CC,gBAGE,IAAIzC,eAAeyC,iBAFf,iEAWMD,iBACXX,SAAWU,kBAAkBC,cAC9BX,eACK,IAAIc,kDAA2CH,WAEzDX,SAASvB"} \ No newline at end of file +{"version":3,"file":"dialog.min.js","sources":["../../../src/local/dropdown/dialog.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 * Dropdown status JS controls.\n *\n * @module core/local/dropdown/dialog\n * @copyright 2023 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Dropdown from 'theme_boost/bootstrap/dropdown';\nimport {\n firstFocusableElement,\n lastFocusableElement,\n previousFocusableElement,\n nextFocusableElement,\n} from 'core/pagehelpers';\nimport Pending from 'core/pending';\n\nconst Selectors = {\n dropdownButton: '[data-for=\"dropdowndialog_button\"]',\n dropdownDialog: '[data-for=\"dropdowndialog_dialog\"]',\n};\n\n/**\n * Dropdown dialog class.\n * @private\n */\nexport class DropdownDialog {\n /**\n * Constructor.\n * @param {HTMLElement} element The element to initialize.\n */\n constructor(element) {\n this.element = element;\n this.button = element.querySelector(Selectors.dropdownButton);\n this.panel = element.querySelector(Selectors.dropdownDialog);\n }\n\n /**\n * Initialize the subpanel element.\n *\n * This method adds the event listeners to the subpanel and the position classes.\n */\n init() {\n if (this.element.dataset.dropdownDialogInitialized) {\n return;\n }\n\n // Menu Item events.\n this.button.addEventListener('keydown', this._buttonKeyHandler.bind(this));\n // Subpanel content events.\n this.panel.addEventListener('keydown', this._contentKeyHandler.bind(this));\n\n this.element.dataset.dropdownDialogInitialized = true;\n }\n\n /**\n * Dropdown button key handler.\n * @param {Event} event\n * @private\n */\n _buttonKeyHandler(event) {\n if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') {\n event.stopPropagation();\n event.preventDefault();\n this.setVisible(false);\n return;\n }\n\n if (event.key === 'ArrowDown' || event.key === 'ArrowRight') {\n event.stopPropagation();\n event.preventDefault();\n this.setVisible(true);\n this._focusPanelContent();\n }\n }\n\n /**\n * Sub panel content key handler.\n * @param {Event} event\n * @private\n */\n _contentKeyHandler(event) {\n let newFocus = null;\n\n if (event.key === 'End') {\n newFocus = lastFocusableElement(this.panel);\n }\n if (event.key === 'Home') {\n newFocus = firstFocusableElement(this.panel);\n }\n if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') {\n newFocus = previousFocusableElement(this.panel, false);\n if (!newFocus) {\n newFocus = this.button;\n }\n }\n if (event.key === 'ArrowDown' || event.key === 'ArrowRight') {\n newFocus = nextFocusableElement(this.panel, false);\n }\n if (newFocus !== null) {\n event.stopPropagation();\n event.preventDefault();\n newFocus.focus();\n }\n }\n\n /**\n * Focus on the first focusable element of the subpanel.\n * @private\n */\n _focusPanelContent() {\n const pendingPromise = new Pending('core/dropdown/dialog:focuscontent');\n // Some Bootstrap events are triggered after the click event.\n // To prevent this from affecting the focus we wait a bit.\n setTimeout(() => {\n const firstFocusable = firstFocusableElement(this.panel);\n if (firstFocusable) {\n firstFocusable.focus();\n }\n pendingPromise.resolve();\n }, 100);\n }\n\n /**\n * Set the visibility of a subpanel.\n * @param {Boolean} visible true if the subpanel should be visible.\n */\n setVisible(visible) {\n if (visible === this.isVisible()) {\n return;\n }\n Dropdown.getOrCreateInstance(this.button).toggle();\n }\n\n /**\n * Get the visibility of a subpanel.\n * @returns {Boolean} true if the subpanel is visible.\n */\n isVisible() {\n return this.button.getAttribute('aria-expanded') === 'true';\n }\n\n /**\n * Set the content of the button.\n * @param {String} content\n */\n setButtonContent(content) {\n this.button.innerHTML = content;\n }\n\n /**\n * Set the disabled state of the button.\n * @param {Boolean} disabled\n */\n setButtonDisabled(disabled) {\n if (disabled) {\n this.button.setAttribute('disabled', 'disabled');\n } else {\n this.button.removeAttribute('disabled');\n }\n }\n\n /**\n * Return the main dropdown HTML element.\n * @returns {HTMLElement} The element.\n */\n getElement() {\n return this.element;\n }\n}\n\n/**\n * Get the dropdown dialog instance from a selector.\n * @param {string} selector The query selector to init.\n * @returns {DropdownDialog|null} The dropdown dialog instance if any.\n */\nexport const getDropdownDialog = (selector) => {\n const dropdownElement = document.querySelector(selector);\n if (!dropdownElement) {\n return null;\n }\n return new DropdownDialog(dropdownElement);\n};\n\n/**\n * Initialize module.\n *\n * @method\n * @param {string} selector The query selector to init.\n */\nexport const init = (selector) => {\n const dropdown = getDropdownDialog(selector);\n if (!dropdown) {\n throw new Error(`Dopdown dialog element not found: ${selector}`);\n }\n dropdown.init();\n};\n"],"names":["Selectors","DropdownDialog","constructor","element","button","querySelector","panel","init","this","dataset","dropdownDialogInitialized","addEventListener","_buttonKeyHandler","bind","_contentKeyHandler","event","key","stopPropagation","preventDefault","setVisible","_focusPanelContent","newFocus","focus","pendingPromise","Pending","setTimeout","firstFocusable","resolve","visible","isVisible","getOrCreateInstance","toggle","getAttribute","setButtonContent","content","innerHTML","setButtonDisabled","disabled","setAttribute","removeAttribute","getElement","getDropdownDialog","selector","dropdownElement","document","dropdown","Error"],"mappings":";;;;;;;iOAgCMA,yBACc,qCADdA,yBAEc,2CAOPC,eAKTC,YAAYC,cACHA,QAAUA,aACVC,OAASD,QAAQE,cAAcL,+BAC/BM,MAAQH,QAAQE,cAAcL,0BAQvCO,OACQC,KAAKL,QAAQM,QAAQC,iCAKpBN,OAAOO,iBAAiB,UAAWH,KAAKI,kBAAkBC,KAAKL,YAE/DF,MAAMK,iBAAiB,UAAWH,KAAKM,mBAAmBD,KAAKL,YAE/DL,QAAQM,QAAQC,2BAA4B,GAQrDE,kBAAkBG,UACI,YAAdA,MAAMC,KAAmC,cAAdD,MAAMC,WACjCD,MAAME,kBACNF,MAAMG,2BACDC,YAAW,GAIF,cAAdJ,MAAMC,KAAqC,eAAdD,MAAMC,MACnCD,MAAME,kBACNF,MAAMG,sBACDC,YAAW,QACXC,sBASbN,mBAAmBC,WACXM,SAAW,KAEG,QAAdN,MAAMC,MACNK,UAAW,qCAAqBb,KAAKF,QAEvB,SAAdS,MAAMC,MACNK,UAAW,sCAAsBb,KAAKF,QAExB,YAAdS,MAAMC,KAAmC,cAAdD,MAAMC,MACjCK,UAAW,yCAAyBb,KAAKF,OAAO,GAC3Ce,WACDA,SAAWb,KAAKJ,SAGN,cAAdW,MAAMC,KAAqC,eAAdD,MAAMC,MACnCK,UAAW,qCAAqBb,KAAKF,OAAO,IAE/B,OAAbe,WACAN,MAAME,kBACNF,MAAMG,iBACNG,SAASC,SAQjBF,2BACUG,eAAiB,IAAIC,iBAAQ,qCAGnCC,YAAW,WACDC,gBAAiB,sCAAsBlB,KAAKF,OAC9CoB,gBACAA,eAAeJ,QAEnBC,eAAeI,YAChB,KAOPR,WAAWS,SACHA,UAAYpB,KAAKqB,+BAGZC,oBAAoBtB,KAAKJ,QAAQ2B,SAO9CF,kBACyD,SAA9CrB,KAAKJ,OAAO4B,aAAa,iBAOpCC,iBAAiBC,cACR9B,OAAO+B,UAAYD,QAO5BE,kBAAkBC,UACVA,cACKjC,OAAOkC,aAAa,WAAY,iBAEhClC,OAAOmC,gBAAgB,YAQpCC,oBACWhC,KAAKL,sDASPsC,kBAAqBC,iBACxBC,gBAAkBC,SAASvC,cAAcqC,iBAC1CC,gBAGE,IAAI1C,eAAe0C,iBAFf,iEAWMD,iBACXG,SAAWJ,kBAAkBC,cAC9BG,eACK,IAAIC,kDAA2CJ,WAEzDG,SAAStC"} \ No newline at end of file diff --git a/lib/amd/build/modal.min.js b/lib/amd/build/modal.min.js index f5cf1f6e10d23..67799756f634b 100644 --- a/lib/amd/build/modal.min.js +++ b/lib/amd/build/modal.min.js @@ -1,3 +1,3 @@ -define("core/modal",["exports","jquery","core/templates","core/notification","core/key_codes","core/modal_backdrop","core/modal_events","core/modal_registry","core/pending","core/custom_interaction_events","core_filters/events","core/local/aria/focuslock","core/aria","core/fullscreen","./toast"],(function(_exports,_jquery,Templates,Notification,KeyCodes,_modal_backdrop,_modal_events,ModalRegistry,_pending,CustomEvents,FilterEvents,FocusLock,Aria,Fullscreen,_toast){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}}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,_jquery=_interopRequireDefault(_jquery),Templates=_interopRequireWildcard(Templates),Notification=_interopRequireWildcard(Notification),KeyCodes=_interopRequireWildcard(KeyCodes),_modal_backdrop=_interopRequireDefault(_modal_backdrop),_modal_events=_interopRequireDefault(_modal_events),ModalRegistry=_interopRequireWildcard(ModalRegistry),_pending=_interopRequireDefault(_pending),CustomEvents=_interopRequireWildcard(CustomEvents),FilterEvents=_interopRequireWildcard(FilterEvents),FocusLock=_interopRequireWildcard(FocusLock),Aria=_interopRequireWildcard(Aria),Fullscreen=_interopRequireWildcard(Fullscreen);const SELECTORS_CONTAINER='[data-region="modal-container"]',SELECTORS_MODAL='[data-region="modal"]',SELECTORS_HEADER='[data-region="header"]',SELECTORS_TITLE='[data-region="title"]',SELECTORS_BODY='[data-region="body"]',SELECTORS_FOOTER='[data-region="footer"]',SELECTORS_HIDE='[data-action="hide"]',SELECTORS_DIALOG="[role=dialog]",SELECTORS_FORM="form",SELECTORS_MENU_BAR="[role=menubar]",SELECTORS_HAS_Z_INDEX=".moodle-has-zindex",TEMPLATES_LOADING="core/loading",TEMPLATES_BACKDROP="core/modal_backdrop";class Modal{get root(){return(0,_jquery.default)(this._root.filter(SELECTORS_CONTAINER))}set root(root){this._root=root}constructor(root){this.root=(0,_jquery.default)(root),this.modal=this.root.find(SELECTORS_MODAL),this.header=this.modal.find(SELECTORS_HEADER),this.headerPromise=_jquery.default.Deferred(),this.title=this.header.find(SELECTORS_TITLE),this.titlePromise=_jquery.default.Deferred(),this.body=this.modal.find(SELECTORS_BODY),this.bodyPromise=_jquery.default.Deferred(),this.footer=this.modal.find(SELECTORS_FOOTER),this.footerPromise=_jquery.default.Deferred(),this.hiddenSiblings=[],this.isAttached=!1,this.bodyJS=null,this.footerJS=null,this.modalCount=Modal.modalCounter++,this.attachmentPoint=document.createElement("div"),document.body.append(this.attachmentPoint),this.focusOnClose=null,this.templateJS=null,this.root.is(SELECTORS_CONTAINER)||Notification.exception({message:"Element is not a modal container"}),this.modal.length||Notification.exception({message:"Container does not contain a modal"}),this.header.length||Notification.exception({message:"Modal is missing a header region"}),this.title.length||Notification.exception({message:"Modal header is missing a title region"}),this.body.length||Notification.exception({message:"Modal is missing a body region"}),this.footer.length||Notification.exception({message:"Modal is missing a footer region"}),this.registerEventListeners()}static registerModalType(){if(!this.TYPE)throw new Error("Unknown modal type",this);if(!this.TEMPLATE)throw new Error("Unknown modal template",this);ModalRegistry.register(this.TYPE,this,this.TEMPLATE)}static async create(){let modalConfig=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const pendingModalPromise=new _pending.default("core/modal_factory:create");modalConfig.type=this.TYPE;const templateName=this._getTemplateName(modalConfig),templateContext=modalConfig.templateContext||{},{html:html,js:js}=await Templates.renderForPromise(templateName,templateContext),modal=new this(html);return js&&modal.setTemplateJS(js),modal.configure(modalConfig),pendingModalPromise.resolve(),modal}static _getTemplateName(modalConfig){if(modalConfig.template)return modalConfig.template;if(this.TEMPLATE)return this.TEMPLATE;if(ModalRegistry.has(this.TYPE)){window.console.warning("Use of core/modal_registry is deprecated. Please define your modal template in a new static TEMPLATE property on your modal class.");return ModalRegistry.get(this.TYPE).template}throw new Error("Unable to determine template name for modal ".concat(this.TYPE))}configure(){let{show:show=!1,large:large=!1,isVerticallyCentered:isVerticallyCentered=!1,removeOnClose:removeOnClose=!1,scrollable:scrollable=!0,returnElement:returnElement,title:title,body:body,footer:footer,buttons:buttons={}}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};large&&this.setLarge(),isVerticallyCentered&&this.setVerticallyCentered(),this.setRemoveOnClose(removeOnClose),this.setReturnElement(returnElement),this.setScrollable(scrollable),void 0!==title&&this.setTitle(title),void 0!==body&&this.setBody(body),void 0!==footer&&this.setFooter(footer),Object.entries(buttons).forEach((_ref=>{let[key,value]=_ref;return this.setButtonText(key,value)})),show&&this.show()}attachToDOM(){this.getAttachmentPoint().append(this._root),this.isAttached||(FocusLock.trapFocus(this.root[0]),this.templateJS&&(Templates.runTemplateJS(this.templateJS),this.templateJS=null),this.bodyJS&&(Templates.runTemplateJS(this.bodyJS),this.bodyJS=null),this.footerJS&&(Templates.runTemplateJS(this.footerJS),this.footerJS=null),this.isAttached=!0)}countOtherVisibleModals(){let count=0;return(0,_jquery.default)("body").find(SELECTORS_CONTAINER).each(((index,element)=>{element=(0,_jquery.default)(element),!this.root.is(element)&&element.hasClass("show")&&count++})),count}getBackdrop(){return Modal.backdropPromise||(Modal.backdropPromise=Templates.render(TEMPLATES_BACKDROP,{}).then((html=>new _modal_backdrop.default((0,_jquery.default)(html)))).catch(Notification.exception)),Modal.backdropPromise}getRoot(){return this.root}getModal(){return this.modal}getTitle(){return this.title}getBody(){return this.body}getFooter(){return this.footer}getTitlePromise(){return this.titlePromise}getBodyPromise(){return this.bodyPromise}getFooterPromise(){return this.footerPromise}getModalCount(){return this.modalCount}setTitle(value){const title=this.getTitle();this.titlePromise=_jquery.default.Deferred(),this.asyncSet(value,title.html.bind(title)).then((()=>{this.titlePromise.resolve(title)})).catch(Notification.exception)}setBody(value){this.bodyPromise=_jquery.default.Deferred();const body=this.getBody();if("string"==typeof value)body.html(value),FilterEvents.notifyFilterContentUpdated(body),this.getRoot().trigger(_modal_events.default.bodyRendered,this),this.bodyPromise.resolve(body);else{const modalPromise=new _pending.default("amd-modal-js-pending-id-".concat(this.getModalCount()));let contentPromise=null;if(body.css("overflow","hidden"),"pending"==(value=_jquery.default.when(value)).state()){let height=body.innerHeight();height<100&&(height=100),body.animate({height:"".concat(height,"px")},150),body.html(""),contentPromise=Templates.render(TEMPLATES_LOADING,{}).then((html=>{const loadingIcon=(0,_jquery.default)(html).hide();return body.html(loadingIcon),loadingIcon.fadeIn(150),_jquery.default.when(loadingIcon.promise(),value)})).then((loadingIcon=>loadingIcon.fadeOut(100).promise())).then((()=>value))}else contentPromise=value;contentPromise.then(((html,js)=>{let result=null;if(this.isVisible()){body.css("opacity",0);const currentHeight=body.innerHeight();body.html(html),body.css("height","");const newHeight=body.innerHeight();body.css("height","".concat(currentHeight,"px")),result=body.animate({height:"".concat(newHeight,"px"),opacity:1},{duration:150,queue:!1}).promise()}else body.html(html);return js&&(this.isAttached?Templates.runTemplateJS(js):this.bodyJS=js),result})).then((result=>(FilterEvents.notifyFilterContentUpdated(body),this.getRoot().trigger(_modal_events.default.bodyRendered,this),result))).then((()=>{this.bodyPromise.resolve(body)})).catch(Notification.exception).always((()=>{body.css("height",""),body.css("overflow",""),body.css("opacity",""),modalPromise.resolve()}))}}setBodyContent(promise){return promise.then((_ref2=>{let{html:html,js:js}=_ref2;return this.setBody(_jquery.default.when(html,js))})).catch((exception=>{throw this.hide(),exception}))}setFooter(value){this.showFooter(),this.footerPromise=_jquery.default.Deferred();const footer=this.getFooter();"string"==typeof value?(footer.html(value),this.footerPromise.resolve(footer)):Templates.render(TEMPLATES_LOADING,{}).then((html=>(footer.html(html),value))).then(((html,js)=>(footer.html(html),js&&(this.isAttached?Templates.runTemplateJS(js):this.footerJS=js),footer))).then((footer=>{this.footerPromise.resolve(footer),this.showFooter()})).catch(Notification.exception)}hasFooterContent(){return!!this.getFooter().children().length}hideFooter(){this.getFooter().addClass("hidden")}showFooter(){this.getFooter().removeClass("hidden")}setLarge(){this.isLarge()||this.getModal().addClass("modal-lg")}setVerticallyCentered(){this.isVerticallyCentered()||this.getModal().addClass("modal-dialog-centered")}isLarge(){return this.getModal().hasClass("modal-lg")}isVerticallyCentered(){return this.getModal().hasClass("modal-dialog-centered")}setSmall(){this.isSmall()||this.getModal().removeClass("modal-lg")}isSmall(){return!this.getModal().hasClass("modal-lg")}setScrollable(value){value?this.getModal()[0].classList.add("modal-dialog-scrollable"):this.getModal()[0].classList.remove("modal-dialog-scrollable")}calculateZIndex(){const items=(0,_jquery.default)("".concat(SELECTORS_DIALOG,", ").concat(SELECTORS_MENU_BAR,", ").concat(SELECTORS_HAS_Z_INDEX));let zIndex=parseInt(this.root.css("z-index"));return items.each(((index,item)=>{if(!(item=(0,_jquery.default)(item)).is(":visible"))return;const itemZIndex=item.css("z-index")?parseInt(item.css("z-index")):0;itemZIndex>zIndex&&(zIndex=itemZIndex)})),zIndex}isVisible(){return this.root.hasClass("show")}hasFocus(){const target=(0,_jquery.default)(document.activeElement);return this.root.is(target)||this.root.has(target).length}hasTransitions(){return this.getRoot().hasClass("fade")}getAttachmentPoint(){return(0,_jquery.default)(Fullscreen.getElement()||this.attachmentPoint)}show(){if(this.isVisible())return _jquery.default.Deferred().resolve();const pendingPromise=new _pending.default("core/modal:show");return this.hasFooterContent()?this.showFooter():this.hideFooter(),this.attachToDOM(),!this.focusOnClose&&document.activeElement&&(this.focusOnClose=document.activeElement),this.getBackdrop().then((backdrop=>{const newIndex=this.calculateZIndex()+2,newBackdropIndex=newIndex-1;this.root.css("z-index",newIndex),backdrop.setZIndex(newBackdropIndex),backdrop.show(),this.root.removeClass("hide").addClass("show"),this.accessibilityShow(),this.getModal().focus(),(0,_jquery.default)("body").addClass("modal-open"),this.root.trigger(_modal_events.default.shown,this)})).then(pendingPromise.resolve)}hideIfNotForm(){0==this.modal.find(SELECTORS_FORM).length&&this.hide()}hide(){this.getBackdrop().done((backdrop=>{FocusLock.untrapFocus(),this.countOtherVisibleModals()||(backdrop.hide(),(0,_jquery.default)("body").removeClass("modal-open"));const currentIndex=parseInt(this.root.css("z-index"));this.root.css("z-index",""),backdrop.setZIndex(currentIndex-3),this.accessibilityHide(),this.hasTransitions()?this.getRoot().one("transitionend webkitTransitionEnd oTransitionEnd",(()=>{this.getRoot().removeClass("show").addClass("hide")})):this.getRoot().removeClass("show").addClass("hide"),(0,_jquery.default)(document.body).find(this.getRoot()).length&&(0,_jquery.default)(document.body).append(this.getRoot()),this.getRoot().find('[data-bs-toggle="popover"]').each((function(){var _document$getElementB;null===(_document$getElementB=document.getElementById(this.getAttribute("aria-describedby")))||void 0===_document$getElementB||_document$getElementB.remove()})),this.root.trigger(_modal_events.default.hidden,this)}))}destroy(){this.hide(),(0,_toast.removeToastRegion)(this.getBody().get(0)),this.root.remove(),this.root.trigger(_modal_events.default.destroyed,this),this.attachmentPoint.remove()}accessibilityShow(){Aria.unhide(this.root.get()),Aria.hideSiblings(this.root.get()[0])}accessibilityHide(){Aria.unhideSiblings(this.root.get()[0]),Aria.hide(this.root.get())}registerEventListeners(){this.getRoot().on("keydown",(e=>{this.isVisible()&&e.keyCode==KeyCodes.escape&&(this.removeOnClose?this.destroy():this.hide())})),this.getRoot().click((e=>{if(!(0,_jquery.default)(e.target).closest(SELECTORS_MODAL).length&&(0,_jquery.default)(e.target).closest(SELECTORS_CONTAINER).length){const outsideClickEvent=_jquery.default.Event(_modal_events.default.outsideClick);this.getRoot().trigger(outsideClickEvent,this),outsideClickEvent.isDefaultPrevented()||this.hideIfNotForm()}})),CustomEvents.define(this.getModal(),[CustomEvents.events.activate]),this.getModal().on(CustomEvents.events.activate,SELECTORS_HIDE,((e,data)=>{this.removeOnClose?this.destroy():this.hide(),data.originalEvent.preventDefault()})),this.getRoot().on(_modal_events.default.hidden,(()=>{this.focusOnClose&&this.focusOnClose.focus()}))}registerCloseOnCancel(){this.getModal().on(CustomEvents.events.activate,this.getActionSelector("cancel"),((e,data)=>{const cancelEvent=_jquery.default.Event(_modal_events.default.cancel);this.getRoot().trigger(cancelEvent,this),cancelEvent.isDefaultPrevented()||(data.originalEvent.preventDefault(),this.removeOnClose?this.destroy():this.hide())}))}registerCloseOnSave(){this.getModal().on(CustomEvents.events.activate,this.getActionSelector("save"),((e,data)=>{const saveEvent=_jquery.default.Event(_modal_events.default.save);this.getRoot().trigger(saveEvent,this),saveEvent.isDefaultPrevented()||(data.originalEvent.preventDefault(),this.removeOnClose?this.destroy():this.hide())}))}registerCloseOnDelete(){this.getModal().on(CustomEvents.events.activate,this.getActionSelector("delete"),((e,data)=>{const deleteEvent=_jquery.default.Event(_modal_events.default.delete);this.getRoot().trigger(deleteEvent,this),deleteEvent.isDefaultPrevented()||(data.originalEvent.preventDefault(),this.removeOnClose?this.destroy():this.hide())}))}asyncSet(value,setFunction){return(value=>value instanceof Promise?_jquery.default.when(value):"object"==typeof value&&value.hasOwnProperty("then")?value:_jquery.default.Deferred().resolve(value))(value).then((content=>setFunction(content))).catch(Notification.exception)}setButtonText(action,value){const button=this.getFooter().find(this.getActionSelector(action));if(!button)throw new Error("Unable to find the '"+action+"' button");return this.asyncSet(value,button.text.bind(button))}getActionSelector(action){return"[data-action='"+action+"']"}setRemoveOnClose(remove){this.removeOnClose=remove}setReturnElement(element){this.focusOnClose=element}setButtonDisabled(action,disabled){const button=this.getFooter().find(this.getActionSelector(action));if(!button)throw new Error("Unable to find the '"+action+"' button");disabled?button.attr("disabled",""):button.removeAttr("disabled")}setTemplateJS(js){this.templateJS=js}}return _exports.default=Modal,_defineProperty(Modal,"TYPE","default"),_defineProperty(Modal,"TEMPLATE","core/modal"),_defineProperty(Modal,"backdropPromise",null),_defineProperty(Modal,"modalCounter",0),_exports.default})); +define("core/modal",["exports","jquery","core/templates","core/notification","core/key_codes","core/modal_backdrop","core/modal_events","core/modal_registry","core/pending","core/custom_interaction_events","core_filters/events","core/local/aria/focuslock","core/aria","core/fullscreen","./toast","core/event_dispatcher"],(function(_exports,_jquery,Templates,Notification,KeyCodes,_modal_backdrop,_modal_events,ModalRegistry,_pending,CustomEvents,FilterEvents,FocusLock,Aria,Fullscreen,_toast,_event_dispatcher){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}}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,_jquery=_interopRequireDefault(_jquery),Templates=_interopRequireWildcard(Templates),Notification=_interopRequireWildcard(Notification),KeyCodes=_interopRequireWildcard(KeyCodes),_modal_backdrop=_interopRequireDefault(_modal_backdrop),_modal_events=_interopRequireDefault(_modal_events),ModalRegistry=_interopRequireWildcard(ModalRegistry),_pending=_interopRequireDefault(_pending),CustomEvents=_interopRequireWildcard(CustomEvents),FilterEvents=_interopRequireWildcard(FilterEvents),FocusLock=_interopRequireWildcard(FocusLock),Aria=_interopRequireWildcard(Aria),Fullscreen=_interopRequireWildcard(Fullscreen);const SELECTORS_CONTAINER='[data-region="modal-container"]',SELECTORS_MODAL='[data-region="modal"]',SELECTORS_HEADER='[data-region="header"]',SELECTORS_TITLE='[data-region="title"]',SELECTORS_BODY='[data-region="body"]',SELECTORS_FOOTER='[data-region="footer"]',SELECTORS_HIDE='[data-action="hide"]',SELECTORS_DIALOG="[role=dialog]",SELECTORS_FORM="form",SELECTORS_MENU_BAR="[role=menubar]",SELECTORS_HAS_Z_INDEX=".moodle-has-zindex",TEMPLATES_LOADING="core/loading",TEMPLATES_BACKDROP="core/modal_backdrop";class Modal{get root(){return(0,_jquery.default)(this._root.filter(SELECTORS_CONTAINER))}set root(root){this._root=root}constructor(root){this.root=(0,_jquery.default)(root),this.modal=this.root.find(SELECTORS_MODAL),this.header=this.modal.find(SELECTORS_HEADER),this.headerPromise=_jquery.default.Deferred(),this.title=this.header.find(SELECTORS_TITLE),this.titlePromise=_jquery.default.Deferred(),this.body=this.modal.find(SELECTORS_BODY),this.bodyPromise=_jquery.default.Deferred(),this.footer=this.modal.find(SELECTORS_FOOTER),this.footerPromise=_jquery.default.Deferred(),this.hiddenSiblings=[],this.isAttached=!1,this.bodyJS=null,this.footerJS=null,this.modalCount=Modal.modalCounter++,this.attachmentPoint=document.createElement("div"),document.body.append(this.attachmentPoint),this.focusOnClose=null,this.templateJS=null,this.root.is(SELECTORS_CONTAINER)||Notification.exception({message:"Element is not a modal container"}),this.modal.length||Notification.exception({message:"Container does not contain a modal"}),this.header.length||Notification.exception({message:"Modal is missing a header region"}),this.title.length||Notification.exception({message:"Modal header is missing a title region"}),this.body.length||Notification.exception({message:"Modal is missing a body region"}),this.footer.length||Notification.exception({message:"Modal is missing a footer region"}),this.registerEventListeners()}static registerModalType(){if(!this.TYPE)throw new Error("Unknown modal type",this);if(!this.TEMPLATE)throw new Error("Unknown modal template",this);ModalRegistry.register(this.TYPE,this,this.TEMPLATE)}static async create(){let modalConfig=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const pendingModalPromise=new _pending.default("core/modal_factory:create");modalConfig.type=this.TYPE;const templateName=this._getTemplateName(modalConfig),templateContext=modalConfig.templateContext||{},{html:html,js:js}=await Templates.renderForPromise(templateName,templateContext),modal=new this(html);return js&&modal.setTemplateJS(js),modal.configure(modalConfig),pendingModalPromise.resolve(),modal}static _getTemplateName(modalConfig){if(modalConfig.template)return modalConfig.template;if(this.TEMPLATE)return this.TEMPLATE;if(ModalRegistry.has(this.TYPE)){window.console.warning("Use of core/modal_registry is deprecated. Please define your modal template in a new static TEMPLATE property on your modal class.");return ModalRegistry.get(this.TYPE).template}throw new Error("Unable to determine template name for modal ".concat(this.TYPE))}configure(){let{show:show=!1,large:large=!1,isVerticallyCentered:isVerticallyCentered=!1,removeOnClose:removeOnClose=!1,scrollable:scrollable=!0,returnElement:returnElement,title:title,body:body,footer:footer,buttons:buttons={}}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};large&&this.setLarge(),isVerticallyCentered&&this.setVerticallyCentered(),this.setRemoveOnClose(removeOnClose),this.setReturnElement(returnElement),this.setScrollable(scrollable),void 0!==title&&this.setTitle(title),void 0!==body&&this.setBody(body),void 0!==footer&&this.setFooter(footer),Object.entries(buttons).forEach((_ref=>{let[key,value]=_ref;return this.setButtonText(key,value)})),show&&this.show()}attachToDOM(){this.getAttachmentPoint().append(this._root),this.isAttached||(FocusLock.trapFocus(this.root[0]),this.templateJS&&(Templates.runTemplateJS(this.templateJS),this.templateJS=null),this.bodyJS&&(Templates.runTemplateJS(this.bodyJS),this.bodyJS=null),this.footerJS&&(Templates.runTemplateJS(this.footerJS),this.footerJS=null),this.isAttached=!0)}countOtherVisibleModals(){let count=0;return(0,_jquery.default)("body").find(SELECTORS_CONTAINER).each(((index,element)=>{element=(0,_jquery.default)(element),!this.root.is(element)&&element.hasClass("show")&&count++})),count}getBackdrop(){return Modal.backdropPromise||(Modal.backdropPromise=Templates.render(TEMPLATES_BACKDROP,{}).then((html=>new _modal_backdrop.default((0,_jquery.default)(html)))).catch(Notification.exception)),Modal.backdropPromise}getRoot(){return this.root}getModal(){return this.modal}getTitle(){return this.title}getBody(){return this.body}getFooter(){return this.footer}getTitlePromise(){return this.titlePromise}getBodyPromise(){return this.bodyPromise}getFooterPromise(){return this.footerPromise}getModalCount(){return this.modalCount}setTitle(value){const title=this.getTitle();this.titlePromise=_jquery.default.Deferred(),this.asyncSet(value,title.html.bind(title)).then((()=>{this.titlePromise.resolve(title)})).catch(Notification.exception)}setBody(value){this.bodyPromise=_jquery.default.Deferred();const body=this.getBody();if("string"==typeof value)body.html(value),FilterEvents.notifyFilterContentUpdated(body),this.getRoot().trigger(_modal_events.default.bodyRendered,this),this.bodyPromise.resolve(body);else{const modalPromise=new _pending.default("amd-modal-js-pending-id-".concat(this.getModalCount()));let contentPromise=null;if(body.css("overflow","hidden"),"pending"==(value=_jquery.default.when(value)).state()){let height=body.innerHeight();height<100&&(height=100),body.animate({height:"".concat(height,"px")},150),body.html(""),contentPromise=Templates.render(TEMPLATES_LOADING,{}).then((html=>{const loadingIcon=(0,_jquery.default)(html).hide();return body.html(loadingIcon),loadingIcon.fadeIn(150),_jquery.default.when(loadingIcon.promise(),value)})).then((loadingIcon=>loadingIcon.fadeOut(100).promise())).then((()=>value))}else contentPromise=value;contentPromise.then(((html,js)=>{let result=null;if(this.isVisible()){body.css("opacity",0);const currentHeight=body.innerHeight();body.html(html),body.css("height","");const newHeight=body.innerHeight();body.css("height","".concat(currentHeight,"px")),result=body.animate({height:"".concat(newHeight,"px"),opacity:1},{duration:150,queue:!1}).promise()}else body.html(html);return js&&(this.isAttached?Templates.runTemplateJS(js):this.bodyJS=js),result})).then((result=>(FilterEvents.notifyFilterContentUpdated(body),this.getRoot().trigger(_modal_events.default.bodyRendered,this),(0,_event_dispatcher.dispatchEvent)("core/modal:bodyRendered",this,this.modal[0]),result))).then((()=>{this.bodyPromise.resolve(body)})).catch(Notification.exception).always((()=>{body.css("height",""),body.css("overflow",""),body.css("opacity",""),modalPromise.resolve()}))}}setBodyContent(promise){return promise.then((_ref2=>{let{html:html,js:js}=_ref2;return this.setBody(_jquery.default.when(html,js))})).catch((exception=>{throw this.hide(),exception}))}setFooter(value){this.showFooter(),this.footerPromise=_jquery.default.Deferred();const footer=this.getFooter();"string"==typeof value?(footer.html(value),this.footerPromise.resolve(footer)):Templates.render(TEMPLATES_LOADING,{}).then((html=>(footer.html(html),value))).then(((html,js)=>(footer.html(html),js&&(this.isAttached?Templates.runTemplateJS(js):this.footerJS=js),footer))).then((footer=>{this.footerPromise.resolve(footer),this.showFooter()})).catch(Notification.exception)}hasFooterContent(){return!!this.getFooter().children().length}hideFooter(){this.getFooter().addClass("hidden")}showFooter(){this.getFooter().removeClass("hidden")}setLarge(){this.isLarge()||this.getModal().addClass("modal-lg")}setVerticallyCentered(){this.isVerticallyCentered()||this.getModal().addClass("modal-dialog-centered")}isLarge(){return this.getModal().hasClass("modal-lg")}isVerticallyCentered(){return this.getModal().hasClass("modal-dialog-centered")}setSmall(){this.isSmall()||this.getModal().removeClass("modal-lg")}isSmall(){return!this.getModal().hasClass("modal-lg")}setScrollable(value){value?this.getModal()[0].classList.add("modal-dialog-scrollable"):this.getModal()[0].classList.remove("modal-dialog-scrollable")}calculateZIndex(){const items=(0,_jquery.default)("".concat(SELECTORS_DIALOG,", ").concat(SELECTORS_MENU_BAR,", ").concat(SELECTORS_HAS_Z_INDEX));let zIndex=parseInt(this.root.css("z-index"));return items.each(((index,item)=>{if(!(item=(0,_jquery.default)(item)).is(":visible"))return;const itemZIndex=item.css("z-index")?parseInt(item.css("z-index")):0;itemZIndex>zIndex&&(zIndex=itemZIndex)})),zIndex}isVisible(){return this.root.hasClass("show")}hasFocus(){const target=(0,_jquery.default)(document.activeElement);return this.root.is(target)||this.root.has(target).length}hasTransitions(){return this.getRoot().hasClass("fade")}getAttachmentPoint(){return(0,_jquery.default)(Fullscreen.getElement()||this.attachmentPoint)}show(){if(this.isVisible())return _jquery.default.Deferred().resolve();const pendingPromise=new _pending.default("core/modal:show");return this.hasFooterContent()?this.showFooter():this.hideFooter(),this.attachToDOM(),!this.focusOnClose&&document.activeElement&&(this.focusOnClose=document.activeElement),this.getBackdrop().then((backdrop=>{const newIndex=this.calculateZIndex()+2,newBackdropIndex=newIndex-1;this.root.css("z-index",newIndex),backdrop.setZIndex(newBackdropIndex),backdrop.show(),this.root.removeClass("hide").addClass("show"),this.accessibilityShow(),this.getModal().focus(),(0,_jquery.default)("body").addClass("modal-open"),this.root.trigger(_modal_events.default.shown,this),(0,_event_dispatcher.dispatchEvent)("core/modal:shown",this,this.modal[0])})).then(pendingPromise.resolve)}hideIfNotForm(){0==this.modal.find(SELECTORS_FORM).length&&this.hide()}hide(){this.getBackdrop().done((backdrop=>{FocusLock.untrapFocus(),this.countOtherVisibleModals()||(backdrop.hide(),(0,_jquery.default)("body").removeClass("modal-open"));const currentIndex=parseInt(this.root.css("z-index"));this.root.css("z-index",""),backdrop.setZIndex(currentIndex-3),this.accessibilityHide(),this.hasTransitions()?this.getRoot().one("transitionend webkitTransitionEnd oTransitionEnd",(()=>{this.getRoot().removeClass("show").addClass("hide")})):this.getRoot().removeClass("show").addClass("hide"),(0,_jquery.default)(document.body).find(this.getRoot()).length&&(0,_jquery.default)(document.body).append(this.getRoot()),this.getRoot().find('[data-bs-toggle="popover"]').each((function(){var _document$getElementB;null===(_document$getElementB=document.getElementById(this.getAttribute("aria-describedby")))||void 0===_document$getElementB||_document$getElementB.remove()})),this.root.trigger(_modal_events.default.hidden,this)}))}destroy(){this.hide(),(0,_toast.removeToastRegion)(this.getBody().get(0)),this.root.remove(),this.root.trigger(_modal_events.default.destroyed,this),this.attachmentPoint.remove()}accessibilityShow(){Aria.unhide(this.root.get()),Aria.hideSiblings(this.root.get()[0])}accessibilityHide(){Aria.unhideSiblings(this.root.get()[0]),Aria.hide(this.root.get())}registerEventListeners(){this.getRoot().on("keydown",(e=>{this.isVisible()&&e.keyCode==KeyCodes.escape&&(this.removeOnClose?this.destroy():this.hide())})),this.getRoot().click((e=>{if(!(0,_jquery.default)(e.target).closest(SELECTORS_MODAL).length&&(0,_jquery.default)(e.target).closest(SELECTORS_CONTAINER).length){const outsideClickEvent=_jquery.default.Event(_modal_events.default.outsideClick);this.getRoot().trigger(outsideClickEvent,this),outsideClickEvent.isDefaultPrevented()||this.hideIfNotForm()}})),CustomEvents.define(this.getModal(),[CustomEvents.events.activate]),this.getModal().on(CustomEvents.events.activate,SELECTORS_HIDE,((e,data)=>{this.removeOnClose?this.destroy():this.hide(),data.originalEvent.preventDefault()})),this.getRoot().on(_modal_events.default.hidden,(()=>{this.focusOnClose&&this.focusOnClose.focus()}))}registerCloseOnCancel(){this.getModal().on(CustomEvents.events.activate,this.getActionSelector("cancel"),((e,data)=>{const cancelEvent=_jquery.default.Event(_modal_events.default.cancel);this.getRoot().trigger(cancelEvent,this),cancelEvent.isDefaultPrevented()||(data.originalEvent.preventDefault(),this.removeOnClose?this.destroy():this.hide())}))}registerCloseOnSave(){this.getModal().on(CustomEvents.events.activate,this.getActionSelector("save"),((e,data)=>{const saveEvent=_jquery.default.Event(_modal_events.default.save);this.getRoot().trigger(saveEvent,this),saveEvent.isDefaultPrevented()||(data.originalEvent.preventDefault(),this.removeOnClose?this.destroy():this.hide())}))}registerCloseOnDelete(){this.getModal().on(CustomEvents.events.activate,this.getActionSelector("delete"),((e,data)=>{const deleteEvent=_jquery.default.Event(_modal_events.default.delete);this.getRoot().trigger(deleteEvent,this),deleteEvent.isDefaultPrevented()||(data.originalEvent.preventDefault(),this.removeOnClose?this.destroy():this.hide())}))}asyncSet(value,setFunction){return(value=>value instanceof Promise?_jquery.default.when(value):"object"==typeof value&&value.hasOwnProperty("then")?value:_jquery.default.Deferred().resolve(value))(value).then((content=>setFunction(content))).catch(Notification.exception)}setButtonText(action,value){const button=this.getFooter().find(this.getActionSelector(action));if(!button)throw new Error("Unable to find the '"+action+"' button");return this.asyncSet(value,button.text.bind(button))}getActionSelector(action){return"[data-action='"+action+"']"}setRemoveOnClose(remove){this.removeOnClose=remove}setReturnElement(element){this.focusOnClose=element}setButtonDisabled(action,disabled){const button=this.getFooter().find(this.getActionSelector(action));if(!button)throw new Error("Unable to find the '"+action+"' button");disabled?button.attr("disabled",""):button.removeAttr("disabled")}setTemplateJS(js){this.templateJS=js}}return _exports.default=Modal,_defineProperty(Modal,"TYPE","default"),_defineProperty(Modal,"TEMPLATE","core/modal"),_defineProperty(Modal,"backdropPromise",null),_defineProperty(Modal,"modalCounter",0),_exports.default})); //# sourceMappingURL=modal.min.js.map \ No newline at end of file diff --git a/lib/amd/build/modal.min.js.map b/lib/amd/build/modal.min.js.map index 68a5e6e085222..a35763ff17bc6 100644 --- a/lib/amd/build/modal.min.js.map +++ b/lib/amd/build/modal.min.js.map @@ -1 +1 @@ -{"version":3,"file":"modal.min.js","sources":["../src/modal.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 * Contain the logic for modals.\n *\n * @module core/modal\n * @copyright 2016 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as Templates from 'core/templates';\nimport * as Notification from 'core/notification';\nimport * as KeyCodes from 'core/key_codes';\nimport ModalBackdrop from 'core/modal_backdrop';\nimport ModalEvents from 'core/modal_events';\nimport * as ModalRegistry from 'core/modal_registry';\nimport Pending from 'core/pending';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as FilterEvents from 'core_filters/events';\nimport * as FocusLock from 'core/local/aria/focuslock';\nimport * as Aria from 'core/aria';\nimport * as Fullscreen from 'core/fullscreen';\nimport {removeToastRegion} from './toast';\n\n/**\n * A configuration to provide to the modal.\n *\n * @typedef {Object} ModalConfig\n *\n * @property {string} [type] The type of modal to create.\n * @property {string|Promise} [title] The title of the modal.\n * @property {string|Promise} [body] The body of the modal.\n * @property {string|Promise} [footer] The footer of the modal.\n * @property {boolean} [show=false] Whether to show the modal immediately.\n * @property {boolean} [scrollable=true] Whether the modal should be scrollable.\n * @property {boolean} [removeOnClose=true] Whether the modal should be removed from the DOM when it is closed.\n * @property {Element|jQuery} [returnElement] The element to focus when closing the modal.\n * @property {boolean} [large=false] Whether the modal should be a large modal.\n * @property {boolean} [isVerticallyCentered=false] Whether the modal should be vertically centered.\n * @property {object} [buttons={}] The buttons to display in the footer as a key => title pair.\n */\n\nconst SELECTORS = {\n CONTAINER: '[data-region=\"modal-container\"]',\n MODAL: '[data-region=\"modal\"]',\n HEADER: '[data-region=\"header\"]',\n TITLE: '[data-region=\"title\"]',\n BODY: '[data-region=\"body\"]',\n FOOTER: '[data-region=\"footer\"]',\n HIDE: '[data-action=\"hide\"]',\n DIALOG: '[role=dialog]',\n FORM: 'form',\n MENU_BAR: '[role=menubar]',\n HAS_Z_INDEX: '.moodle-has-zindex',\n CAN_RECEIVE_FOCUS: 'input:not([type=\"hidden\"]), a[href], button, textarea, select, [tabindex]',\n};\n\nconst TEMPLATES = {\n LOADING: 'core/loading',\n BACKDROP: 'core/modal_backdrop',\n};\n\nexport default class Modal {\n /** @var {string} The type of modal */\n static TYPE = 'default';\n\n /** @var {string} The template to use for this modal */\n static TEMPLATE = 'core/modal';\n\n /** @var {Promise} Module singleton for the backdrop to be reused by all Modal instances */\n static backdropPromise = null;\n\n /**\n * @var {Number} A counter that gets incremented for each modal created.\n * This can be used to generate unique values for the modals.\n */\n static modalCounter = 0;\n\n /**\n * Getter method for .root element.\n * @return {object} jQuery object\n */\n get root() {\n return $(this._root.filter(SELECTORS.CONTAINER));\n }\n\n /**\n * Setter method for .root element.\n * @param {object} root jQuery object\n */\n set root(root) {\n this._root = root;\n }\n\n /**\n * Constructor for the Modal.\n *\n * @param {HTMLElement} root The HTMLElement at the root of the Modal content\n */\n constructor(root) {\n this.root = $(root);\n\n this.modal = this.root.find(SELECTORS.MODAL);\n this.header = this.modal.find(SELECTORS.HEADER);\n this.headerPromise = $.Deferred();\n this.title = this.header.find(SELECTORS.TITLE);\n this.titlePromise = $.Deferred();\n this.body = this.modal.find(SELECTORS.BODY);\n this.bodyPromise = $.Deferred();\n this.footer = this.modal.find(SELECTORS.FOOTER);\n this.footerPromise = $.Deferred();\n this.hiddenSiblings = [];\n this.isAttached = false;\n this.bodyJS = null;\n this.footerJS = null;\n this.modalCount = Modal.modalCounter++;\n this.attachmentPoint = document.createElement('div');\n document.body.append(this.attachmentPoint);\n this.focusOnClose = null;\n this.templateJS = null;\n\n if (!this.root.is(SELECTORS.CONTAINER)) {\n Notification.exception({message: 'Element is not a modal container'});\n }\n\n if (!this.modal.length) {\n Notification.exception({message: 'Container does not contain a modal'});\n }\n\n if (!this.header.length) {\n Notification.exception({message: 'Modal is missing a header region'});\n }\n\n if (!this.title.length) {\n Notification.exception({message: 'Modal header is missing a title region'});\n }\n\n if (!this.body.length) {\n Notification.exception({message: 'Modal is missing a body region'});\n }\n\n if (!this.footer.length) {\n Notification.exception({message: 'Modal is missing a footer region'});\n }\n\n this.registerEventListeners();\n }\n\n /**\n * Register a modal with the legacy modal registry.\n *\n * This is provided to allow backwards-compatibility with existing code that uses the legacy modal registry.\n * It is not necessary to register modals for code only present in Moodle 4.3 and later.\n */\n static registerModalType() {\n if (!this.TYPE) {\n throw new Error(`Unknown modal type`, this);\n }\n\n if (!this.TEMPLATE) {\n throw new Error(`Unknown modal template`, this);\n }\n ModalRegistry.register(\n this.TYPE,\n this,\n this.TEMPLATE,\n );\n }\n\n /**\n * Create a new modal using the ModalFactory.\n * This is a shortcut to creating the modal.\n * Create a new modal using the supplied configuration.\n *\n * @param {ModalConfig} modalConfig\n * @returns {Promise}\n */\n static async create(modalConfig = {}) {\n const pendingModalPromise = new Pending('core/modal_factory:create');\n modalConfig.type = this.TYPE;\n\n const templateName = this._getTemplateName(modalConfig);\n const templateContext = modalConfig.templateContext || {};\n const {html, js} = await Templates.renderForPromise(templateName, templateContext);\n\n const modal = new this(html);\n if (js) {\n modal.setTemplateJS(js);\n }\n modal.configure(modalConfig);\n\n pendingModalPromise.resolve();\n\n return modal;\n }\n\n /**\n * A helper to get the template name for this modal.\n *\n * @param {ModalConfig} modalConfig\n * @returns {string}\n * @protected\n */\n static _getTemplateName(modalConfig) {\n if (modalConfig.template) {\n return modalConfig.template;\n }\n\n if (this.TEMPLATE) {\n return this.TEMPLATE;\n }\n\n if (ModalRegistry.has(this.TYPE)) {\n // Note: This is provided as an interim backwards-compatability layer and will be removed four releases after 4.3.\n window.console.warning(\n 'Use of core/modal_registry is deprecated. ' +\n 'Please define your modal template in a new static TEMPLATE property on your modal class.',\n );\n const config = ModalRegistry.get(this.TYPE);\n return config.template;\n }\n\n throw new Error(`Unable to determine template name for modal ${this.TYPE}`);\n }\n\n /**\n * Configure the modal.\n *\n * @param {ModalConfig} param0 The configuration options\n */\n configure({\n show = false,\n large = false,\n isVerticallyCentered = false,\n removeOnClose = false,\n scrollable = true,\n returnElement,\n title,\n body,\n footer,\n buttons = {},\n } = {}) {\n if (large) {\n this.setLarge();\n }\n\n if (isVerticallyCentered) {\n this.setVerticallyCentered();\n }\n\n // If configured remove the modal when hiding it.\n // Ideally this should be true, but we need to identify places that this breaks first.\n this.setRemoveOnClose(removeOnClose);\n this.setReturnElement(returnElement);\n this.setScrollable(scrollable);\n\n if (title !== undefined) {\n this.setTitle(title);\n }\n\n if (body !== undefined) {\n this.setBody(body);\n }\n\n if (footer !== undefined) {\n this.setFooter(footer);\n }\n\n Object.entries(buttons).forEach(([key, value]) => this.setButtonText(key, value));\n\n // If configured show the modal.\n if (show) {\n this.show();\n }\n }\n\n /**\n * Attach the modal to the correct part of the page.\n *\n * If it hasn't already been added it runs any\n * javascript that has been cached until now.\n *\n * @method attachToDOM\n */\n attachToDOM() {\n this.getAttachmentPoint().append(this._root);\n\n if (this.isAttached) {\n return;\n }\n\n FocusLock.trapFocus(this.root[0]);\n\n // If we'd cached any JS then we can run it how that the modal is\n // attached to the DOM.\n if (this.templateJS) {\n Templates.runTemplateJS(this.templateJS);\n this.templateJS = null;\n }\n\n if (this.bodyJS) {\n Templates.runTemplateJS(this.bodyJS);\n this.bodyJS = null;\n }\n\n if (this.footerJS) {\n Templates.runTemplateJS(this.footerJS);\n this.footerJS = null;\n }\n\n this.isAttached = true;\n }\n\n /**\n * Count the number of other visible modals (not including this one).\n *\n * @method countOtherVisibleModals\n * @return {int}\n */\n countOtherVisibleModals() {\n let count = 0;\n $('body').find(SELECTORS.CONTAINER).each((index, element) => {\n element = $(element);\n\n // If we haven't found ourself and the element is visible.\n if (!this.root.is(element) && element.hasClass('show')) {\n count++;\n }\n });\n\n return count;\n }\n\n /**\n * Get the modal backdrop.\n *\n * @method getBackdrop\n * @return {object} jQuery promise\n */\n getBackdrop() {\n if (!Modal.backdropPromise) {\n Modal.backdropPromise = Templates.render(TEMPLATES.BACKDROP, {})\n .then((html) => new ModalBackdrop($(html)))\n .catch(Notification.exception);\n }\n\n return Modal.backdropPromise;\n }\n\n /**\n * Get the root element of this modal.\n *\n * @method getRoot\n * @return {object} jQuery object\n */\n getRoot() {\n return this.root;\n }\n\n /**\n * Get the modal element of this modal.\n *\n * @method getModal\n * @return {object} jQuery object\n */\n getModal() {\n return this.modal;\n }\n\n /**\n * Get the modal title element.\n *\n * @method getTitle\n * @return {object} jQuery object\n */\n getTitle() {\n return this.title;\n }\n\n /**\n * Get the modal body element.\n *\n * @method getBody\n * @return {object} jQuery object\n */\n getBody() {\n return this.body;\n }\n\n /**\n * Get the modal footer element.\n *\n * @method getFooter\n * @return {object} jQuery object\n */\n getFooter() {\n return this.footer;\n }\n\n /**\n * Get a promise resolving to the title region.\n *\n * @method getTitlePromise\n * @return {Promise}\n */\n getTitlePromise() {\n return this.titlePromise;\n }\n\n /**\n * Get a promise resolving to the body region.\n *\n * @method getBodyPromise\n * @return {object} jQuery object\n */\n getBodyPromise() {\n return this.bodyPromise;\n }\n\n /**\n * Get a promise resolving to the footer region.\n *\n * @method getFooterPromise\n * @return {object} jQuery object\n */\n getFooterPromise() {\n return this.footerPromise;\n }\n\n /**\n * Get the unique modal count.\n *\n * @method getModalCount\n * @return {int}\n */\n getModalCount() {\n return this.modalCount;\n }\n\n /**\n * Set the modal title element.\n *\n * This method is overloaded to take either a string value for the title or a jQuery promise that is resolved with\n * HTML most commonly from a Str.get_string call.\n *\n * @method setTitle\n * @param {(string|object)} value The title string or jQuery promise which resolves to the title.\n */\n setTitle(value) {\n const title = this.getTitle();\n this.titlePromise = $.Deferred();\n\n this.asyncSet(value, title.html.bind(title))\n .then(() => {\n this.titlePromise.resolve(title);\n return;\n })\n .catch(Notification.exception);\n }\n\n /**\n * Set the modal body element.\n *\n * This method is overloaded to take either a string value for the body or a jQuery promise that is resolved with\n * HTML and Javascript most commonly from a Templates.render call.\n *\n * @method setBody\n * @param {(string|object)} value The body string or jQuery promise which resolves to the body.\n * @fires event:filterContentUpdated\n */\n setBody(value) {\n this.bodyPromise = $.Deferred();\n\n const body = this.getBody();\n\n if (typeof value === 'string') {\n // Just set the value if it's a string.\n body.html(value);\n FilterEvents.notifyFilterContentUpdated(body);\n this.getRoot().trigger(ModalEvents.bodyRendered, this);\n this.bodyPromise.resolve(body);\n } else {\n const modalPromise = new Pending(`amd-modal-js-pending-id-${this.getModalCount()}`);\n // Otherwise we assume it's a promise to be resolved with\n // html and javascript.\n let contentPromise = null;\n body.css('overflow', 'hidden');\n\n // Ensure that the `value` is a jQuery Promise.\n value = $.when(value);\n\n if (value.state() == 'pending') {\n // We're still waiting for the body promise to resolve so\n // let's show a loading icon.\n let height = body.innerHeight();\n if (height < 100) {\n height = 100;\n }\n\n body.animate({height: `${height}px`}, 150);\n\n body.html('');\n contentPromise = Templates.render(TEMPLATES.LOADING, {})\n .then((html) => {\n const loadingIcon = $(html).hide();\n body.html(loadingIcon);\n loadingIcon.fadeIn(150);\n\n // We only want the loading icon to fade out\n // when the content for the body has finished\n // loading.\n return $.when(loadingIcon.promise(), value);\n })\n .then((loadingIcon) => {\n // Once the content has finished loading and\n // the loading icon has been shown then we can\n // fade the icon away to reveal the content.\n return loadingIcon.fadeOut(100).promise();\n })\n .then(() => {\n return value;\n });\n } else {\n // The content is already loaded so let's just display\n // it to the user. No need for a loading icon.\n contentPromise = value;\n }\n\n // Now we can actually display the content.\n contentPromise.then((html, js) => {\n let result = null;\n\n if (this.isVisible()) {\n // If the modal is visible then we should display\n // the content gracefully for the user.\n body.css('opacity', 0);\n const currentHeight = body.innerHeight();\n body.html(html);\n // We need to clear any height values we've set here\n // in order to measure the height of the content being\n // added. This then allows us to animate the height\n // transition.\n body.css('height', '');\n const newHeight = body.innerHeight();\n body.css('height', `${currentHeight}px`);\n result = body.animate(\n {height: `${newHeight}px`, opacity: 1},\n {duration: 150, queue: false}\n ).promise();\n } else {\n // Since the modal isn't visible we can just immediately\n // set the content. No need to animate it.\n body.html(html);\n }\n\n if (js) {\n if (this.isAttached) {\n // If we're in the DOM then run the JS immediately.\n Templates.runTemplateJS(js);\n } else {\n // Otherwise cache it to be run when we're attached.\n this.bodyJS = js;\n }\n }\n\n return result;\n })\n .then((result) => {\n FilterEvents.notifyFilterContentUpdated(body);\n this.getRoot().trigger(ModalEvents.bodyRendered, this);\n return result;\n })\n .then(() => {\n this.bodyPromise.resolve(body);\n return;\n })\n .catch(Notification.exception)\n .always(() => {\n // When we're done displaying all of the content we need\n // to clear the custom values we've set here.\n body.css('height', '');\n body.css('overflow', '');\n body.css('opacity', '');\n modalPromise.resolve();\n\n return;\n });\n }\n }\n\n /**\n * Alternative to setBody() that can be used from non-Jquery modules\n *\n * @param {Promise} promise promise that returns {html, js} object\n * @return {Promise}\n */\n setBodyContent(promise) {\n // Call the leegacy API for now and pass it a jQuery Promise.\n // This is a non-spec feature of jQuery and cannot be produced with spec promises.\n // We can encourage people to migrate to this approach, and in future we can swap\n // it so that setBody() calls setBodyPromise().\n return promise.then(({html, js}) => this.setBody($.when(html, js)))\n .catch(exception => {\n this.hide();\n throw exception;\n });\n }\n\n /**\n * Set the modal footer element. The footer element is made visible, if it\n * isn't already.\n *\n * This method is overloaded to take either a string\n * value for the body or a jQuery promise that is resolved with HTML and Javascript\n * most commonly from a Templates.render call.\n *\n * @method setFooter\n * @param {(string|object)} value The footer string or jQuery promise\n */\n setFooter(value) {\n // Make sure the footer is visible.\n this.showFooter();\n this.footerPromise = $.Deferred();\n\n const footer = this.getFooter();\n\n if (typeof value === 'string') {\n // Just set the value if it's a string.\n footer.html(value);\n this.footerPromise.resolve(footer);\n } else {\n // Otherwise we assume it's a promise to be resolved with\n // html and javascript.\n Templates.render(TEMPLATES.LOADING, {})\n .then((html) => {\n footer.html(html);\n\n return value;\n })\n .then((html, js) => {\n footer.html(html);\n\n if (js) {\n if (this.isAttached) {\n // If we're in the DOM then run the JS immediately.\n Templates.runTemplateJS(js);\n } else {\n // Otherwise cache it to be run when we're attached.\n this.footerJS = js;\n }\n }\n\n return footer;\n })\n .then((footer) => {\n this.footerPromise.resolve(footer);\n this.showFooter();\n return;\n })\n .catch(Notification.exception);\n }\n }\n\n /**\n * Check if the footer has any content in it.\n *\n * @method hasFooterContent\n * @return {bool}\n */\n hasFooterContent() {\n return this.getFooter().children().length ? true : false;\n }\n\n /**\n * Hide the footer element.\n *\n * @method hideFooter\n */\n hideFooter() {\n this.getFooter().addClass('hidden');\n }\n\n /**\n * Show the footer element.\n *\n * @method showFooter\n */\n showFooter() {\n this.getFooter().removeClass('hidden');\n }\n\n /**\n * Mark the modal as a large modal.\n *\n * @method setLarge\n */\n setLarge() {\n if (this.isLarge()) {\n return;\n }\n\n this.getModal().addClass('modal-lg');\n }\n\n /**\n * Mark the modal as a centered modal.\n *\n * @method setVerticallyCentered\n */\n setVerticallyCentered() {\n if (this.isVerticallyCentered()) {\n return;\n }\n this.getModal().addClass('modal-dialog-centered');\n }\n\n /**\n * Check if the modal is a large modal.\n *\n * @method isLarge\n * @return {bool}\n */\n isLarge() {\n return this.getModal().hasClass('modal-lg');\n }\n\n /**\n * Check if the modal is vertically centered.\n *\n * @method isVerticallyCentered\n * @return {bool}\n */\n isVerticallyCentered() {\n return this.getModal().hasClass('modal-dialog-centered');\n }\n\n /**\n * Mark the modal as a small modal.\n *\n * @method setSmall\n */\n setSmall() {\n if (this.isSmall()) {\n return;\n }\n\n this.getModal().removeClass('modal-lg');\n }\n\n /**\n * Check if the modal is a small modal.\n *\n * @method isSmall\n * @return {bool}\n */\n isSmall() {\n return !this.getModal().hasClass('modal-lg');\n }\n\n /**\n * Set this modal to be scrollable or not.\n *\n * @method setScrollable\n * @param {bool} value Whether the modal is scrollable or not\n */\n setScrollable(value) {\n if (!value) {\n this.getModal()[0].classList.remove('modal-dialog-scrollable');\n return;\n }\n\n this.getModal()[0].classList.add('modal-dialog-scrollable');\n }\n\n\n /**\n * Determine the highest z-index value currently on the page.\n *\n * @method calculateZIndex\n * @return {int}\n */\n calculateZIndex() {\n const items = $(`${SELECTORS.DIALOG}, ${SELECTORS.MENU_BAR}, ${SELECTORS.HAS_Z_INDEX}`);\n let zIndex = parseInt(this.root.css('z-index'));\n\n items.each((index, item) => {\n item = $(item);\n if (!item.is(':visible')) {\n // Do not include items which are not visible in the z-index calculation.\n // This is important because some dialogues are not removed from the DOM.\n return;\n }\n // Note that webkit browsers won't return the z-index value from the CSS stylesheet\n // if the element doesn't have a position specified. Instead it'll return \"auto\".\n const itemZIndex = item.css('z-index') ? parseInt(item.css('z-index')) : 0;\n\n if (itemZIndex > zIndex) {\n zIndex = itemZIndex;\n }\n });\n\n return zIndex;\n }\n\n /**\n * Check if this modal is visible.\n *\n * @method isVisible\n * @return {bool}\n */\n isVisible() {\n return this.root.hasClass('show');\n }\n\n /**\n * Check if this modal has focus.\n *\n * @method hasFocus\n * @return {bool}\n */\n hasFocus() {\n const target = $(document.activeElement);\n return this.root.is(target) || this.root.has(target).length;\n }\n\n /**\n * Check if this modal has CSS transitions applied.\n *\n * @method hasTransitions\n * @return {bool}\n */\n hasTransitions() {\n return this.getRoot().hasClass('fade');\n }\n\n /**\n * Gets the jQuery wrapped node that the Modal should be attached to.\n *\n * @returns {jQuery}\n */\n getAttachmentPoint() {\n return $(Fullscreen.getElement() || this.attachmentPoint);\n }\n\n /**\n * Display this modal. The modal will be attached to the DOM if it hasn't\n * already been.\n *\n * @method show\n * @returns {Promise}\n */\n show() {\n if (this.isVisible()) {\n return $.Deferred().resolve();\n }\n\n const pendingPromise = new Pending('core/modal:show');\n\n if (this.hasFooterContent()) {\n this.showFooter();\n } else {\n this.hideFooter();\n }\n\n this.attachToDOM();\n\n // If the focusOnClose was not set. Set the focus back to triggered element.\n if (!this.focusOnClose && document.activeElement) {\n this.focusOnClose = document.activeElement;\n }\n\n return this.getBackdrop()\n .then((backdrop) => {\n const currentIndex = this.calculateZIndex();\n const newIndex = currentIndex + 2;\n const newBackdropIndex = newIndex - 1;\n this.root.css('z-index', newIndex);\n backdrop.setZIndex(newBackdropIndex);\n backdrop.show();\n\n this.root.removeClass('hide').addClass('show');\n this.accessibilityShow();\n this.getModal().focus();\n $('body').addClass('modal-open');\n this.root.trigger(ModalEvents.shown, this);\n\n return;\n })\n .then(pendingPromise.resolve);\n }\n\n /**\n * Hide this modal if it does not contain a form.\n *\n * @method hideIfNotForm\n */\n hideIfNotForm() {\n const formElement = this.modal.find(SELECTORS.FORM);\n if (formElement.length == 0) {\n this.hide();\n }\n }\n\n /**\n * Hide this modal.\n *\n * @method hide\n */\n hide() {\n this.getBackdrop().done((backdrop) => {\n FocusLock.untrapFocus();\n\n if (!this.countOtherVisibleModals()) {\n // Hide the backdrop if we're the last open modal.\n backdrop.hide();\n $('body').removeClass('modal-open');\n }\n\n const currentIndex = parseInt(this.root.css('z-index'));\n this.root.css('z-index', '');\n backdrop.setZIndex(currentIndex - 3);\n\n this.accessibilityHide();\n\n if (this.hasTransitions()) {\n // Wait for CSS transitions to complete before hiding the element.\n this.getRoot().one('transitionend webkitTransitionEnd oTransitionEnd', () => {\n this.getRoot().removeClass('show').addClass('hide');\n });\n } else {\n this.getRoot().removeClass('show').addClass('hide');\n }\n\n // Ensure the modal is moved onto the body node if it is still attached to the DOM.\n if ($(document.body).find(this.getRoot()).length) {\n $(document.body).append(this.getRoot());\n }\n\n // Closes popover elements that are inside the modal at the time the modal is closed.\n this.getRoot().find('[data-bs-toggle=\"popover\"]').each(function() {\n document.getElementById(this.getAttribute('aria-describedby'))?.remove();\n });\n\n this.root.trigger(ModalEvents.hidden, this);\n });\n }\n\n /**\n * Remove this modal from the DOM.\n *\n * @method destroy\n */\n destroy() {\n this.hide();\n removeToastRegion(this.getBody().get(0));\n this.root.remove();\n this.root.trigger(ModalEvents.destroyed, this);\n this.attachmentPoint.remove();\n }\n\n /**\n * Sets the appropriate aria attributes on this dialogue and the other\n * elements in the DOM to ensure that screen readers are able to navigate\n * the dialogue popup correctly.\n *\n * @method accessibilityShow\n */\n accessibilityShow() {\n // Make us visible to screen readers.\n Aria.unhide(this.root.get());\n\n // Hide siblings.\n Aria.hideSiblings(this.root.get()[0]);\n }\n\n /**\n * Restores the aria visibility on the DOM elements changed when displaying\n * the dialogue popup and makes the dialogue aria hidden to allow screen\n * readers to navigate the main page correctly when the dialogue is closed.\n *\n * @method accessibilityHide\n */\n accessibilityHide() {\n // Unhide siblings.\n Aria.unhideSiblings(this.root.get()[0]);\n\n // Hide this modal.\n Aria.hide(this.root.get());\n }\n\n /**\n * Set up all of the event handling for the modal.\n *\n * @method registerEventListeners\n */\n registerEventListeners() {\n this.getRoot().on('keydown', (e) => {\n if (!this.isVisible()) {\n return;\n }\n\n if (e.keyCode == KeyCodes.escape) {\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n }\n });\n\n // Listen for clicks on the modal container.\n this.getRoot().click((e) => {\n // If the click wasn't inside the modal element then we should\n // hide the modal.\n if (!$(e.target).closest(SELECTORS.MODAL).length) {\n // The check above fails to detect the click was inside the modal when the DOM tree is already changed.\n // So, we check if we can still find the container element or not. If not, then the DOM tree is changed.\n // It's best not to hide the modal in that case.\n if ($(e.target).closest(SELECTORS.CONTAINER).length) {\n const outsideClickEvent = $.Event(ModalEvents.outsideClick);\n this.getRoot().trigger(outsideClickEvent, this);\n\n if (!outsideClickEvent.isDefaultPrevented()) {\n this.hideIfNotForm();\n }\n }\n }\n });\n\n CustomEvents.define(this.getModal(), [CustomEvents.events.activate]);\n this.getModal().on(CustomEvents.events.activate, SELECTORS.HIDE, (e, data) => {\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n data.originalEvent.preventDefault();\n });\n\n this.getRoot().on(ModalEvents.hidden, () => {\n if (this.focusOnClose) {\n // Focus on the element that actually triggers the modal.\n this.focusOnClose.focus();\n }\n });\n }\n\n /**\n * Register a listener to close the dialogue when the cancel button is pressed.\n *\n * @method registerCloseOnCancel\n */\n registerCloseOnCancel() {\n // Handle the clicking of the Cancel button.\n this.getModal().on(CustomEvents.events.activate, this.getActionSelector('cancel'), (e, data) => {\n const cancelEvent = $.Event(ModalEvents.cancel);\n this.getRoot().trigger(cancelEvent, this);\n\n if (!cancelEvent.isDefaultPrevented()) {\n data.originalEvent.preventDefault();\n\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n }\n });\n }\n\n /**\n * Register a listener to close the dialogue when the save button is pressed.\n *\n * @method registerCloseOnSave\n */\n registerCloseOnSave() {\n // Handle the clicking of the Cancel button.\n this.getModal().on(CustomEvents.events.activate, this.getActionSelector('save'), (e, data) => {\n const saveEvent = $.Event(ModalEvents.save);\n this.getRoot().trigger(saveEvent, this);\n\n if (!saveEvent.isDefaultPrevented()) {\n data.originalEvent.preventDefault();\n\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n }\n });\n }\n\n\n /**\n * Register a listener to close the dialogue when the delete button is pressed.\n *\n * @method registerCloseOnDelete\n */\n registerCloseOnDelete() {\n // Handle the clicking of the Cancel button.\n this.getModal().on(CustomEvents.events.activate, this.getActionSelector('delete'), (e, data) => {\n const deleteEvent = $.Event(ModalEvents.delete);\n this.getRoot().trigger(deleteEvent, this);\n\n if (!deleteEvent.isDefaultPrevented()) {\n data.originalEvent.preventDefault();\n\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n }\n });\n }\n\n /**\n * Set or resolve and set the value using the function.\n *\n * @method asyncSet\n * @param {(string|object)} value The string or jQuery promise.\n * @param {function} setFunction The setter\n * @return {Promise}\n */\n asyncSet(value, setFunction) {\n const getWrappedValue = (value) => {\n if (value instanceof Promise) {\n return $.when(value);\n }\n\n if (typeof value !== 'object' || !value.hasOwnProperty('then')) {\n return $.Deferred().resolve(value);\n }\n\n return value;\n };\n\n return getWrappedValue(value)\n .then((content) => setFunction(content))\n .catch(Notification.exception);\n }\n\n /**\n * Set the title text of a button.\n *\n * This method is overloaded to take either a string value for the button title or a jQuery promise that is resolved with\n * text most commonly from a Str.get_string call.\n *\n * @param {DOMString} action The action of the button\n * @param {(String|object)} value The button text, or a promise which will resolve to it\n * @returns {Promise}\n */\n setButtonText(action, value) {\n const button = this.getFooter().find(this.getActionSelector(action));\n\n if (!button) {\n throw new Error(\"Unable to find the '\" + action + \"' button\");\n }\n\n return this.asyncSet(value, button.text.bind(button));\n }\n\n /**\n * Get the Selector for an action.\n *\n * @param {String} action\n * @returns {DOMString}\n */\n getActionSelector(action) {\n return \"[data-action='\" + action + \"']\";\n }\n\n /**\n * Set the flag to remove the modal from the DOM on close.\n *\n * @param {Boolean} remove\n */\n setRemoveOnClose(remove) {\n this.removeOnClose = remove;\n }\n\n /**\n * Set the return element for the modal.\n *\n * @param {Element|jQuery} element Element to focus when the modal is closed\n */\n setReturnElement(element) {\n this.focusOnClose = element;\n }\n\n /**\n * Set the a button enabled or disabled.\n *\n * @param {DOMString} action The action of the button\n * @param {Boolean} disabled the new disabled value\n */\n setButtonDisabled(action, disabled) {\n const button = this.getFooter().find(this.getActionSelector(action));\n\n if (!button) {\n throw new Error(\"Unable to find the '\" + action + \"' button\");\n }\n if (disabled) {\n button.attr('disabled', '');\n } else {\n button.removeAttr('disabled');\n }\n }\n\n /**\n * Set the template JS for this modal.\n * @param {String} js The JavaScript to run when the modal is attached to the DOM.\n */\n setTemplateJS(js) {\n this.templateJS = js;\n }\n}\n"],"names":["SELECTORS","TEMPLATES","Modal","root","this","_root","filter","constructor","modal","find","header","headerPromise","$","Deferred","title","titlePromise","body","bodyPromise","footer","footerPromise","hiddenSiblings","isAttached","bodyJS","footerJS","modalCount","modalCounter","attachmentPoint","document","createElement","append","focusOnClose","templateJS","is","Notification","exception","message","length","registerEventListeners","TYPE","Error","TEMPLATE","ModalRegistry","register","modalConfig","pendingModalPromise","Pending","type","templateName","_getTemplateName","templateContext","html","js","Templates","renderForPromise","setTemplateJS","configure","resolve","template","has","window","console","warning","get","show","large","isVerticallyCentered","removeOnClose","scrollable","returnElement","buttons","setLarge","setVerticallyCentered","setRemoveOnClose","setReturnElement","setScrollable","undefined","setTitle","setBody","setFooter","Object","entries","forEach","_ref","key","value","setButtonText","attachToDOM","getAttachmentPoint","FocusLock","trapFocus","runTemplateJS","countOtherVisibleModals","count","each","index","element","hasClass","getBackdrop","backdropPromise","render","then","ModalBackdrop","catch","getRoot","getModal","getTitle","getBody","getFooter","getTitlePromise","getBodyPromise","getFooterPromise","getModalCount","asyncSet","bind","FilterEvents","notifyFilterContentUpdated","trigger","ModalEvents","bodyRendered","modalPromise","contentPromise","css","when","state","height","innerHeight","animate","loadingIcon","hide","fadeIn","promise","fadeOut","result","isVisible","currentHeight","newHeight","opacity","duration","queue","always","setBodyContent","_ref2","showFooter","hasFooterContent","children","hideFooter","addClass","removeClass","isLarge","setSmall","isSmall","classList","add","remove","calculateZIndex","items","zIndex","parseInt","item","itemZIndex","hasFocus","target","activeElement","hasTransitions","Fullscreen","getElement","pendingPromise","backdrop","newIndex","newBackdropIndex","setZIndex","accessibilityShow","focus","shown","hideIfNotForm","done","untrapFocus","currentIndex","accessibilityHide","one","getElementById","getAttribute","hidden","destroy","destroyed","Aria","unhide","hideSiblings","unhideSiblings","on","e","keyCode","KeyCodes","escape","click","closest","outsideClickEvent","Event","outsideClick","isDefaultPrevented","CustomEvents","define","events","activate","data","originalEvent","preventDefault","registerCloseOnCancel","getActionSelector","cancelEvent","cancel","registerCloseOnSave","saveEvent","save","registerCloseOnDelete","deleteEvent","delete","setFunction","Promise","hasOwnProperty","getWrappedValue","content","action","button","text","setButtonDisabled","disabled","attr","removeAttr"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"modal.min.js","sources":["../src/modal.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 * Contain the logic for modals.\n *\n * @module core/modal\n * @copyright 2016 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as Templates from 'core/templates';\nimport * as Notification from 'core/notification';\nimport * as KeyCodes from 'core/key_codes';\nimport ModalBackdrop from 'core/modal_backdrop';\nimport ModalEvents from 'core/modal_events';\nimport * as ModalRegistry from 'core/modal_registry';\nimport Pending from 'core/pending';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as FilterEvents from 'core_filters/events';\nimport * as FocusLock from 'core/local/aria/focuslock';\nimport * as Aria from 'core/aria';\nimport * as Fullscreen from 'core/fullscreen';\nimport {removeToastRegion} from './toast';\nimport {dispatchEvent} from 'core/event_dispatcher';\n\n/**\n * A configuration to provide to the modal.\n *\n * @typedef {Object} ModalConfig\n *\n * @property {string} [type] The type of modal to create.\n * @property {string|Promise} [title] The title of the modal.\n * @property {string|Promise} [body] The body of the modal.\n * @property {string|Promise} [footer] The footer of the modal.\n * @property {boolean} [show=false] Whether to show the modal immediately.\n * @property {boolean} [scrollable=true] Whether the modal should be scrollable.\n * @property {boolean} [removeOnClose=true] Whether the modal should be removed from the DOM when it is closed.\n * @property {Element|jQuery} [returnElement] The element to focus when closing the modal.\n * @property {boolean} [large=false] Whether the modal should be a large modal.\n * @property {boolean} [isVerticallyCentered=false] Whether the modal should be vertically centered.\n * @property {object} [buttons={}] The buttons to display in the footer as a key => title pair.\n */\n\nconst SELECTORS = {\n CONTAINER: '[data-region=\"modal-container\"]',\n MODAL: '[data-region=\"modal\"]',\n HEADER: '[data-region=\"header\"]',\n TITLE: '[data-region=\"title\"]',\n BODY: '[data-region=\"body\"]',\n FOOTER: '[data-region=\"footer\"]',\n HIDE: '[data-action=\"hide\"]',\n DIALOG: '[role=dialog]',\n FORM: 'form',\n MENU_BAR: '[role=menubar]',\n HAS_Z_INDEX: '.moodle-has-zindex',\n CAN_RECEIVE_FOCUS: 'input:not([type=\"hidden\"]), a[href], button, textarea, select, [tabindex]',\n};\n\nconst TEMPLATES = {\n LOADING: 'core/loading',\n BACKDROP: 'core/modal_backdrop',\n};\n\nexport default class Modal {\n /** @var {string} The type of modal */\n static TYPE = 'default';\n\n /** @var {string} The template to use for this modal */\n static TEMPLATE = 'core/modal';\n\n /** @var {Promise} Module singleton for the backdrop to be reused by all Modal instances */\n static backdropPromise = null;\n\n /**\n * @var {Number} A counter that gets incremented for each modal created.\n * This can be used to generate unique values for the modals.\n */\n static modalCounter = 0;\n\n /**\n * Getter method for .root element.\n * @return {object} jQuery object\n */\n get root() {\n return $(this._root.filter(SELECTORS.CONTAINER));\n }\n\n /**\n * Setter method for .root element.\n * @param {object} root jQuery object\n */\n set root(root) {\n this._root = root;\n }\n\n /**\n * Constructor for the Modal.\n *\n * @param {HTMLElement} root The HTMLElement at the root of the Modal content\n */\n constructor(root) {\n this.root = $(root);\n\n this.modal = this.root.find(SELECTORS.MODAL);\n this.header = this.modal.find(SELECTORS.HEADER);\n this.headerPromise = $.Deferred();\n this.title = this.header.find(SELECTORS.TITLE);\n this.titlePromise = $.Deferred();\n this.body = this.modal.find(SELECTORS.BODY);\n this.bodyPromise = $.Deferred();\n this.footer = this.modal.find(SELECTORS.FOOTER);\n this.footerPromise = $.Deferred();\n this.hiddenSiblings = [];\n this.isAttached = false;\n this.bodyJS = null;\n this.footerJS = null;\n this.modalCount = Modal.modalCounter++;\n this.attachmentPoint = document.createElement('div');\n document.body.append(this.attachmentPoint);\n this.focusOnClose = null;\n this.templateJS = null;\n\n if (!this.root.is(SELECTORS.CONTAINER)) {\n Notification.exception({message: 'Element is not a modal container'});\n }\n\n if (!this.modal.length) {\n Notification.exception({message: 'Container does not contain a modal'});\n }\n\n if (!this.header.length) {\n Notification.exception({message: 'Modal is missing a header region'});\n }\n\n if (!this.title.length) {\n Notification.exception({message: 'Modal header is missing a title region'});\n }\n\n if (!this.body.length) {\n Notification.exception({message: 'Modal is missing a body region'});\n }\n\n if (!this.footer.length) {\n Notification.exception({message: 'Modal is missing a footer region'});\n }\n\n this.registerEventListeners();\n }\n\n /**\n * Register a modal with the legacy modal registry.\n *\n * This is provided to allow backwards-compatibility with existing code that uses the legacy modal registry.\n * It is not necessary to register modals for code only present in Moodle 4.3 and later.\n */\n static registerModalType() {\n if (!this.TYPE) {\n throw new Error(`Unknown modal type`, this);\n }\n\n if (!this.TEMPLATE) {\n throw new Error(`Unknown modal template`, this);\n }\n ModalRegistry.register(\n this.TYPE,\n this,\n this.TEMPLATE,\n );\n }\n\n /**\n * Create a new modal using the ModalFactory.\n * This is a shortcut to creating the modal.\n * Create a new modal using the supplied configuration.\n *\n * @param {ModalConfig} modalConfig\n * @returns {Promise}\n */\n static async create(modalConfig = {}) {\n const pendingModalPromise = new Pending('core/modal_factory:create');\n modalConfig.type = this.TYPE;\n\n const templateName = this._getTemplateName(modalConfig);\n const templateContext = modalConfig.templateContext || {};\n const {html, js} = await Templates.renderForPromise(templateName, templateContext);\n\n const modal = new this(html);\n if (js) {\n modal.setTemplateJS(js);\n }\n modal.configure(modalConfig);\n\n pendingModalPromise.resolve();\n\n return modal;\n }\n\n /**\n * A helper to get the template name for this modal.\n *\n * @param {ModalConfig} modalConfig\n * @returns {string}\n * @protected\n */\n static _getTemplateName(modalConfig) {\n if (modalConfig.template) {\n return modalConfig.template;\n }\n\n if (this.TEMPLATE) {\n return this.TEMPLATE;\n }\n\n if (ModalRegistry.has(this.TYPE)) {\n // Note: This is provided as an interim backwards-compatability layer and will be removed four releases after 4.3.\n window.console.warning(\n 'Use of core/modal_registry is deprecated. ' +\n 'Please define your modal template in a new static TEMPLATE property on your modal class.',\n );\n const config = ModalRegistry.get(this.TYPE);\n return config.template;\n }\n\n throw new Error(`Unable to determine template name for modal ${this.TYPE}`);\n }\n\n /**\n * Configure the modal.\n *\n * @param {ModalConfig} param0 The configuration options\n */\n configure({\n show = false,\n large = false,\n isVerticallyCentered = false,\n removeOnClose = false,\n scrollable = true,\n returnElement,\n title,\n body,\n footer,\n buttons = {},\n } = {}) {\n if (large) {\n this.setLarge();\n }\n\n if (isVerticallyCentered) {\n this.setVerticallyCentered();\n }\n\n // If configured remove the modal when hiding it.\n // Ideally this should be true, but we need to identify places that this breaks first.\n this.setRemoveOnClose(removeOnClose);\n this.setReturnElement(returnElement);\n this.setScrollable(scrollable);\n\n if (title !== undefined) {\n this.setTitle(title);\n }\n\n if (body !== undefined) {\n this.setBody(body);\n }\n\n if (footer !== undefined) {\n this.setFooter(footer);\n }\n\n Object.entries(buttons).forEach(([key, value]) => this.setButtonText(key, value));\n\n // If configured show the modal.\n if (show) {\n this.show();\n }\n }\n\n /**\n * Attach the modal to the correct part of the page.\n *\n * If it hasn't already been added it runs any\n * javascript that has been cached until now.\n *\n * @method attachToDOM\n */\n attachToDOM() {\n this.getAttachmentPoint().append(this._root);\n\n if (this.isAttached) {\n return;\n }\n\n FocusLock.trapFocus(this.root[0]);\n\n // If we'd cached any JS then we can run it how that the modal is\n // attached to the DOM.\n if (this.templateJS) {\n Templates.runTemplateJS(this.templateJS);\n this.templateJS = null;\n }\n\n if (this.bodyJS) {\n Templates.runTemplateJS(this.bodyJS);\n this.bodyJS = null;\n }\n\n if (this.footerJS) {\n Templates.runTemplateJS(this.footerJS);\n this.footerJS = null;\n }\n\n this.isAttached = true;\n }\n\n /**\n * Count the number of other visible modals (not including this one).\n *\n * @method countOtherVisibleModals\n * @return {int}\n */\n countOtherVisibleModals() {\n let count = 0;\n $('body').find(SELECTORS.CONTAINER).each((index, element) => {\n element = $(element);\n\n // If we haven't found ourself and the element is visible.\n if (!this.root.is(element) && element.hasClass('show')) {\n count++;\n }\n });\n\n return count;\n }\n\n /**\n * Get the modal backdrop.\n *\n * @method getBackdrop\n * @return {object} jQuery promise\n */\n getBackdrop() {\n if (!Modal.backdropPromise) {\n Modal.backdropPromise = Templates.render(TEMPLATES.BACKDROP, {})\n .then((html) => new ModalBackdrop($(html)))\n .catch(Notification.exception);\n }\n\n return Modal.backdropPromise;\n }\n\n /**\n * Get the root element of this modal.\n *\n * @method getRoot\n * @return {object} jQuery object\n */\n getRoot() {\n return this.root;\n }\n\n /**\n * Get the modal element of this modal.\n *\n * @method getModal\n * @return {object} jQuery object\n */\n getModal() {\n return this.modal;\n }\n\n /**\n * Get the modal title element.\n *\n * @method getTitle\n * @return {object} jQuery object\n */\n getTitle() {\n return this.title;\n }\n\n /**\n * Get the modal body element.\n *\n * @method getBody\n * @return {object} jQuery object\n */\n getBody() {\n return this.body;\n }\n\n /**\n * Get the modal footer element.\n *\n * @method getFooter\n * @return {object} jQuery object\n */\n getFooter() {\n return this.footer;\n }\n\n /**\n * Get a promise resolving to the title region.\n *\n * @method getTitlePromise\n * @return {Promise}\n */\n getTitlePromise() {\n return this.titlePromise;\n }\n\n /**\n * Get a promise resolving to the body region.\n *\n * @method getBodyPromise\n * @return {object} jQuery object\n */\n getBodyPromise() {\n return this.bodyPromise;\n }\n\n /**\n * Get a promise resolving to the footer region.\n *\n * @method getFooterPromise\n * @return {object} jQuery object\n */\n getFooterPromise() {\n return this.footerPromise;\n }\n\n /**\n * Get the unique modal count.\n *\n * @method getModalCount\n * @return {int}\n */\n getModalCount() {\n return this.modalCount;\n }\n\n /**\n * Set the modal title element.\n *\n * This method is overloaded to take either a string value for the title or a jQuery promise that is resolved with\n * HTML most commonly from a Str.get_string call.\n *\n * @method setTitle\n * @param {(string|object)} value The title string or jQuery promise which resolves to the title.\n */\n setTitle(value) {\n const title = this.getTitle();\n this.titlePromise = $.Deferred();\n\n this.asyncSet(value, title.html.bind(title))\n .then(() => {\n this.titlePromise.resolve(title);\n return;\n })\n .catch(Notification.exception);\n }\n\n /**\n * Set the modal body element.\n *\n * This method is overloaded to take either a string value for the body or a jQuery promise that is resolved with\n * HTML and Javascript most commonly from a Templates.render call.\n *\n * @method setBody\n * @param {(string|object)} value The body string or jQuery promise which resolves to the body.\n * @fires event:filterContentUpdated\n */\n setBody(value) {\n this.bodyPromise = $.Deferred();\n\n const body = this.getBody();\n\n if (typeof value === 'string') {\n // Just set the value if it's a string.\n body.html(value);\n FilterEvents.notifyFilterContentUpdated(body);\n this.getRoot().trigger(ModalEvents.bodyRendered, this);\n this.bodyPromise.resolve(body);\n } else {\n const modalPromise = new Pending(`amd-modal-js-pending-id-${this.getModalCount()}`);\n // Otherwise we assume it's a promise to be resolved with\n // html and javascript.\n let contentPromise = null;\n body.css('overflow', 'hidden');\n\n // Ensure that the `value` is a jQuery Promise.\n value = $.when(value);\n\n if (value.state() == 'pending') {\n // We're still waiting for the body promise to resolve so\n // let's show a loading icon.\n let height = body.innerHeight();\n if (height < 100) {\n height = 100;\n }\n\n body.animate({height: `${height}px`}, 150);\n\n body.html('');\n contentPromise = Templates.render(TEMPLATES.LOADING, {})\n .then((html) => {\n const loadingIcon = $(html).hide();\n body.html(loadingIcon);\n loadingIcon.fadeIn(150);\n\n // We only want the loading icon to fade out\n // when the content for the body has finished\n // loading.\n return $.when(loadingIcon.promise(), value);\n })\n .then((loadingIcon) => {\n // Once the content has finished loading and\n // the loading icon has been shown then we can\n // fade the icon away to reveal the content.\n return loadingIcon.fadeOut(100).promise();\n })\n .then(() => {\n return value;\n });\n } else {\n // The content is already loaded so let's just display\n // it to the user. No need for a loading icon.\n contentPromise = value;\n }\n\n // Now we can actually display the content.\n contentPromise.then((html, js) => {\n let result = null;\n\n if (this.isVisible()) {\n // If the modal is visible then we should display\n // the content gracefully for the user.\n body.css('opacity', 0);\n const currentHeight = body.innerHeight();\n body.html(html);\n // We need to clear any height values we've set here\n // in order to measure the height of the content being\n // added. This then allows us to animate the height\n // transition.\n body.css('height', '');\n const newHeight = body.innerHeight();\n body.css('height', `${currentHeight}px`);\n result = body.animate(\n {height: `${newHeight}px`, opacity: 1},\n {duration: 150, queue: false}\n ).promise();\n } else {\n // Since the modal isn't visible we can just immediately\n // set the content. No need to animate it.\n body.html(html);\n }\n\n if (js) {\n if (this.isAttached) {\n // If we're in the DOM then run the JS immediately.\n Templates.runTemplateJS(js);\n } else {\n // Otherwise cache it to be run when we're attached.\n this.bodyJS = js;\n }\n }\n\n return result;\n })\n .then((result) => {\n FilterEvents.notifyFilterContentUpdated(body);\n this.getRoot().trigger(ModalEvents.bodyRendered, this);\n dispatchEvent('core/modal:bodyRendered', this, this.modal[0]);\n return result;\n })\n .then(() => {\n this.bodyPromise.resolve(body);\n return;\n })\n .catch(Notification.exception)\n .always(() => {\n // When we're done displaying all of the content we need\n // to clear the custom values we've set here.\n body.css('height', '');\n body.css('overflow', '');\n body.css('opacity', '');\n modalPromise.resolve();\n\n return;\n });\n }\n }\n\n /**\n * Alternative to setBody() that can be used from non-Jquery modules\n *\n * @param {Promise} promise promise that returns {html, js} object\n * @return {Promise}\n */\n setBodyContent(promise) {\n // Call the leegacy API for now and pass it a jQuery Promise.\n // This is a non-spec feature of jQuery and cannot be produced with spec promises.\n // We can encourage people to migrate to this approach, and in future we can swap\n // it so that setBody() calls setBodyPromise().\n return promise.then(({html, js}) => this.setBody($.when(html, js)))\n .catch(exception => {\n this.hide();\n throw exception;\n });\n }\n\n /**\n * Set the modal footer element. The footer element is made visible, if it\n * isn't already.\n *\n * This method is overloaded to take either a string\n * value for the body or a jQuery promise that is resolved with HTML and Javascript\n * most commonly from a Templates.render call.\n *\n * @method setFooter\n * @param {(string|object)} value The footer string or jQuery promise\n */\n setFooter(value) {\n // Make sure the footer is visible.\n this.showFooter();\n this.footerPromise = $.Deferred();\n\n const footer = this.getFooter();\n\n if (typeof value === 'string') {\n // Just set the value if it's a string.\n footer.html(value);\n this.footerPromise.resolve(footer);\n } else {\n // Otherwise we assume it's a promise to be resolved with\n // html and javascript.\n Templates.render(TEMPLATES.LOADING, {})\n .then((html) => {\n footer.html(html);\n\n return value;\n })\n .then((html, js) => {\n footer.html(html);\n\n if (js) {\n if (this.isAttached) {\n // If we're in the DOM then run the JS immediately.\n Templates.runTemplateJS(js);\n } else {\n // Otherwise cache it to be run when we're attached.\n this.footerJS = js;\n }\n }\n\n return footer;\n })\n .then((footer) => {\n this.footerPromise.resolve(footer);\n this.showFooter();\n return;\n })\n .catch(Notification.exception);\n }\n }\n\n /**\n * Check if the footer has any content in it.\n *\n * @method hasFooterContent\n * @return {bool}\n */\n hasFooterContent() {\n return this.getFooter().children().length ? true : false;\n }\n\n /**\n * Hide the footer element.\n *\n * @method hideFooter\n */\n hideFooter() {\n this.getFooter().addClass('hidden');\n }\n\n /**\n * Show the footer element.\n *\n * @method showFooter\n */\n showFooter() {\n this.getFooter().removeClass('hidden');\n }\n\n /**\n * Mark the modal as a large modal.\n *\n * @method setLarge\n */\n setLarge() {\n if (this.isLarge()) {\n return;\n }\n\n this.getModal().addClass('modal-lg');\n }\n\n /**\n * Mark the modal as a centered modal.\n *\n * @method setVerticallyCentered\n */\n setVerticallyCentered() {\n if (this.isVerticallyCentered()) {\n return;\n }\n this.getModal().addClass('modal-dialog-centered');\n }\n\n /**\n * Check if the modal is a large modal.\n *\n * @method isLarge\n * @return {bool}\n */\n isLarge() {\n return this.getModal().hasClass('modal-lg');\n }\n\n /**\n * Check if the modal is vertically centered.\n *\n * @method isVerticallyCentered\n * @return {bool}\n */\n isVerticallyCentered() {\n return this.getModal().hasClass('modal-dialog-centered');\n }\n\n /**\n * Mark the modal as a small modal.\n *\n * @method setSmall\n */\n setSmall() {\n if (this.isSmall()) {\n return;\n }\n\n this.getModal().removeClass('modal-lg');\n }\n\n /**\n * Check if the modal is a small modal.\n *\n * @method isSmall\n * @return {bool}\n */\n isSmall() {\n return !this.getModal().hasClass('modal-lg');\n }\n\n /**\n * Set this modal to be scrollable or not.\n *\n * @method setScrollable\n * @param {bool} value Whether the modal is scrollable or not\n */\n setScrollable(value) {\n if (!value) {\n this.getModal()[0].classList.remove('modal-dialog-scrollable');\n return;\n }\n\n this.getModal()[0].classList.add('modal-dialog-scrollable');\n }\n\n\n /**\n * Determine the highest z-index value currently on the page.\n *\n * @method calculateZIndex\n * @return {int}\n */\n calculateZIndex() {\n const items = $(`${SELECTORS.DIALOG}, ${SELECTORS.MENU_BAR}, ${SELECTORS.HAS_Z_INDEX}`);\n let zIndex = parseInt(this.root.css('z-index'));\n\n items.each((index, item) => {\n item = $(item);\n if (!item.is(':visible')) {\n // Do not include items which are not visible in the z-index calculation.\n // This is important because some dialogues are not removed from the DOM.\n return;\n }\n // Note that webkit browsers won't return the z-index value from the CSS stylesheet\n // if the element doesn't have a position specified. Instead it'll return \"auto\".\n const itemZIndex = item.css('z-index') ? parseInt(item.css('z-index')) : 0;\n\n if (itemZIndex > zIndex) {\n zIndex = itemZIndex;\n }\n });\n\n return zIndex;\n }\n\n /**\n * Check if this modal is visible.\n *\n * @method isVisible\n * @return {bool}\n */\n isVisible() {\n return this.root.hasClass('show');\n }\n\n /**\n * Check if this modal has focus.\n *\n * @method hasFocus\n * @return {bool}\n */\n hasFocus() {\n const target = $(document.activeElement);\n return this.root.is(target) || this.root.has(target).length;\n }\n\n /**\n * Check if this modal has CSS transitions applied.\n *\n * @method hasTransitions\n * @return {bool}\n */\n hasTransitions() {\n return this.getRoot().hasClass('fade');\n }\n\n /**\n * Gets the jQuery wrapped node that the Modal should be attached to.\n *\n * @returns {jQuery}\n */\n getAttachmentPoint() {\n return $(Fullscreen.getElement() || this.attachmentPoint);\n }\n\n /**\n * Display this modal. The modal will be attached to the DOM if it hasn't\n * already been.\n *\n * @method show\n * @returns {Promise}\n */\n show() {\n if (this.isVisible()) {\n return $.Deferred().resolve();\n }\n\n const pendingPromise = new Pending('core/modal:show');\n\n if (this.hasFooterContent()) {\n this.showFooter();\n } else {\n this.hideFooter();\n }\n\n this.attachToDOM();\n\n // If the focusOnClose was not set. Set the focus back to triggered element.\n if (!this.focusOnClose && document.activeElement) {\n this.focusOnClose = document.activeElement;\n }\n\n return this.getBackdrop()\n .then((backdrop) => {\n const currentIndex = this.calculateZIndex();\n const newIndex = currentIndex + 2;\n const newBackdropIndex = newIndex - 1;\n this.root.css('z-index', newIndex);\n backdrop.setZIndex(newBackdropIndex);\n backdrop.show();\n\n this.root.removeClass('hide').addClass('show');\n this.accessibilityShow();\n this.getModal().focus();\n $('body').addClass('modal-open');\n this.root.trigger(ModalEvents.shown, this);\n dispatchEvent('core/modal:shown', this, this.modal[0]);\n\n return;\n })\n .then(pendingPromise.resolve);\n }\n\n /**\n * Hide this modal if it does not contain a form.\n *\n * @method hideIfNotForm\n */\n hideIfNotForm() {\n const formElement = this.modal.find(SELECTORS.FORM);\n if (formElement.length == 0) {\n this.hide();\n }\n }\n\n /**\n * Hide this modal.\n *\n * @method hide\n */\n hide() {\n this.getBackdrop().done((backdrop) => {\n FocusLock.untrapFocus();\n\n if (!this.countOtherVisibleModals()) {\n // Hide the backdrop if we're the last open modal.\n backdrop.hide();\n $('body').removeClass('modal-open');\n }\n\n const currentIndex = parseInt(this.root.css('z-index'));\n this.root.css('z-index', '');\n backdrop.setZIndex(currentIndex - 3);\n\n this.accessibilityHide();\n\n if (this.hasTransitions()) {\n // Wait for CSS transitions to complete before hiding the element.\n this.getRoot().one('transitionend webkitTransitionEnd oTransitionEnd', () => {\n this.getRoot().removeClass('show').addClass('hide');\n });\n } else {\n this.getRoot().removeClass('show').addClass('hide');\n }\n\n // Ensure the modal is moved onto the body node if it is still attached to the DOM.\n if ($(document.body).find(this.getRoot()).length) {\n $(document.body).append(this.getRoot());\n }\n\n // Closes popover elements that are inside the modal at the time the modal is closed.\n this.getRoot().find('[data-bs-toggle=\"popover\"]').each(function() {\n document.getElementById(this.getAttribute('aria-describedby'))?.remove();\n });\n\n this.root.trigger(ModalEvents.hidden, this);\n });\n }\n\n /**\n * Remove this modal from the DOM.\n *\n * @method destroy\n */\n destroy() {\n this.hide();\n removeToastRegion(this.getBody().get(0));\n this.root.remove();\n this.root.trigger(ModalEvents.destroyed, this);\n this.attachmentPoint.remove();\n }\n\n /**\n * Sets the appropriate aria attributes on this dialogue and the other\n * elements in the DOM to ensure that screen readers are able to navigate\n * the dialogue popup correctly.\n *\n * @method accessibilityShow\n */\n accessibilityShow() {\n // Make us visible to screen readers.\n Aria.unhide(this.root.get());\n\n // Hide siblings.\n Aria.hideSiblings(this.root.get()[0]);\n }\n\n /**\n * Restores the aria visibility on the DOM elements changed when displaying\n * the dialogue popup and makes the dialogue aria hidden to allow screen\n * readers to navigate the main page correctly when the dialogue is closed.\n *\n * @method accessibilityHide\n */\n accessibilityHide() {\n // Unhide siblings.\n Aria.unhideSiblings(this.root.get()[0]);\n\n // Hide this modal.\n Aria.hide(this.root.get());\n }\n\n /**\n * Set up all of the event handling for the modal.\n *\n * @method registerEventListeners\n */\n registerEventListeners() {\n this.getRoot().on('keydown', (e) => {\n if (!this.isVisible()) {\n return;\n }\n\n if (e.keyCode == KeyCodes.escape) {\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n }\n });\n\n // Listen for clicks on the modal container.\n this.getRoot().click((e) => {\n // If the click wasn't inside the modal element then we should\n // hide the modal.\n if (!$(e.target).closest(SELECTORS.MODAL).length) {\n // The check above fails to detect the click was inside the modal when the DOM tree is already changed.\n // So, we check if we can still find the container element or not. If not, then the DOM tree is changed.\n // It's best not to hide the modal in that case.\n if ($(e.target).closest(SELECTORS.CONTAINER).length) {\n const outsideClickEvent = $.Event(ModalEvents.outsideClick);\n this.getRoot().trigger(outsideClickEvent, this);\n\n if (!outsideClickEvent.isDefaultPrevented()) {\n this.hideIfNotForm();\n }\n }\n }\n });\n\n CustomEvents.define(this.getModal(), [CustomEvents.events.activate]);\n this.getModal().on(CustomEvents.events.activate, SELECTORS.HIDE, (e, data) => {\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n data.originalEvent.preventDefault();\n });\n\n this.getRoot().on(ModalEvents.hidden, () => {\n if (this.focusOnClose) {\n // Focus on the element that actually triggers the modal.\n this.focusOnClose.focus();\n }\n });\n }\n\n /**\n * Register a listener to close the dialogue when the cancel button is pressed.\n *\n * @method registerCloseOnCancel\n */\n registerCloseOnCancel() {\n // Handle the clicking of the Cancel button.\n this.getModal().on(CustomEvents.events.activate, this.getActionSelector('cancel'), (e, data) => {\n const cancelEvent = $.Event(ModalEvents.cancel);\n this.getRoot().trigger(cancelEvent, this);\n\n if (!cancelEvent.isDefaultPrevented()) {\n data.originalEvent.preventDefault();\n\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n }\n });\n }\n\n /**\n * Register a listener to close the dialogue when the save button is pressed.\n *\n * @method registerCloseOnSave\n */\n registerCloseOnSave() {\n // Handle the clicking of the Cancel button.\n this.getModal().on(CustomEvents.events.activate, this.getActionSelector('save'), (e, data) => {\n const saveEvent = $.Event(ModalEvents.save);\n this.getRoot().trigger(saveEvent, this);\n\n if (!saveEvent.isDefaultPrevented()) {\n data.originalEvent.preventDefault();\n\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n }\n });\n }\n\n\n /**\n * Register a listener to close the dialogue when the delete button is pressed.\n *\n * @method registerCloseOnDelete\n */\n registerCloseOnDelete() {\n // Handle the clicking of the Cancel button.\n this.getModal().on(CustomEvents.events.activate, this.getActionSelector('delete'), (e, data) => {\n const deleteEvent = $.Event(ModalEvents.delete);\n this.getRoot().trigger(deleteEvent, this);\n\n if (!deleteEvent.isDefaultPrevented()) {\n data.originalEvent.preventDefault();\n\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n }\n });\n }\n\n /**\n * Set or resolve and set the value using the function.\n *\n * @method asyncSet\n * @param {(string|object)} value The string or jQuery promise.\n * @param {function} setFunction The setter\n * @return {Promise}\n */\n asyncSet(value, setFunction) {\n const getWrappedValue = (value) => {\n if (value instanceof Promise) {\n return $.when(value);\n }\n\n if (typeof value !== 'object' || !value.hasOwnProperty('then')) {\n return $.Deferred().resolve(value);\n }\n\n return value;\n };\n\n return getWrappedValue(value)\n .then((content) => setFunction(content))\n .catch(Notification.exception);\n }\n\n /**\n * Set the title text of a button.\n *\n * This method is overloaded to take either a string value for the button title or a jQuery promise that is resolved with\n * text most commonly from a Str.get_string call.\n *\n * @param {DOMString} action The action of the button\n * @param {(String|object)} value The button text, or a promise which will resolve to it\n * @returns {Promise}\n */\n setButtonText(action, value) {\n const button = this.getFooter().find(this.getActionSelector(action));\n\n if (!button) {\n throw new Error(\"Unable to find the '\" + action + \"' button\");\n }\n\n return this.asyncSet(value, button.text.bind(button));\n }\n\n /**\n * Get the Selector for an action.\n *\n * @param {String} action\n * @returns {DOMString}\n */\n getActionSelector(action) {\n return \"[data-action='\" + action + \"']\";\n }\n\n /**\n * Set the flag to remove the modal from the DOM on close.\n *\n * @param {Boolean} remove\n */\n setRemoveOnClose(remove) {\n this.removeOnClose = remove;\n }\n\n /**\n * Set the return element for the modal.\n *\n * @param {Element|jQuery} element Element to focus when the modal is closed\n */\n setReturnElement(element) {\n this.focusOnClose = element;\n }\n\n /**\n * Set the a button enabled or disabled.\n *\n * @param {DOMString} action The action of the button\n * @param {Boolean} disabled the new disabled value\n */\n setButtonDisabled(action, disabled) {\n const button = this.getFooter().find(this.getActionSelector(action));\n\n if (!button) {\n throw new Error(\"Unable to find the '\" + action + \"' button\");\n }\n if (disabled) {\n button.attr('disabled', '');\n } else {\n button.removeAttr('disabled');\n }\n }\n\n /**\n * Set the template JS for this modal.\n * @param {String} js The JavaScript to run when the modal is attached to the DOM.\n */\n setTemplateJS(js) {\n this.templateJS = js;\n }\n}\n"],"names":["SELECTORS","TEMPLATES","Modal","root","this","_root","filter","constructor","modal","find","header","headerPromise","$","Deferred","title","titlePromise","body","bodyPromise","footer","footerPromise","hiddenSiblings","isAttached","bodyJS","footerJS","modalCount","modalCounter","attachmentPoint","document","createElement","append","focusOnClose","templateJS","is","Notification","exception","message","length","registerEventListeners","TYPE","Error","TEMPLATE","ModalRegistry","register","modalConfig","pendingModalPromise","Pending","type","templateName","_getTemplateName","templateContext","html","js","Templates","renderForPromise","setTemplateJS","configure","resolve","template","has","window","console","warning","get","show","large","isVerticallyCentered","removeOnClose","scrollable","returnElement","buttons","setLarge","setVerticallyCentered","setRemoveOnClose","setReturnElement","setScrollable","undefined","setTitle","setBody","setFooter","Object","entries","forEach","_ref","key","value","setButtonText","attachToDOM","getAttachmentPoint","FocusLock","trapFocus","runTemplateJS","countOtherVisibleModals","count","each","index","element","hasClass","getBackdrop","backdropPromise","render","then","ModalBackdrop","catch","getRoot","getModal","getTitle","getBody","getFooter","getTitlePromise","getBodyPromise","getFooterPromise","getModalCount","asyncSet","bind","FilterEvents","notifyFilterContentUpdated","trigger","ModalEvents","bodyRendered","modalPromise","contentPromise","css","when","state","height","innerHeight","animate","loadingIcon","hide","fadeIn","promise","fadeOut","result","isVisible","currentHeight","newHeight","opacity","duration","queue","always","setBodyContent","_ref2","showFooter","hasFooterContent","children","hideFooter","addClass","removeClass","isLarge","setSmall","isSmall","classList","add","remove","calculateZIndex","items","zIndex","parseInt","item","itemZIndex","hasFocus","target","activeElement","hasTransitions","Fullscreen","getElement","pendingPromise","backdrop","newIndex","newBackdropIndex","setZIndex","accessibilityShow","focus","shown","hideIfNotForm","done","untrapFocus","currentIndex","accessibilityHide","one","getElementById","getAttribute","hidden","destroy","destroyed","Aria","unhide","hideSiblings","unhideSiblings","on","e","keyCode","KeyCodes","escape","click","closest","outsideClickEvent","Event","outsideClick","isDefaultPrevented","CustomEvents","define","events","activate","data","originalEvent","preventDefault","registerCloseOnCancel","getActionSelector","cancelEvent","cancel","registerCloseOnSave","saveEvent","save","registerCloseOnDelete","deleteEvent","delete","setFunction","Promise","hasOwnProperty","getWrappedValue","content","action","button","text","setButtonDisabled","disabled","attr","removeAttr"],"mappings":"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"} \ No newline at end of file diff --git a/lib/amd/build/moremenu.min.js b/lib/amd/build/moremenu.min.js index b1260db9988ce..1fe5c44dae3ea 100644 --- a/lib/amd/build/moremenu.min.js +++ b/lib/amd/build/moremenu.min.js @@ -1,4 +1,4 @@ -define("core/moremenu",["exports","jquery","core/menu_navigation"],(function(_exports,_jquery,_menu_navigation){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +define("core/moremenu",["exports","core/menu_navigation"],(function(_exports,_menu_navigation){var obj; /** * Moves wrapping navigation items into a more menu. * @@ -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-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})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_menu_navigation=(obj=_menu_navigation)&&obj.__esModule?obj:{default:obj};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()};document.querySelector("."+Selectors_classes.dropdownmoremenu).addEventListener("show.bs.dropdown",(()=>{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 371e2c00cde86..8aacb8b2ea131 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-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 +{"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 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 document.querySelector('.' + Selectors.classes.dropdownmoremenu).addEventListener('show.bs.dropdown', () => {\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","document","querySelectorAll","dropdown","removeEventListener"],"mappings":"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"} \ No newline at end of file diff --git a/lib/amd/src/comboboxsearch/search_combobox.js b/lib/amd/src/comboboxsearch/search_combobox.js index 434d5d81aa8f7..020da1faff7e8 100644 --- a/lib/amd/src/comboboxsearch/search_combobox.js +++ b/lib/amd/src/comboboxsearch/search_combobox.js @@ -14,6 +14,7 @@ // along with Moodle. If not, see . import $ from 'jquery'; +import Dropdown from 'theme_boost/bootstrap/dropdown'; import {debounce} from 'core/utils'; import Pending from 'core/pending'; @@ -260,9 +261,9 @@ export default class { */ toggleDropdown(on = false) { if (on) { - $(this.toggle).dropdown('show'); + Dropdown.getOrCreateInstance(this.toggle).show(); } else { - $(this.toggle).dropdown('hide'); + Dropdown.getOrCreateInstance(this.toggle).hide(); } } 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/local/action_menu/subpanel.js b/lib/amd/src/local/action_menu/subpanel.js index e916d229aff94..fc839b19a8f22 100644 --- a/lib/amd/src/local/action_menu/subpanel.js +++ b/lib/amd/src/local/action_menu/subpanel.js @@ -21,7 +21,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import jQuery from 'jquery'; import {debounce} from 'core/utils'; import { isBehatSite, @@ -71,9 +70,8 @@ const initPageEvents = () => { if (initialized) { return; } - // Hide all subpanels when hidind a dropdown. - // This is using JQuery because of BS4 events. JQuery won't be needed with BS5. - jQuery(document).on(BootstrapEvents.hideDropdown, () => { + // Hide all subpanels when hiding a dropdown. + document.addEventListener(BootstrapEvents.hideDropdown, () => { document.querySelectorAll(`${Selectors.subPanelContent}.show`).forEach(visibleSubPanel => { const dropdownSubPanel = visibleSubPanel.closest(Selectors.subPanel); const subPanel = new SubPanel(dropdownSubPanel); diff --git a/lib/amd/src/local/dropdown/dialog.js b/lib/amd/src/local/dropdown/dialog.js index 6c2bbb121ac96..6b5f282c11ea6 100644 --- a/lib/amd/src/local/dropdown/dialog.js +++ b/lib/amd/src/local/dropdown/dialog.js @@ -21,8 +21,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -// The jQuery module is only used for interacting with Bootstrap 4. It can be removed when MDL-71979 is integrated. -import jQuery from 'jquery'; +import Dropdown from 'theme_boost/bootstrap/dropdown'; import { firstFocusableElement, lastFocusableElement, @@ -145,8 +144,7 @@ export class DropdownDialog { if (visible === this.isVisible()) { return; } - // All jQuery in this code can be replaced when MDL-71979 is integrated. - jQuery(this.button).dropdown('toggle'); + Dropdown.getOrCreateInstance(this.button).toggle(); } /** diff --git a/lib/amd/src/modal.js b/lib/amd/src/modal.js index 35773e62529fb..d7ad249c825c7 100644 --- a/lib/amd/src/modal.js +++ b/lib/amd/src/modal.js @@ -35,6 +35,7 @@ import * as FocusLock from 'core/local/aria/focuslock'; import * as Aria from 'core/aria'; import * as Fullscreen from 'core/fullscreen'; import {removeToastRegion} from './toast'; +import {dispatchEvent} from 'core/event_dispatcher'; /** * A configuration to provide to the modal. @@ -582,6 +583,7 @@ export default class Modal { .then((result) => { FilterEvents.notifyFilterContentUpdated(body); this.getRoot().trigger(ModalEvents.bodyRendered, this); + dispatchEvent('core/modal:bodyRendered', this, this.modal[0]); return result; }) .then(() => { @@ -897,6 +899,7 @@ export default class Modal { this.getModal().focus(); $('body').addClass('modal-open'); this.root.trigger(ModalEvents.shown, this); + dispatchEvent('core/modal:shown', this, this.modal[0]); return; }) diff --git a/lib/amd/src/moremenu.js b/lib/amd/src/moremenu.js index f00625ceb9e2d..2575526b7b689 100644 --- a/lib/amd/src/moremenu.js +++ b/lib/amd/src/moremenu.js @@ -22,7 +22,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import $ from 'jquery'; import menu_navigation from "core/menu_navigation"; /** * Moremenu selectors. @@ -248,7 +247,7 @@ export default menu => { // If there are dropdowns in the MoreMenu, add a new // event listener to show the contents on click and prevent the // moreMenu from closing. - $('.' + Selectors.classes.dropdownmoremenu).on('show.bs.dropdown', function() { + document.querySelector('.' + Selectors.classes.dropdownmoremenu).addEventListener('show.bs.dropdown', () => { const moreDropdown = menu.querySelector(Selectors.regions.moredropdown); moreDropdown.querySelectorAll('.dropdown').forEach((dropdown) => { dropdown.removeEventListener('click', toggledropdown, true); 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/local/toast/message.mustache b/lib/templates/local/toast/message.mustache index 3b0bce70eb9b8..759b603da1a5d 100644 --- a/lib/templates/local/toast/message.mustache +++ b/lib/templates/local/toast/message.mustache @@ -51,13 +51,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/lib/templates/search_input_navbar.mustache b/lib/templates/search_input_navbar.mustache index 87fa183492280..8ecd388ae4a16 100644 --- a/lib/templates/search_input_navbar.mustache +++ b/lib/templates/search_input_navbar.mustache @@ -90,25 +90,27 @@ require( function( $ ) { - var uniqid = "{{uniqid}}"; - var container = $('#searchinput-navbar-' + uniqid); - var opensearch = container.find('[data-action="opensearch"]'); - var input = container.find('[data-region="input"]'); - var submit = container.find('[data-action="submit"]'); + const container = document.getElementById('searchform-navbar'); + const opensearch = container.parentElement.querySelector('[data-action="opensearch"]'); + const input = container.querySelector('[data-region="input"]'); + const submit = container.querySelector('[data-action="submit"]'); - submit.on('click', function(e) { - if (input.val() === '') { + submit.addEventListener('click', (e) => { + if (input.valUE === '') { e.preventDefault(); } }); - container.on('hidden.bs.collapse', function() { - opensearch.removeClass('d-none'); - input.val(''); + + container.addEventListener('hidden.bs.collapse', () => { + opensearch.classList.remove('d-none'); + input.value = ''; }); - container.on('show.bs.collapse', function() { - opensearch.addClass('d-none'); + + container.addEventListener('show.bs.collapse', () => { + opensearch.classList.add('d-none'); }); - container.on('shown.bs.collapse', function() { + + container.addEventListener('shown.bs.collapse', () => { input.focus(); }); }); diff --git a/lib/tests/behat/fixtures/dropdown_output_testpage.php b/lib/tests/behat/fixtures/dropdown_output_testpage.php index 50a830398fcdc..55e4036fcfe57 100644 --- a/lib/tests/behat/fixtures/dropdown_output_testpage.php +++ b/lib/tests/behat/fixtures/dropdown_output_testpage.php @@ -139,8 +139,8 @@ $inlinejs = << { + ['core/local/dropdown/dialog'], + (Module) => { const dialog = Module.getDropdownDialog('#dialogjscontrols'); document.querySelector('#buttontext').addEventListener('click', () => { @@ -163,12 +163,10 @@ } visibility(); - - // Bootstrap 4 events are still jQuery. - jQuery(dialog.getElement()).on('shown.bs.dropdown', (e) => { + dialog.getElement().addEventListener('shown.bs.dropdown', (e) => { visibility(); }); - jQuery(dialog.getElement()).on('hidden.bs.dropdown', (e) => { + dialog.getElement().addEventListener('hidden.bs.dropdown', (e) => { visibility(); }); } diff --git a/message/amd/build/message_drawer.min.js b/message/amd/build/message_drawer.min.js index fb313ba1879ae..3c02f3b71eaf4 100644 --- a/message/amd/build/message_drawer.min.js +++ b/message/amd/build/message_drawer.min.js @@ -5,6 +5,6 @@ * @copyright 2018 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("core_message/message_drawer",["jquery","core/custom_interaction_events","core/pubsub","core_message/message_drawer_view_contact","core_message/message_drawer_view_contacts","core_message/message_drawer_view_conversation","core_message/message_drawer_view_group_info","core_message/message_drawer_view_overview","core_message/message_drawer_view_search","core_message/message_drawer_view_settings","core_message/message_drawer_router","core_message/message_drawer_routes","core_message/message_drawer_events","core_message/message_drawer_helper","core/pending","core/drawer"],(function($,CustomEvents,PubSub,ViewContact,ViewContacts,ViewConversation,ViewGroupInfo,ViewOverview,ViewSearch,ViewSettings,Router,Routes,Events,Helper,Pending,Drawer){var SELECTORS_DRAWER='[data-region="right-hand-drawer"]',SELECTORS_JUMPTO='.popover-region [data-region="jumpto"]',SELECTORS_PANEL_BODY_CONTAINER='[data-region="panel-body-container"]',SELECTORS_PANEL_HEADER_CONTAINER='[data-region="panel-header-container"]',SELECTORS_VIEW_CONTACT='[data-region="view-contact"]',SELECTORS_VIEW_CONTACTS='[data-region="view-contacts"]',SELECTORS_VIEW_CONVERSATION='[data-region="view-conversation"]',SELECTORS_VIEW_GROUP_INFO='[data-region="view-group-info"]',SELECTORS_VIEW_OVERVIEW='[data-region="view-overview"]',SELECTORS_VIEW_SEARCH='[data-region="view-search"]',SELECTORS_VIEW_SETTINGS='[data-region="view-settings"]',SELECTORS_ROUTES="[data-route]",SELECTORS_ROUTES_BACK="[data-route-back]",SELECTORS_HEADER_CONTAINER='[data-region="header-container"]',SELECTORS_BODY_CONTAINER='[data-region="body-container"]',SELECTORS_FOOTER_CONTAINER='[data-region="footer-container"]',SELECTORS_CLOSE_BUTTON='[data-action="closedrawer"]',routes=[[Routes.VIEW_CONTACT,SELECTORS_VIEW_CONTACT,ViewContact.show,ViewContact.description],[Routes.VIEW_CONTACTS,SELECTORS_VIEW_CONTACTS,ViewContacts.show,ViewContacts.description],[Routes.VIEW_CONVERSATION,SELECTORS_VIEW_CONVERSATION,ViewConversation.show,ViewConversation.description],[Routes.VIEW_GROUP_INFO,SELECTORS_VIEW_GROUP_INFO,ViewGroupInfo.show,ViewGroupInfo.description],[Routes.VIEW_OVERVIEW,SELECTORS_VIEW_OVERVIEW,ViewOverview.show,ViewOverview.description],[Routes.VIEW_SEARCH,SELECTORS_VIEW_SEARCH,ViewSearch.show,ViewSearch.description],[Routes.VIEW_SETTINGS,SELECTORS_VIEW_SETTINGS,ViewSettings.show,ViewSettings.description]],createRoutes=function(namespace,root){routes.forEach((function(route){Router.add(namespace,route[0],function(namespace,root,selector){var header=root.find(SELECTORS_HEADER_CONTAINER).find(selector);header.length||(header=root.find(SELECTORS_PANEL_HEADER_CONTAINER).find(selector));var body=root.find(SELECTORS_BODY_CONTAINER).find(selector);body.length||(body=root.find(SELECTORS_PANEL_BODY_CONTAINER).find(selector));var footer=root.find(SELECTORS_FOOTER_CONTAINER).find(selector);return[namespace,header.length?header:null,body.length?body:null,footer.length?footer:null]}(namespace,root,route[1]),route[2],route[3])}))},show=function(namespace,root){root.attr("data-shown")||(Router.go(namespace,Routes.VIEW_OVERVIEW),root.attr("data-shown",!0));var drawerRoot=Drawer.getDrawerRoot(root);drawerRoot.length&&Drawer.show(drawerRoot)},hide=function(root){var drawerRoot=Drawer.getDrawerRoot(root);drawerRoot.length&&Drawer.hide(drawerRoot)},setJumpFrom=function(buttonid){$(SELECTORS_DRAWER).attr("data-origin",buttonid)},registerEventListeners=function(namespace,root,alwaysVisible){CustomEvents.define(root,[CustomEvents.events.activate]);var paramRegex=/^data-route-param-?(\d*)$/;root.on(CustomEvents.events.activate,SELECTORS_ROUTES,(function(e,data){for(var element=$(e.target).closest(SELECTORS_ROUTES),route=element.attr("data-route"),attributes=[],i=0;i1?aParts[1]:0,bIndex=bParts.length>1?bParts[1]:0;return aIndex1?aParts[1]:0,bIndex=bParts.length>1?bParts[1]:0;return aIndex{collapse.addEventListener("hide.bs.collapse",(e=>{var pendingPromise=new Pending;e.target.addEventListener("hidden.bs.collapse",(function(){pendingPromise.resolve()}),{once:!0})}))})),root[0].querySelectorAll(".collapse").forEach((collapse=>{collapse.addEventListener("show.bs.collapse",(e=>{var pendingPromise=new Pending;e.target.addEventListener("shown.bs.collapse",(function(){pendingPromise.resolve()}),{once:!0})}))})),$(SELECTORS_JUMPTO).focus((function(){var firstInput=root.find(SELECTORS_CLOSE_BUTTON);firstInput.length?firstInput.focus():$(SELECTORS_HEADER_CONTAINER).find(SELECTORS_ROUTES_BACK).focus()})),$(SELECTORS_DRAWER).focus((function(){var button=$(this).attr("data-origin");button&&$("#"+button).focus()})),alwaysVisible||(PubSub.subscribe(Events.SHOW,(function(){show(namespace,root)})),PubSub.subscribe(Events.HIDE,(function(){hide(root)})),PubSub.subscribe(Events.TOGGLE_VISIBILITY,(function(buttonid){!function(root){var drawerRoot=Drawer.getDrawerRoot(root);return!drawerRoot.length||Drawer.isVisible(drawerRoot)}(root)?(show(namespace,root),setJumpFrom(buttonid),$(SELECTORS_JUMPTO).attr("tabindex",0)):(hide(root),$(SELECTORS_JUMPTO).attr("tabindex",-1))}))),PubSub.subscribe(Events.SHOW_CONVERSATION,(function(args){setJumpFrom(args.buttonid),show(namespace,root),Router.go(namespace,Routes.VIEW_CONVERSATION,args.conversationid)})),root.find(SELECTORS_CLOSE_BUTTON).on(CustomEvents.events.activate,(function(e,data){data.originalEvent.preventDefault();var button=$(SELECTORS_DRAWER).attr("data-origin");button&&$("#"+button).focus(),PubSub.publish(Events.TOGGLE_VISIBILITY)})),PubSub.subscribe(Events.CREATE_CONVERSATION_WITH_USER,(function(args){setJumpFrom(args.buttonid),show(namespace,root),Router.go(namespace,Routes.VIEW_CONVERSATION,null,"create",args.userid)})),PubSub.subscribe(Events.SHOW_SETTINGS,(function(){show(namespace,root),Router.go(namespace,Routes.VIEW_SETTINGS)})),PubSub.subscribe(Events.PREFERENCES_UPDATED,(function(preferences){var filteredPreferences=preferences.filter((function(preference){return"message_entertosend"==preference.type})),enterToSendPreference=filteredPreferences.length?filteredPreferences[0]:null;enterToSendPreference&&root.find(SELECTORS_FOOTER_CONTAINER).find(SELECTORS_VIEW_CONVERSATION).attr("data-enter-to-send",enterToSendPreference.value)}))};return{init:function(root,uniqueId,alwaysVisible,route){if(root=$(root),createRoutes(uniqueId,root),registerEventListeners(uniqueId,root,alwaysVisible),alwaysVisible&&(show(uniqueId,root),route)){var routeParams=route.params||[];routeParams=[uniqueId,route.path].concat(routeParams),Router.go.apply(null,routeParams)}Helper.markDrawerReady()}}})); //# sourceMappingURL=message_drawer.min.js.map \ No newline at end of file diff --git a/message/amd/build/message_drawer.min.js.map b/message/amd/build/message_drawer.min.js.map index 66500d6d74b18..a1bc089c392f9 100644 --- a/message/amd/build/message_drawer.min.js.map +++ b/message/amd/build/message_drawer.min.js.map @@ -1 +1 @@ -{"version":3,"file":"message_drawer.min.js","sources":["../src/message_drawer.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 * Controls the message drawer.\n *\n * @module core_message/message_drawer\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/custom_interaction_events',\n 'core/pubsub',\n 'core_message/message_drawer_view_contact',\n 'core_message/message_drawer_view_contacts',\n 'core_message/message_drawer_view_conversation',\n 'core_message/message_drawer_view_group_info',\n 'core_message/message_drawer_view_overview',\n 'core_message/message_drawer_view_search',\n 'core_message/message_drawer_view_settings',\n 'core_message/message_drawer_router',\n 'core_message/message_drawer_routes',\n 'core_message/message_drawer_events',\n 'core_message/message_drawer_helper',\n 'core/pending',\n 'core/drawer',\n],\nfunction(\n $,\n CustomEvents,\n PubSub,\n ViewContact,\n ViewContacts,\n ViewConversation,\n ViewGroupInfo,\n ViewOverview,\n ViewSearch,\n ViewSettings,\n Router,\n Routes,\n Events,\n Helper,\n Pending,\n Drawer\n) {\n\n var SELECTORS = {\n DRAWER: '[data-region=\"right-hand-drawer\"]',\n JUMPTO: '.popover-region [data-region=\"jumpto\"]',\n PANEL_BODY_CONTAINER: '[data-region=\"panel-body-container\"]',\n PANEL_HEADER_CONTAINER: '[data-region=\"panel-header-container\"]',\n VIEW_CONTACT: '[data-region=\"view-contact\"]',\n VIEW_CONTACTS: '[data-region=\"view-contacts\"]',\n VIEW_CONVERSATION: '[data-region=\"view-conversation\"]',\n VIEW_GROUP_INFO: '[data-region=\"view-group-info\"]',\n VIEW_OVERVIEW: '[data-region=\"view-overview\"]',\n VIEW_SEARCH: '[data-region=\"view-search\"]',\n VIEW_SETTINGS: '[data-region=\"view-settings\"]',\n ROUTES: '[data-route]',\n ROUTES_BACK: '[data-route-back]',\n HEADER_CONTAINER: '[data-region=\"header-container\"]',\n BODY_CONTAINER: '[data-region=\"body-container\"]',\n FOOTER_CONTAINER: '[data-region=\"footer-container\"]',\n CLOSE_BUTTON: '[data-action=\"closedrawer\"]'\n };\n\n /**\n * Get elements for route.\n *\n * @param {String} namespace Unique identifier for the Routes\n * @param {Object} root The message drawer container.\n * @param {string} selector The route container.\n *\n * @return {array} elements Found route container objects.\n */\n var getParametersForRoute = function(namespace, root, selector) {\n\n var header = root.find(SELECTORS.HEADER_CONTAINER).find(selector);\n if (!header.length) {\n header = root.find(SELECTORS.PANEL_HEADER_CONTAINER).find(selector);\n }\n var body = root.find(SELECTORS.BODY_CONTAINER).find(selector);\n if (!body.length) {\n body = root.find(SELECTORS.PANEL_BODY_CONTAINER).find(selector);\n }\n var footer = root.find(SELECTORS.FOOTER_CONTAINER).find(selector);\n\n return [\n namespace,\n header.length ? header : null,\n body.length ? body : null,\n footer.length ? footer : null\n ];\n };\n\n var routes = [\n [Routes.VIEW_CONTACT, SELECTORS.VIEW_CONTACT, ViewContact.show, ViewContact.description],\n [Routes.VIEW_CONTACTS, SELECTORS.VIEW_CONTACTS, ViewContacts.show, ViewContacts.description],\n [Routes.VIEW_CONVERSATION, SELECTORS.VIEW_CONVERSATION, ViewConversation.show, ViewConversation.description],\n [Routes.VIEW_GROUP_INFO, SELECTORS.VIEW_GROUP_INFO, ViewGroupInfo.show, ViewGroupInfo.description],\n [Routes.VIEW_OVERVIEW, SELECTORS.VIEW_OVERVIEW, ViewOverview.show, ViewOverview.description],\n [Routes.VIEW_SEARCH, SELECTORS.VIEW_SEARCH, ViewSearch.show, ViewSearch.description],\n [Routes.VIEW_SETTINGS, SELECTORS.VIEW_SETTINGS, ViewSettings.show, ViewSettings.description]\n ];\n\n /**\n * Create routes.\n *\n * @param {String} namespace Unique identifier for the Routes\n * @param {Object} root The message drawer container.\n */\n var createRoutes = function(namespace, root) {\n routes.forEach(function(route) {\n Router.add(namespace, route[0], getParametersForRoute(namespace, root, route[1]), route[2], route[3]);\n });\n };\n\n /**\n * Show the message drawer.\n *\n * @param {string} namespace The route namespace.\n * @param {Object} root The message drawer container.\n */\n var show = function(namespace, root) {\n if (!root.attr('data-shown')) {\n Router.go(namespace, Routes.VIEW_OVERVIEW);\n root.attr('data-shown', true);\n }\n\n var drawerRoot = Drawer.getDrawerRoot(root);\n if (drawerRoot.length) {\n Drawer.show(drawerRoot);\n }\n };\n\n /**\n * Hide the message drawer.\n *\n * @param {Object} root The message drawer container.\n */\n var hide = function(root) {\n var drawerRoot = Drawer.getDrawerRoot(root);\n if (drawerRoot.length) {\n Drawer.hide(drawerRoot);\n }\n };\n\n /**\n * Check if the drawer is visible.\n *\n * @param {Object} root The message drawer container.\n * @return {boolean}\n */\n var isVisible = function(root) {\n var drawerRoot = Drawer.getDrawerRoot(root);\n if (drawerRoot.length) {\n return Drawer.isVisible(drawerRoot);\n }\n return true;\n };\n\n /**\n * Set Jump from button\n *\n * @param {String} buttonid The originating button id\n */\n var setJumpFrom = function(buttonid) {\n $(SELECTORS.DRAWER).attr('data-origin', buttonid);\n };\n\n /**\n * Listen to and handle events for routing, showing and hiding the message drawer.\n *\n * @param {string} namespace The route namespace.\n * @param {Object} root The message drawer container.\n * @param {bool} alwaysVisible Is this messaging app always shown?\n */\n var registerEventListeners = function(namespace, root, alwaysVisible) {\n CustomEvents.define(root, [CustomEvents.events.activate]);\n var paramRegex = /^data-route-param-?(\\d*)$/;\n\n root.on(CustomEvents.events.activate, SELECTORS.ROUTES, function(e, data) {\n var element = $(e.target).closest(SELECTORS.ROUTES);\n var route = element.attr('data-route');\n var attributes = [];\n\n for (var i = 0; i < element[0].attributes.length; i++) {\n attributes.push(element[0].attributes[i]);\n }\n\n var paramAttributes = attributes.filter(function(attribute) {\n var name = attribute.nodeName;\n var match = paramRegex.test(name);\n return match;\n });\n paramAttributes.sort(function(a, b) {\n var aParts = paramRegex.exec(a.nodeName);\n var bParts = paramRegex.exec(b.nodeName);\n var aIndex = aParts.length > 1 ? aParts[1] : 0;\n var bIndex = bParts.length > 1 ? bParts[1] : 0;\n\n if (aIndex < bIndex) {\n return -1;\n } else if (bIndex < aIndex) {\n return 1;\n } else {\n return 0;\n }\n });\n\n var params = paramAttributes.map(function(attribute) {\n return attribute.nodeValue;\n });\n\n var routeParams = [namespace, route].concat(params);\n\n Router.go.apply(null, routeParams);\n\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ROUTES_BACK, function(e, data) {\n Router.back(namespace);\n\n data.originalEvent.preventDefault();\n });\n\n // These are theme-specific to help us fix random behat fails.\n // These events target those events defined in BS3 and BS4 onwards.\n root.on('hide.bs.collapse', '.collapse', function(e) {\n var pendingPromise = new Pending();\n $(e.target).one('hidden.bs.collapse', function() {\n pendingPromise.resolve();\n });\n });\n\n root.on('show.bs.collapse', '.collapse', function(e) {\n var pendingPromise = new Pending();\n $(e.target).one('shown.bs.collapse', function() {\n pendingPromise.resolve();\n });\n });\n\n $(SELECTORS.JUMPTO).focus(function() {\n var firstInput = root.find(SELECTORS.CLOSE_BUTTON);\n if (firstInput.length) {\n firstInput.focus();\n } else {\n $(SELECTORS.HEADER_CONTAINER).find(SELECTORS.ROUTES_BACK).focus();\n }\n });\n\n $(SELECTORS.DRAWER).focus(function() {\n var button = $(this).attr('data-origin');\n if (button) {\n $('#' + button).focus();\n }\n });\n\n if (!alwaysVisible) {\n PubSub.subscribe(Events.SHOW, function() {\n show(namespace, root);\n });\n\n PubSub.subscribe(Events.HIDE, function() {\n hide(root);\n });\n\n PubSub.subscribe(Events.TOGGLE_VISIBILITY, function(buttonid) {\n if (isVisible(root)) {\n hide(root);\n $(SELECTORS.JUMPTO).attr('tabindex', -1);\n } else {\n show(namespace, root);\n setJumpFrom(buttonid);\n $(SELECTORS.JUMPTO).attr('tabindex', 0);\n }\n });\n }\n\n PubSub.subscribe(Events.SHOW_CONVERSATION, function(args) {\n setJumpFrom(args.buttonid);\n show(namespace, root);\n Router.go(namespace, Routes.VIEW_CONVERSATION, args.conversationid);\n });\n\n var closebutton = root.find(SELECTORS.CLOSE_BUTTON);\n closebutton.on(CustomEvents.events.activate, function(e, data) {\n data.originalEvent.preventDefault();\n\n var button = $(SELECTORS.DRAWER).attr('data-origin');\n if (button) {\n $('#' + button).focus();\n }\n PubSub.publish(Events.TOGGLE_VISIBILITY);\n });\n\n PubSub.subscribe(Events.CREATE_CONVERSATION_WITH_USER, function(args) {\n setJumpFrom(args.buttonid);\n show(namespace, root);\n Router.go(namespace, Routes.VIEW_CONVERSATION, null, 'create', args.userid);\n });\n\n PubSub.subscribe(Events.SHOW_SETTINGS, function() {\n show(namespace, root);\n Router.go(namespace, Routes.VIEW_SETTINGS);\n });\n\n PubSub.subscribe(Events.PREFERENCES_UPDATED, function(preferences) {\n var filteredPreferences = preferences.filter(function(preference) {\n return preference.type == 'message_entertosend';\n });\n var enterToSendPreference = filteredPreferences.length ? filteredPreferences[0] : null;\n\n if (enterToSendPreference) {\n var viewConversationFooter = root.find(SELECTORS.FOOTER_CONTAINER).find(SELECTORS.VIEW_CONVERSATION);\n viewConversationFooter.attr('data-enter-to-send', enterToSendPreference.value);\n }\n });\n };\n\n /**\n * Initialise the message drawer.\n *\n * @param {Object} root The message drawer container.\n * @param {String} uniqueId Unique identifier for the Routes\n * @param {bool} alwaysVisible Should we show the app now, or wait for the user?\n * @param {Object} route\n */\n var init = function(root, uniqueId, alwaysVisible, route) {\n root = $(root);\n createRoutes(uniqueId, root);\n registerEventListeners(uniqueId, root, alwaysVisible);\n\n if (alwaysVisible) {\n show(uniqueId, root);\n\n if (route) {\n var routeParams = route.params || [];\n routeParams = [uniqueId, route.path].concat(routeParams);\n Router.go.apply(null, routeParams);\n }\n }\n\n // Mark the drawer as ready.\n Helper.markDrawerReady();\n };\n\n return {\n init: init,\n };\n});\n"],"names":["define","$","CustomEvents","PubSub","ViewContact","ViewContacts","ViewConversation","ViewGroupInfo","ViewOverview","ViewSearch","ViewSettings","Router","Routes","Events","Helper","Pending","Drawer","SELECTORS","routes","VIEW_CONTACT","show","description","VIEW_CONTACTS","VIEW_CONVERSATION","VIEW_GROUP_INFO","VIEW_OVERVIEW","VIEW_SEARCH","VIEW_SETTINGS","createRoutes","namespace","root","forEach","route","add","selector","header","find","length","body","footer","getParametersForRoute","attr","go","drawerRoot","getDrawerRoot","hide","setJumpFrom","buttonid","registerEventListeners","alwaysVisible","events","activate","paramRegex","on","e","data","element","target","closest","attributes","i","push","paramAttributes","filter","attribute","name","nodeName","test","sort","a","b","aParts","exec","bParts","aIndex","bIndex","params","map","nodeValue","routeParams","concat","apply","originalEvent","preventDefault","back","pendingPromise","one","resolve","focus","firstInput","button","this","subscribe","SHOW","HIDE","TOGGLE_VISIBILITY","isVisible","SHOW_CONVERSATION","args","conversationid","publish","CREATE_CONVERSATION_WITH_USER","userid","SHOW_SETTINGS","PREFERENCES_UPDATED","preferences","filteredPreferences","preference","type","enterToSendPreference","value","init","uniqueId","path","markDrawerReady"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"message_drawer.min.js","sources":["../src/message_drawer.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 * Controls the message drawer.\n *\n * @module core_message/message_drawer\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/custom_interaction_events',\n 'core/pubsub',\n 'core_message/message_drawer_view_contact',\n 'core_message/message_drawer_view_contacts',\n 'core_message/message_drawer_view_conversation',\n 'core_message/message_drawer_view_group_info',\n 'core_message/message_drawer_view_overview',\n 'core_message/message_drawer_view_search',\n 'core_message/message_drawer_view_settings',\n 'core_message/message_drawer_router',\n 'core_message/message_drawer_routes',\n 'core_message/message_drawer_events',\n 'core_message/message_drawer_helper',\n 'core/pending',\n 'core/drawer',\n],\nfunction(\n $,\n CustomEvents,\n PubSub,\n ViewContact,\n ViewContacts,\n ViewConversation,\n ViewGroupInfo,\n ViewOverview,\n ViewSearch,\n ViewSettings,\n Router,\n Routes,\n Events,\n Helper,\n Pending,\n Drawer\n) {\n\n var SELECTORS = {\n DRAWER: '[data-region=\"right-hand-drawer\"]',\n JUMPTO: '.popover-region [data-region=\"jumpto\"]',\n PANEL_BODY_CONTAINER: '[data-region=\"panel-body-container\"]',\n PANEL_HEADER_CONTAINER: '[data-region=\"panel-header-container\"]',\n VIEW_CONTACT: '[data-region=\"view-contact\"]',\n VIEW_CONTACTS: '[data-region=\"view-contacts\"]',\n VIEW_CONVERSATION: '[data-region=\"view-conversation\"]',\n VIEW_GROUP_INFO: '[data-region=\"view-group-info\"]',\n VIEW_OVERVIEW: '[data-region=\"view-overview\"]',\n VIEW_SEARCH: '[data-region=\"view-search\"]',\n VIEW_SETTINGS: '[data-region=\"view-settings\"]',\n ROUTES: '[data-route]',\n ROUTES_BACK: '[data-route-back]',\n HEADER_CONTAINER: '[data-region=\"header-container\"]',\n BODY_CONTAINER: '[data-region=\"body-container\"]',\n FOOTER_CONTAINER: '[data-region=\"footer-container\"]',\n CLOSE_BUTTON: '[data-action=\"closedrawer\"]'\n };\n\n /**\n * Get elements for route.\n *\n * @param {String} namespace Unique identifier for the Routes\n * @param {Object} root The message drawer container.\n * @param {string} selector The route container.\n *\n * @return {array} elements Found route container objects.\n */\n var getParametersForRoute = function(namespace, root, selector) {\n\n var header = root.find(SELECTORS.HEADER_CONTAINER).find(selector);\n if (!header.length) {\n header = root.find(SELECTORS.PANEL_HEADER_CONTAINER).find(selector);\n }\n var body = root.find(SELECTORS.BODY_CONTAINER).find(selector);\n if (!body.length) {\n body = root.find(SELECTORS.PANEL_BODY_CONTAINER).find(selector);\n }\n var footer = root.find(SELECTORS.FOOTER_CONTAINER).find(selector);\n\n return [\n namespace,\n header.length ? header : null,\n body.length ? body : null,\n footer.length ? footer : null\n ];\n };\n\n var routes = [\n [Routes.VIEW_CONTACT, SELECTORS.VIEW_CONTACT, ViewContact.show, ViewContact.description],\n [Routes.VIEW_CONTACTS, SELECTORS.VIEW_CONTACTS, ViewContacts.show, ViewContacts.description],\n [Routes.VIEW_CONVERSATION, SELECTORS.VIEW_CONVERSATION, ViewConversation.show, ViewConversation.description],\n [Routes.VIEW_GROUP_INFO, SELECTORS.VIEW_GROUP_INFO, ViewGroupInfo.show, ViewGroupInfo.description],\n [Routes.VIEW_OVERVIEW, SELECTORS.VIEW_OVERVIEW, ViewOverview.show, ViewOverview.description],\n [Routes.VIEW_SEARCH, SELECTORS.VIEW_SEARCH, ViewSearch.show, ViewSearch.description],\n [Routes.VIEW_SETTINGS, SELECTORS.VIEW_SETTINGS, ViewSettings.show, ViewSettings.description]\n ];\n\n /**\n * Create routes.\n *\n * @param {String} namespace Unique identifier for the Routes\n * @param {Object} root The message drawer container.\n */\n var createRoutes = function(namespace, root) {\n routes.forEach(function(route) {\n Router.add(namespace, route[0], getParametersForRoute(namespace, root, route[1]), route[2], route[3]);\n });\n };\n\n /**\n * Show the message drawer.\n *\n * @param {string} namespace The route namespace.\n * @param {Object} root The message drawer container.\n */\n var show = function(namespace, root) {\n if (!root.attr('data-shown')) {\n Router.go(namespace, Routes.VIEW_OVERVIEW);\n root.attr('data-shown', true);\n }\n\n var drawerRoot = Drawer.getDrawerRoot(root);\n if (drawerRoot.length) {\n Drawer.show(drawerRoot);\n }\n };\n\n /**\n * Hide the message drawer.\n *\n * @param {Object} root The message drawer container.\n */\n var hide = function(root) {\n var drawerRoot = Drawer.getDrawerRoot(root);\n if (drawerRoot.length) {\n Drawer.hide(drawerRoot);\n }\n };\n\n /**\n * Check if the drawer is visible.\n *\n * @param {Object} root The message drawer container.\n * @return {boolean}\n */\n var isVisible = function(root) {\n var drawerRoot = Drawer.getDrawerRoot(root);\n if (drawerRoot.length) {\n return Drawer.isVisible(drawerRoot);\n }\n return true;\n };\n\n /**\n * Set Jump from button\n *\n * @param {String} buttonid The originating button id\n */\n var setJumpFrom = function(buttonid) {\n $(SELECTORS.DRAWER).attr('data-origin', buttonid);\n };\n\n /**\n * Listen to and handle events for routing, showing and hiding the message drawer.\n *\n * @param {string} namespace The route namespace.\n * @param {Object} root The message drawer container.\n * @param {bool} alwaysVisible Is this messaging app always shown?\n */\n var registerEventListeners = function(namespace, root, alwaysVisible) {\n CustomEvents.define(root, [CustomEvents.events.activate]);\n var paramRegex = /^data-route-param-?(\\d*)$/;\n\n root.on(CustomEvents.events.activate, SELECTORS.ROUTES, function(e, data) {\n var element = $(e.target).closest(SELECTORS.ROUTES);\n var route = element.attr('data-route');\n var attributes = [];\n\n for (var i = 0; i < element[0].attributes.length; i++) {\n attributes.push(element[0].attributes[i]);\n }\n\n var paramAttributes = attributes.filter(function(attribute) {\n var name = attribute.nodeName;\n var match = paramRegex.test(name);\n return match;\n });\n paramAttributes.sort(function(a, b) {\n var aParts = paramRegex.exec(a.nodeName);\n var bParts = paramRegex.exec(b.nodeName);\n var aIndex = aParts.length > 1 ? aParts[1] : 0;\n var bIndex = bParts.length > 1 ? bParts[1] : 0;\n\n if (aIndex < bIndex) {\n return -1;\n } else if (bIndex < aIndex) {\n return 1;\n } else {\n return 0;\n }\n });\n\n var params = paramAttributes.map(function(attribute) {\n return attribute.nodeValue;\n });\n\n var routeParams = [namespace, route].concat(params);\n\n Router.go.apply(null, routeParams);\n\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ROUTES_BACK, function(e, data) {\n Router.back(namespace);\n\n data.originalEvent.preventDefault();\n });\n\n // These are theme-specific to help us fix random behat fails.\n // These events target those events defined in BS3 and BS4 onwards.\n root[0].querySelectorAll('.collapse').forEach((collapse) => {\n collapse.addEventListener('hide.bs.collapse', (e) => {\n var pendingPromise = new Pending();\n e.target.addEventListener('hidden.bs.collapse', function() {\n pendingPromise.resolve();\n }, {once: true});\n });\n });\n\n root[0].querySelectorAll('.collapse').forEach((collapse) => {\n collapse.addEventListener('show.bs.collapse', (e) => {\n var pendingPromise = new Pending();\n e.target.addEventListener('shown.bs.collapse', function() {\n pendingPromise.resolve();\n }, {once: true});\n });\n });\n\n $(SELECTORS.JUMPTO).focus(function() {\n var firstInput = root.find(SELECTORS.CLOSE_BUTTON);\n if (firstInput.length) {\n firstInput.focus();\n } else {\n $(SELECTORS.HEADER_CONTAINER).find(SELECTORS.ROUTES_BACK).focus();\n }\n });\n\n $(SELECTORS.DRAWER).focus(function() {\n var button = $(this).attr('data-origin');\n if (button) {\n $('#' + button).focus();\n }\n });\n\n if (!alwaysVisible) {\n PubSub.subscribe(Events.SHOW, function() {\n show(namespace, root);\n });\n\n PubSub.subscribe(Events.HIDE, function() {\n hide(root);\n });\n\n PubSub.subscribe(Events.TOGGLE_VISIBILITY, function(buttonid) {\n if (isVisible(root)) {\n hide(root);\n $(SELECTORS.JUMPTO).attr('tabindex', -1);\n } else {\n show(namespace, root);\n setJumpFrom(buttonid);\n $(SELECTORS.JUMPTO).attr('tabindex', 0);\n }\n });\n }\n\n PubSub.subscribe(Events.SHOW_CONVERSATION, function(args) {\n setJumpFrom(args.buttonid);\n show(namespace, root);\n Router.go(namespace, Routes.VIEW_CONVERSATION, args.conversationid);\n });\n\n var closebutton = root.find(SELECTORS.CLOSE_BUTTON);\n closebutton.on(CustomEvents.events.activate, function(e, data) {\n data.originalEvent.preventDefault();\n\n var button = $(SELECTORS.DRAWER).attr('data-origin');\n if (button) {\n $('#' + button).focus();\n }\n PubSub.publish(Events.TOGGLE_VISIBILITY);\n });\n\n PubSub.subscribe(Events.CREATE_CONVERSATION_WITH_USER, function(args) {\n setJumpFrom(args.buttonid);\n show(namespace, root);\n Router.go(namespace, Routes.VIEW_CONVERSATION, null, 'create', args.userid);\n });\n\n PubSub.subscribe(Events.SHOW_SETTINGS, function() {\n show(namespace, root);\n Router.go(namespace, Routes.VIEW_SETTINGS);\n });\n\n PubSub.subscribe(Events.PREFERENCES_UPDATED, function(preferences) {\n var filteredPreferences = preferences.filter(function(preference) {\n return preference.type == 'message_entertosend';\n });\n var enterToSendPreference = filteredPreferences.length ? filteredPreferences[0] : null;\n\n if (enterToSendPreference) {\n var viewConversationFooter = root.find(SELECTORS.FOOTER_CONTAINER).find(SELECTORS.VIEW_CONVERSATION);\n viewConversationFooter.attr('data-enter-to-send', enterToSendPreference.value);\n }\n });\n };\n\n /**\n * Initialise the message drawer.\n *\n * @param {Object} root The message drawer container.\n * @param {String} uniqueId Unique identifier for the Routes\n * @param {bool} alwaysVisible Should we show the app now, or wait for the user?\n * @param {Object} route\n */\n var init = function(root, uniqueId, alwaysVisible, route) {\n root = $(root);\n createRoutes(uniqueId, root);\n registerEventListeners(uniqueId, root, alwaysVisible);\n\n if (alwaysVisible) {\n show(uniqueId, root);\n\n if (route) {\n var routeParams = route.params || [];\n routeParams = [uniqueId, route.path].concat(routeParams);\n Router.go.apply(null, routeParams);\n }\n }\n\n // Mark the drawer as ready.\n Helper.markDrawerReady();\n };\n\n return {\n init: init,\n };\n});\n"],"names":["define","$","CustomEvents","PubSub","ViewContact","ViewContacts","ViewConversation","ViewGroupInfo","ViewOverview","ViewSearch","ViewSettings","Router","Routes","Events","Helper","Pending","Drawer","SELECTORS","routes","VIEW_CONTACT","show","description","VIEW_CONTACTS","VIEW_CONVERSATION","VIEW_GROUP_INFO","VIEW_OVERVIEW","VIEW_SEARCH","VIEW_SETTINGS","createRoutes","namespace","root","forEach","route","add","selector","header","find","length","body","footer","getParametersForRoute","attr","go","drawerRoot","getDrawerRoot","hide","setJumpFrom","buttonid","registerEventListeners","alwaysVisible","events","activate","paramRegex","on","e","data","element","target","closest","attributes","i","push","paramAttributes","filter","attribute","name","nodeName","test","sort","a","b","aParts","exec","bParts","aIndex","bIndex","params","map","nodeValue","routeParams","concat","apply","originalEvent","preventDefault","back","querySelectorAll","collapse","addEventListener","pendingPromise","resolve","once","focus","firstInput","button","this","subscribe","SHOW","HIDE","TOGGLE_VISIBILITY","isVisible","SHOW_CONVERSATION","args","conversationid","publish","CREATE_CONVERSATION_WITH_USER","userid","SHOW_SETTINGS","PREFERENCES_UPDATED","preferences","filteredPreferences","preference","type","enterToSendPreference","value","init","uniqueId","path","markDrawerReady"],"mappings":"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"} \ No newline at end of file diff --git a/message/amd/build/message_drawer_view_contacts.min.js b/message/amd/build/message_drawer_view_contacts.min.js index eff99c1fe04e6..13c572ade8c84 100644 --- a/message/amd/build/message_drawer_view_contacts.min.js +++ b/message/amd/build/message_drawer_view_contacts.min.js @@ -5,6 +5,6 @@ * @copyright 2018 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("core_message/message_drawer_view_contacts",["jquery","core/pubsub","core/str","core_message/message_drawer_events","core_message/message_drawer_view_contacts_section_contacts","core_message/message_drawer_view_contacts_section_requests"],(function($,PubSub,Str,MessageDrawerEvents,ContactsSection,RequestsSection){var SELECTORS_ACTION_SHOW_CONTACTS_SECTION='[data-action="show-contacts-section"]',SELECTORS_ACTION_SHOW_REQUESTS_SECTION='[data-action="show-requests-section"]',SELECTORS_CONTACT_REQUEST_COUNT='[data-region="contact-request-count"]',SELECTORS_CONTACTS_SECTION_CONTAINER='[data-section="contacts"]',SELECTORS_REQUESTS_SECTION_CONTAINER='[data-section="requests"]',getContactsSectionContainer=function(body){return body.find(SELECTORS_CONTACTS_SECTION_CONTAINER)},getRequestsSectionContainer=function(body){return body.find(SELECTORS_REQUESTS_SECTION_CONTAINER)},getShowContactsAction=function(body){return body.find(SELECTORS_ACTION_SHOW_CONTACTS_SECTION)},getShowRequestsAction=function(body){return body.find(SELECTORS_ACTION_SHOW_REQUESTS_SECTION)},decrementContactRequestCount=function(body){return function(){var countContainer=body.find(SELECTORS_CONTACT_REQUEST_COUNT),count=parseInt(countContainer.text(),10);(count=isNaN(count)?0:count-1)<=0?countContainer.addClass("hidden"):countContainer.text(count)}};return{show:function(namespace,header,body,footer,tab){(body=$(body)).attr("data-contacts-init")||(!function(body){var contactsSection=getContactsSectionContainer(body),requestsSection=getRequestsSectionContainer(body),showContactsAction=getShowContactsAction(body),showRequestsAction=getShowRequestsAction(body);showContactsAction.on("show.bs.tab",(function(){ContactsSection.show(contactsSection)})),showRequestsAction.on("show.bs.tab",(function(){RequestsSection.show(requestsSection)})),PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_ACCEPTED,decrementContactRequestCount(body)),PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_DECLINED,decrementContactRequestCount(body))}(body),body.attr("data-contacts-init",!0));var contactsSection=getContactsSectionContainer(body),requestsSection=getRequestsSectionContainer(body);if(tab){var showContactsAction=getShowContactsAction(body),showRequestsAction=getShowRequestsAction(body);"requests"==tab?(showContactsAction.removeClass("active"),contactsSection.removeClass("show active"),showRequestsAction.addClass("active"),requestsSection.addClass("show active")):(showRequestsAction.removeClass("active"),requestsSection.removeClass("show active"),showContactsAction.addClass("active"),contactsSection.addClass("show active"))}return contactsSection.hasClass("active")?ContactsSection.show(contactsSection):RequestsSection.show(requestsSection),$.Deferred().resolve().promise()},description:function(){return Str.get_string("messagedrawerviewcontacts","core_message")}}})); +define("core_message/message_drawer_view_contacts",["jquery","core/pubsub","core/str","core_message/message_drawer_events","core_message/message_drawer_view_contacts_section_contacts","core_message/message_drawer_view_contacts_section_requests"],(function($,PubSub,Str,MessageDrawerEvents,ContactsSection,RequestsSection){var SELECTORS_ACTION_SHOW_CONTACTS_SECTION='[data-action="show-contacts-section"]',SELECTORS_ACTION_SHOW_REQUESTS_SECTION='[data-action="show-requests-section"]',SELECTORS_CONTACT_REQUEST_COUNT='[data-region="contact-request-count"]',SELECTORS_CONTACTS_SECTION_CONTAINER='[data-section="contacts"]',SELECTORS_REQUESTS_SECTION_CONTAINER='[data-section="requests"]',getContactsSectionContainer=function(body){return body.find(SELECTORS_CONTACTS_SECTION_CONTAINER)},getRequestsSectionContainer=function(body){return body.find(SELECTORS_REQUESTS_SECTION_CONTAINER)},getShowContactsAction=function(body){return body.find(SELECTORS_ACTION_SHOW_CONTACTS_SECTION)},getShowRequestsAction=function(body){return body.find(SELECTORS_ACTION_SHOW_REQUESTS_SECTION)},decrementContactRequestCount=function(body){return function(){var countContainer=body.find(SELECTORS_CONTACT_REQUEST_COUNT),count=parseInt(countContainer.text(),10);(count=isNaN(count)?0:count-1)<=0?countContainer.addClass("hidden"):countContainer.text(count)}};return{show:function(namespace,header,body,footer,tab){(body=$(body)).attr("data-contacts-init")||(!function(body){var contactsSection=getContactsSectionContainer(body),requestsSection=getRequestsSectionContainer(body),showContactsAction=getShowContactsAction(body),showRequestsAction=getShowRequestsAction(body);showContactsAction[0].addEventListener("show.bs.tab",(function(){ContactsSection.show(contactsSection)})),showRequestsAction[0].addEventListener("show.bs.tab",(function(){RequestsSection.show(requestsSection)})),PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_ACCEPTED,decrementContactRequestCount(body)),PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_DECLINED,decrementContactRequestCount(body))}(body),body.attr("data-contacts-init",!0));var contactsSection=getContactsSectionContainer(body),requestsSection=getRequestsSectionContainer(body);if(tab){var showContactsAction=getShowContactsAction(body),showRequestsAction=getShowRequestsAction(body);"requests"==tab?(showContactsAction.removeClass("active"),contactsSection.removeClass("show active"),showRequestsAction.addClass("active"),requestsSection.addClass("show active")):(showRequestsAction.removeClass("active"),requestsSection.removeClass("show active"),showContactsAction.addClass("active"),contactsSection.addClass("show active"))}return contactsSection.hasClass("active")?ContactsSection.show(contactsSection):RequestsSection.show(requestsSection),$.Deferred().resolve().promise()},description:function(){return Str.get_string("messagedrawerviewcontacts","core_message")}}})); //# sourceMappingURL=message_drawer_view_contacts.min.js.map \ No newline at end of file diff --git a/message/amd/build/message_drawer_view_contacts.min.js.map b/message/amd/build/message_drawer_view_contacts.min.js.map index 70c35fd8bad04..c6bafd66c73e8 100644 --- a/message/amd/build/message_drawer_view_contacts.min.js.map +++ b/message/amd/build/message_drawer_view_contacts.min.js.map @@ -1 +1 @@ -{"version":3,"file":"message_drawer_view_contacts.min.js","sources":["../src/message_drawer_view_contacts.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 * Controls the contacts page of the message drawer.\n *\n * @module core_message/message_drawer_view_contacts\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/pubsub',\n 'core/str',\n 'core_message/message_drawer_events',\n 'core_message/message_drawer_view_contacts_section_contacts',\n 'core_message/message_drawer_view_contacts_section_requests'\n],\nfunction(\n $,\n PubSub,\n Str,\n MessageDrawerEvents,\n ContactsSection,\n RequestsSection\n) {\n\n var SELECTORS = {\n ACTION_SHOW_CONTACTS_SECTION: '[data-action=\"show-contacts-section\"]',\n ACTION_SHOW_REQUESTS_SECTION: '[data-action=\"show-requests-section\"]',\n CONTACT_REQUEST_COUNT: '[data-region=\"contact-request-count\"]',\n CONTACTS_SECTION_CONTAINER: '[data-section=\"contacts\"]',\n REQUESTS_SECTION_CONTAINER: '[data-section=\"requests\"]',\n };\n\n /**\n * Get the container element for the contacts section.\n *\n * @param {Object} body Contacts page body element.\n * @return {Object}\n */\n var getContactsSectionContainer = function(body) {\n return body.find(SELECTORS.CONTACTS_SECTION_CONTAINER);\n };\n\n /**\n * Get the container element for the requests section.\n *\n * @param {Object} body Contacts page body element.\n * @return {Object}\n */\n var getRequestsSectionContainer = function(body) {\n return body.find(SELECTORS.REQUESTS_SECTION_CONTAINER);\n };\n\n /**\n * Get the element that triggers showing the contacts section.\n *\n * @param {Object} body Contacts page body element.\n * @return {Object}\n */\n var getShowContactsAction = function(body) {\n return body.find(SELECTORS.ACTION_SHOW_CONTACTS_SECTION);\n };\n\n /**\n * Get the element that triggers showing the requests section.\n *\n * @param {Object} body Contacts page body element.\n * @return {Object}\n */\n var getShowRequestsAction = function(body) {\n return body.find(SELECTORS.ACTION_SHOW_REQUESTS_SECTION);\n };\n\n /**\n * Check if the given section is visible.\n *\n * @param {Object} sectionRoot The root element for the section\n * @return {Bool}\n */\n var isSectionVisible = function(sectionRoot) {\n return sectionRoot.hasClass('active');\n };\n\n /**\n * Decrement the contact request count. If the count is zero or below then\n * hide the count.\n *\n * @param {Object} body Conversation body container element.\n * @return {Function} A function to handle decrementing the count.\n */\n var decrementContactRequestCount = function(body) {\n return function() {\n var countContainer = body.find(SELECTORS.CONTACT_REQUEST_COUNT);\n var count = parseInt(countContainer.text(), 10);\n count = isNaN(count) ? 0 : count - 1;\n\n if (count <= 0) {\n countContainer.addClass('hidden');\n } else {\n countContainer.text(count);\n }\n };\n };\n\n /**\n * Listen to and handle events for contacts.\n *\n * @param {Object} body Contacts body container element.\n */\n var registerEventListeners = function(body) {\n var contactsSection = getContactsSectionContainer(body);\n var requestsSection = getRequestsSectionContainer(body);\n var showContactsAction = getShowContactsAction(body);\n var showRequestsAction = getShowRequestsAction(body);\n\n showContactsAction.on('show.bs.tab', function() {\n ContactsSection.show(contactsSection);\n });\n\n showRequestsAction.on('show.bs.tab', function() {\n RequestsSection.show(requestsSection);\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_ACCEPTED, decrementContactRequestCount(body));\n PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_DECLINED, decrementContactRequestCount(body));\n };\n\n /**\n * Setup the contact page.\n *\n * @param {string} namespace The route namespace.\n * @param {Object} header Contacts header container element.\n * @param {Object} body Contacts body container element.\n * @param {Object} footer Contacts footer container element.\n * @param {String|null} tab Tab to show, either 'requests' or 'contacts', if any.\n * @return {Object} jQuery promise\n */\n var show = function(namespace, header, body, footer, tab) {\n body = $(body);\n\n if (!body.attr('data-contacts-init')) {\n registerEventListeners(body);\n body.attr('data-contacts-init', true);\n }\n\n var contactsSection = getContactsSectionContainer(body);\n var requestsSection = getRequestsSectionContainer(body);\n\n if (tab) {\n var showContactsAction = getShowContactsAction(body);\n var showRequestsAction = getShowRequestsAction(body);\n\n // Unfortunately we need to hardcode the class changes here rather than trigger\n // the bootstrap tab functionality because the bootstrap JS doesn't appear to be\n // loaded by this point which means the tab plugin isn't added and the event listeners\n // haven't been set up so we can't just trigger a click either.\n if (tab == 'requests') {\n showContactsAction.removeClass('active');\n contactsSection.removeClass('show active');\n showRequestsAction.addClass('active');\n requestsSection.addClass('show active');\n } else {\n showRequestsAction.removeClass('active');\n requestsSection.removeClass('show active');\n showContactsAction.addClass('active');\n contactsSection.addClass('show active');\n }\n }\n\n if (isSectionVisible(contactsSection)) {\n ContactsSection.show(contactsSection);\n } else {\n RequestsSection.show(requestsSection);\n }\n\n return $.Deferred().resolve().promise();\n };\n\n /**\n * String describing this page used for aria-labels.\n *\n * @return {Object} jQuery promise\n */\n var description = function() {\n return Str.get_string('messagedrawerviewcontacts', 'core_message');\n };\n\n return {\n show: show,\n description: description\n };\n});\n"],"names":["define","$","PubSub","Str","MessageDrawerEvents","ContactsSection","RequestsSection","SELECTORS","getContactsSectionContainer","body","find","getRequestsSectionContainer","getShowContactsAction","getShowRequestsAction","decrementContactRequestCount","countContainer","count","parseInt","text","isNaN","addClass","show","namespace","header","footer","tab","attr","contactsSection","requestsSection","showContactsAction","showRequestsAction","on","subscribe","CONTACT_REQUEST_ACCEPTED","CONTACT_REQUEST_DECLINED","registerEventListeners","removeClass","hasClass","Deferred","resolve","promise","description","get_string"],"mappings":";;;;;;;AAsBAA,mDACA,CACI,SACA,cACA,WACA,qCACA,6DACA,+DAEJ,SACIC,EACAC,OACAC,IACAC,oBACAC,gBACAC,qBAGIC,uCAC8B,wCAD9BA,uCAE8B,wCAF9BA,gCAGuB,wCAHvBA,qCAI4B,4BAJ5BA,qCAK4B,4BAS5BC,4BAA8B,SAASC,aAChCA,KAAKC,KAAKH,uCASjBI,4BAA8B,SAASF,aAChCA,KAAKC,KAAKH,uCASjBK,sBAAwB,SAASH,aAC1BA,KAAKC,KAAKH,yCASjBM,sBAAwB,SAASJ,aAC1BA,KAAKC,KAAKH,yCAoBjBO,6BAA+B,SAASL,aACjC,eACCM,eAAiBN,KAAKC,KAAKH,iCAC3BS,MAAQC,SAASF,eAAeG,OAAQ,KAC5CF,MAAQG,MAAMH,OAAS,EAAIA,MAAQ,IAEtB,EACTD,eAAeK,SAAS,UAExBL,eAAeG,KAAKF,eAwFzB,CACHK,KAnDO,SAASC,UAAWC,OAAQd,KAAMe,OAAQC,MACjDhB,KAAOR,EAAEQ,OAECiB,KAAK,yBA/BU,SAASjB,UAC9BkB,gBAAkBnB,4BAA4BC,MAC9CmB,gBAAkBjB,4BAA4BF,MAC9CoB,mBAAqBjB,sBAAsBH,MAC3CqB,mBAAqBjB,sBAAsBJ,MAE/CoB,mBAAmBE,GAAG,eAAe,WACjC1B,gBAAgBgB,KAAKM,oBAGzBG,mBAAmBC,GAAG,eAAe,WACjCzB,gBAAgBe,KAAKO,oBAGzB1B,OAAO8B,UAAU5B,oBAAoB6B,yBAA0BnB,6BAA6BL,OAC5FP,OAAO8B,UAAU5B,oBAAoB8B,yBAA0BpB,6BAA6BL,OAiBxF0B,CAAuB1B,MACvBA,KAAKiB,KAAK,sBAAsB,QAGhCC,gBAAkBnB,4BAA4BC,MAC9CmB,gBAAkBjB,4BAA4BF,SAE9CgB,IAAK,KACDI,mBAAqBjB,sBAAsBH,MAC3CqB,mBAAqBjB,sBAAsBJ,MAMpC,YAAPgB,KACAI,mBAAmBO,YAAY,UAC/BT,gBAAgBS,YAAY,eAC5BN,mBAAmBV,SAAS,UAC5BQ,gBAAgBR,SAAS,iBAEzBU,mBAAmBM,YAAY,UAC/BR,gBAAgBQ,YAAY,eAC5BP,mBAAmBT,SAAS,UAC5BO,gBAAgBP,SAAS,uBAIZO,gBAzFFU,SAAS,UA0FxBhC,gBAAgBgB,KAAKM,iBAErBrB,gBAAgBe,KAAKO,iBAGlB3B,EAAEqC,WAAWC,UAAUC,WAc9BC,YANc,kBACPtC,IAAIuC,WAAW,4BAA6B"} \ No newline at end of file +{"version":3,"file":"message_drawer_view_contacts.min.js","sources":["../src/message_drawer_view_contacts.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 * Controls the contacts page of the message drawer.\n *\n * @module core_message/message_drawer_view_contacts\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/pubsub',\n 'core/str',\n 'core_message/message_drawer_events',\n 'core_message/message_drawer_view_contacts_section_contacts',\n 'core_message/message_drawer_view_contacts_section_requests'\n],\nfunction(\n $,\n PubSub,\n Str,\n MessageDrawerEvents,\n ContactsSection,\n RequestsSection\n) {\n\n var SELECTORS = {\n ACTION_SHOW_CONTACTS_SECTION: '[data-action=\"show-contacts-section\"]',\n ACTION_SHOW_REQUESTS_SECTION: '[data-action=\"show-requests-section\"]',\n CONTACT_REQUEST_COUNT: '[data-region=\"contact-request-count\"]',\n CONTACTS_SECTION_CONTAINER: '[data-section=\"contacts\"]',\n REQUESTS_SECTION_CONTAINER: '[data-section=\"requests\"]',\n };\n\n /**\n * Get the container element for the contacts section.\n *\n * @param {Object} body Contacts page body element.\n * @return {Object}\n */\n var getContactsSectionContainer = function(body) {\n return body.find(SELECTORS.CONTACTS_SECTION_CONTAINER);\n };\n\n /**\n * Get the container element for the requests section.\n *\n * @param {Object} body Contacts page body element.\n * @return {Object}\n */\n var getRequestsSectionContainer = function(body) {\n return body.find(SELECTORS.REQUESTS_SECTION_CONTAINER);\n };\n\n /**\n * Get the element that triggers showing the contacts section.\n *\n * @param {Object} body Contacts page body element.\n * @return {Object}\n */\n var getShowContactsAction = function(body) {\n return body.find(SELECTORS.ACTION_SHOW_CONTACTS_SECTION);\n };\n\n /**\n * Get the element that triggers showing the requests section.\n *\n * @param {Object} body Contacts page body element.\n * @return {Object}\n */\n var getShowRequestsAction = function(body) {\n return body.find(SELECTORS.ACTION_SHOW_REQUESTS_SECTION);\n };\n\n /**\n * Check if the given section is visible.\n *\n * @param {Object} sectionRoot The root element for the section\n * @return {Bool}\n */\n var isSectionVisible = function(sectionRoot) {\n return sectionRoot.hasClass('active');\n };\n\n /**\n * Decrement the contact request count. If the count is zero or below then\n * hide the count.\n *\n * @param {Object} body Conversation body container element.\n * @return {Function} A function to handle decrementing the count.\n */\n var decrementContactRequestCount = function(body) {\n return function() {\n var countContainer = body.find(SELECTORS.CONTACT_REQUEST_COUNT);\n var count = parseInt(countContainer.text(), 10);\n count = isNaN(count) ? 0 : count - 1;\n\n if (count <= 0) {\n countContainer.addClass('hidden');\n } else {\n countContainer.text(count);\n }\n };\n };\n\n /**\n * Listen to and handle events for contacts.\n *\n * @param {Object} body Contacts body container element.\n */\n var registerEventListeners = function(body) {\n var contactsSection = getContactsSectionContainer(body);\n var requestsSection = getRequestsSectionContainer(body);\n var showContactsAction = getShowContactsAction(body);\n var showRequestsAction = getShowRequestsAction(body);\n\n showContactsAction[0].addEventListener('show.bs.tab', function() {\n ContactsSection.show(contactsSection);\n });\n\n showRequestsAction[0].addEventListener('show.bs.tab', function() {\n RequestsSection.show(requestsSection);\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_ACCEPTED, decrementContactRequestCount(body));\n PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_DECLINED, decrementContactRequestCount(body));\n };\n\n /**\n * Setup the contact page.\n *\n * @param {string} namespace The route namespace.\n * @param {Object} header Contacts header container element.\n * @param {Object} body Contacts body container element.\n * @param {Object} footer Contacts footer container element.\n * @param {String|null} tab Tab to show, either 'requests' or 'contacts', if any.\n * @return {Object} jQuery promise\n */\n var show = function(namespace, header, body, footer, tab) {\n body = $(body);\n\n if (!body.attr('data-contacts-init')) {\n registerEventListeners(body);\n body.attr('data-contacts-init', true);\n }\n\n var contactsSection = getContactsSectionContainer(body);\n var requestsSection = getRequestsSectionContainer(body);\n\n if (tab) {\n var showContactsAction = getShowContactsAction(body);\n var showRequestsAction = getShowRequestsAction(body);\n\n // Unfortunately we need to hardcode the class changes here rather than trigger\n // the bootstrap tab functionality because the bootstrap JS doesn't appear to be\n // loaded by this point which means the tab plugin isn't added and the event listeners\n // haven't been set up so we can't just trigger a click either.\n if (tab == 'requests') {\n showContactsAction.removeClass('active');\n contactsSection.removeClass('show active');\n showRequestsAction.addClass('active');\n requestsSection.addClass('show active');\n } else {\n showRequestsAction.removeClass('active');\n requestsSection.removeClass('show active');\n showContactsAction.addClass('active');\n contactsSection.addClass('show active');\n }\n }\n\n if (isSectionVisible(contactsSection)) {\n ContactsSection.show(contactsSection);\n } else {\n RequestsSection.show(requestsSection);\n }\n\n return $.Deferred().resolve().promise();\n };\n\n /**\n * String describing this page used for aria-labels.\n *\n * @return {Object} jQuery promise\n */\n var description = function() {\n return Str.get_string('messagedrawerviewcontacts', 'core_message');\n };\n\n return {\n show: show,\n description: description\n };\n});\n"],"names":["define","$","PubSub","Str","MessageDrawerEvents","ContactsSection","RequestsSection","SELECTORS","getContactsSectionContainer","body","find","getRequestsSectionContainer","getShowContactsAction","getShowRequestsAction","decrementContactRequestCount","countContainer","count","parseInt","text","isNaN","addClass","show","namespace","header","footer","tab","attr","contactsSection","requestsSection","showContactsAction","showRequestsAction","addEventListener","subscribe","CONTACT_REQUEST_ACCEPTED","CONTACT_REQUEST_DECLINED","registerEventListeners","removeClass","hasClass","Deferred","resolve","promise","description","get_string"],"mappings":";;;;;;;AAsBAA,mDACA,CACI,SACA,cACA,WACA,qCACA,6DACA,+DAEJ,SACIC,EACAC,OACAC,IACAC,oBACAC,gBACAC,qBAGIC,uCAC8B,wCAD9BA,uCAE8B,wCAF9BA,gCAGuB,wCAHvBA,qCAI4B,4BAJ5BA,qCAK4B,4BAS5BC,4BAA8B,SAASC,aAChCA,KAAKC,KAAKH,uCASjBI,4BAA8B,SAASF,aAChCA,KAAKC,KAAKH,uCASjBK,sBAAwB,SAASH,aAC1BA,KAAKC,KAAKH,yCASjBM,sBAAwB,SAASJ,aAC1BA,KAAKC,KAAKH,yCAoBjBO,6BAA+B,SAASL,aACjC,eACCM,eAAiBN,KAAKC,KAAKH,iCAC3BS,MAAQC,SAASF,eAAeG,OAAQ,KAC5CF,MAAQG,MAAMH,OAAS,EAAIA,MAAQ,IAEtB,EACTD,eAAeK,SAAS,UAExBL,eAAeG,KAAKF,eAwFzB,CACHK,KAnDO,SAASC,UAAWC,OAAQd,KAAMe,OAAQC,MACjDhB,KAAOR,EAAEQ,OAECiB,KAAK,yBA/BU,SAASjB,UAC9BkB,gBAAkBnB,4BAA4BC,MAC9CmB,gBAAkBjB,4BAA4BF,MAC9CoB,mBAAqBjB,sBAAsBH,MAC3CqB,mBAAqBjB,sBAAsBJ,MAE/CoB,mBAAmB,GAAGE,iBAAiB,eAAe,WAClD1B,gBAAgBgB,KAAKM,oBAGzBG,mBAAmB,GAAGC,iBAAiB,eAAe,WAClDzB,gBAAgBe,KAAKO,oBAGzB1B,OAAO8B,UAAU5B,oBAAoB6B,yBAA0BnB,6BAA6BL,OAC5FP,OAAO8B,UAAU5B,oBAAoB8B,yBAA0BpB,6BAA6BL,OAiBxF0B,CAAuB1B,MACvBA,KAAKiB,KAAK,sBAAsB,QAGhCC,gBAAkBnB,4BAA4BC,MAC9CmB,gBAAkBjB,4BAA4BF,SAE9CgB,IAAK,KACDI,mBAAqBjB,sBAAsBH,MAC3CqB,mBAAqBjB,sBAAsBJ,MAMpC,YAAPgB,KACAI,mBAAmBO,YAAY,UAC/BT,gBAAgBS,YAAY,eAC5BN,mBAAmBV,SAAS,UAC5BQ,gBAAgBR,SAAS,iBAEzBU,mBAAmBM,YAAY,UAC/BR,gBAAgBQ,YAAY,eAC5BP,mBAAmBT,SAAS,UAC5BO,gBAAgBP,SAAS,uBAIZO,gBAzFFU,SAAS,UA0FxBhC,gBAAgBgB,KAAKM,iBAErBrB,gBAAgBe,KAAKO,iBAGlB3B,EAAEqC,WAAWC,UAAUC,WAc9BC,YANc,kBACPtC,IAAIuC,WAAW,4BAA6B"} \ No newline at end of file diff --git a/message/amd/build/message_drawer_view_overview_section.min.js b/message/amd/build/message_drawer_view_overview_section.min.js index 0da24906fd335..abaa2d17204c4 100644 --- a/message/amd/build/message_drawer_view_overview_section.min.js +++ b/message/amd/build/message_drawer_view_overview_section.min.js @@ -5,6 +5,6 @@ * @copyright 2018 Ryan Wyllie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("core_message/message_drawer_view_overview_section",["jquery","core/custom_interaction_events","core/notification","core/pubsub","core/str","core/pending","core/templates","core/user_date","core_message/message_repository","core_message/message_drawer_events","core_message/message_drawer_router","core_message/message_drawer_routes","core_message/message_drawer_lazy_load_list","core_message/message_drawer_view_conversation_constants"],(function($,CustomEvents,Notification,PubSub,Str,Pending,Templates,UserDate,MessageRepository,MessageDrawerEvents,MessageDrawerRouter,MessageDrawerRoutes,LazyLoadList,MessageDrawerViewConversationContants){var SELECTORS_TOGGLE='[data-region="toggle"]',SELECTORS_CONVERSATION="[data-conversation-id]",SELECTORS_BLOCKED_ICON_CONTAINER='[data-region="contact-icon-blocked"]',SELECTORS_MUTED_ICON_CONTAINER='[data-region="muted-icon-container"]',SELECTORS_UNREAD_COUNT='[data-region="unread-count"]',SELECTORS_SECTION_TOTAL_COUNT='[data-region="section-total-count"]',SELECTORS_SECTION_TOTAL_COUNT_CONTAINER='[data-region="section-total-count-container"]',SELECTORS_SECTION_UNREAD_COUNT='[data-region="section-unread-count"]',SELECTORS_SECTION_UNREAD_COUNT_CONTAINER='[data-region="section-unread-count-container"]',SELECTORS_PLACEHOLDER_CONTAINER='[data-region="placeholder-container"]',TEMPLATES_CONVERSATIONS_LIST="core_message/message_drawer_conversations_list",TEMPLATES_CONVERSATIONS_LIST_ITEMS_PLACEHOLDER="core_message/message_drawer_conversations_list_items_placeholder",loadedConversationsById={},deletedConversationsById={},loadedTotalCounts=!1,loadedUnreadCounts=!1,isVisible=function(root){return LazyLoadList.getRoot(root).hasClass("show")},setExpanded=function(root){root.addClass("expanded")},formatConversationFromEvent=function(conversation){var recursivelyLowercaseKeys=function(object){return Object.keys(object).reduce((function(carry,key){return $.isArray(object[key])?carry[key.toLowerCase()]=object[key].map(recursivelyLowercaseKeys):carry[key.toLowerCase()]=object[key],carry}),{})},formatted=recursivelyLowercaseKeys(conversation);return formatted.messages=formatted.messages.map((function(message){return message.useridfrom=message.userfrom.id,message})),formatted},render=function(conversations,userId){var pending=new Pending,mapPromises=conversations.map((function(conversation){var lastMessage=conversation.messages.length?conversation.messages[conversation.messages.length-1]:null;return async function(lastMessage){if(!lastMessage)return null;var tmpElement=document.createElement("element");if(tmpElement.innerHTML=lastMessage.text.replace(/50?conversations=conversations.slice(0,-1):LazyLoadList.setLoadedAll(root,!0),offset+=50,conversations.forEach((function(conversation){loadedConversationsById[conversation.id]=conversation})),conversations})).catch(Notification.exception)}}(types,includeFavourites,0);if(registerEventListeners(namespace,root,loadCallback,types,includeFavourites,fromPanel),isVisible(root)){setExpanded(root);var listRoot=LazyLoadList.getRoot(root);LazyLoadList.show(listRoot,loadCallback,(function(contentContainer,conversations,userId){return render(conversations,userId).then((function(html){return contentContainer.append(html),html})).catch(Notification.exception)}))}totalCountPromise.then((function(count){!function(root,count){var container=root.find(SELECTORS_SECTION_TOTAL_COUNT_CONTAINER);container.find(SELECTORS_SECTION_TOTAL_COUNT).text(count),container.removeClass("hidden"),Str.get_string("totalconversations","core_message",count).done((function(string){$("#"+container.attr("aria-labelledby")).text(string)}));var numPlaceholders=count>20?20:count,placeholders=Array.apply(null,Array(numPlaceholders)).map((function(){return!0}));Templates.render(TEMPLATES_CONVERSATIONS_LIST_ITEMS_PLACEHOLDER,{placeholders:placeholders}).then((function(html){root.find(SELECTORS_PLACEHOLDER_CONTAINER).html(html)})).catch((function(){}))}(root,count),loadedTotalCounts=!0})).catch((function(){})),unreadCountPromise.then((function(count){!function(root,count){var container=root.find(SELECTORS_SECTION_UNREAD_COUNT_CONTAINER);container.find(SELECTORS_SECTION_UNREAD_COUNT).text(count),Str.get_string("unreadconversations","core_message",count).done((function(string){$("#"+container.attr("aria-labelledby")).text(string)})),count>0&&container.removeClass("hidden")}(root,count),loadedUnreadCounts=!0})).catch((function(){})),root.attr("data-init",!0)}},isVisible:isVisible}})); +define("core_message/message_drawer_view_overview_section",["jquery","core/custom_interaction_events","core/notification","core/pubsub","core/str","core/pending","core/templates","core/user_date","core_message/message_repository","core_message/message_drawer_events","core_message/message_drawer_router","core_message/message_drawer_routes","core_message/message_drawer_lazy_load_list","core_message/message_drawer_view_conversation_constants"],(function($,CustomEvents,Notification,PubSub,Str,Pending,Templates,UserDate,MessageRepository,MessageDrawerEvents,MessageDrawerRouter,MessageDrawerRoutes,LazyLoadList,MessageDrawerViewConversationContants){var SELECTORS_TOGGLE='[data-region="toggle"]',SELECTORS_CONVERSATION="[data-conversation-id]",SELECTORS_BLOCKED_ICON_CONTAINER='[data-region="contact-icon-blocked"]',SELECTORS_MUTED_ICON_CONTAINER='[data-region="muted-icon-container"]',SELECTORS_UNREAD_COUNT='[data-region="unread-count"]',SELECTORS_SECTION_TOTAL_COUNT='[data-region="section-total-count"]',SELECTORS_SECTION_TOTAL_COUNT_CONTAINER='[data-region="section-total-count-container"]',SELECTORS_SECTION_UNREAD_COUNT='[data-region="section-unread-count"]',SELECTORS_SECTION_UNREAD_COUNT_CONTAINER='[data-region="section-unread-count-container"]',SELECTORS_PLACEHOLDER_CONTAINER='[data-region="placeholder-container"]',TEMPLATES_CONVERSATIONS_LIST="core_message/message_drawer_conversations_list",TEMPLATES_CONVERSATIONS_LIST_ITEMS_PLACEHOLDER="core_message/message_drawer_conversations_list_items_placeholder",loadedConversationsById={},deletedConversationsById={},loadedTotalCounts=!1,loadedUnreadCounts=!1,isVisible=function(root){return LazyLoadList.getRoot(root).hasClass("show")},setExpanded=function(root){root.addClass("expanded")},formatConversationFromEvent=function(conversation){var recursivelyLowercaseKeys=function(object){return Object.keys(object).reduce((function(carry,key){return $.isArray(object[key])?carry[key.toLowerCase()]=object[key].map(recursivelyLowercaseKeys):carry[key.toLowerCase()]=object[key],carry}),{})},formatted=recursivelyLowercaseKeys(conversation);return formatted.messages=formatted.messages.map((function(message){return message.useridfrom=message.userfrom.id,message})),formatted},render=function(conversations,userId){var pending=new Pending,mapPromises=conversations.map((function(conversation){var lastMessage=conversation.messages.length?conversation.messages[conversation.messages.length-1]:null;return async function(lastMessage){if(!lastMessage)return null;var tmpElement=document.createElement("element");if(tmpElement.innerHTML=lastMessage.text.replace(/50?conversations=conversations.slice(0,-1):LazyLoadList.setLoadedAll(root,!0),offset+=50,conversations.forEach((function(conversation){loadedConversationsById[conversation.id]=conversation})),conversations})).catch(Notification.exception)}}(types,includeFavourites,0);if(registerEventListeners(namespace,root,loadCallback,types,includeFavourites,fromPanel),isVisible(root)){setExpanded(root);var listRoot=LazyLoadList.getRoot(root);LazyLoadList.show(listRoot,loadCallback,(function(contentContainer,conversations,userId){return render(conversations,userId).then((function(html){return contentContainer.append(html),html})).catch(Notification.exception)}))}totalCountPromise.then((function(count){!function(root,count){var container=root.find(SELECTORS_SECTION_TOTAL_COUNT_CONTAINER);container.find(SELECTORS_SECTION_TOTAL_COUNT).text(count),container.removeClass("hidden"),Str.get_string("totalconversations","core_message",count).done((function(string){$("#"+container.attr("aria-labelledby")).text(string)}));var numPlaceholders=count>20?20:count,placeholders=Array.apply(null,Array(numPlaceholders)).map((function(){return!0}));Templates.render(TEMPLATES_CONVERSATIONS_LIST_ITEMS_PLACEHOLDER,{placeholders:placeholders}).then((function(html){root.find(SELECTORS_PLACEHOLDER_CONTAINER).html(html)})).catch((function(){}))}(root,count),loadedTotalCounts=!0})).catch((function(){})),unreadCountPromise.then((function(count){!function(root,count){var container=root.find(SELECTORS_SECTION_UNREAD_COUNT_CONTAINER);container.find(SELECTORS_SECTION_UNREAD_COUNT).text(count),Str.get_string("unreadconversations","core_message",count).done((function(string){$("#"+container.attr("aria-labelledby")).text(string)})),count>0&&container.removeClass("hidden")}(root,count),loadedUnreadCounts=!0})).catch((function(){})),root.attr("data-init",!0)}},isVisible:isVisible}})); //# sourceMappingURL=message_drawer_view_overview_section.min.js.map \ No newline at end of file diff --git a/message/amd/build/message_drawer_view_overview_section.min.js.map b/message/amd/build/message_drawer_view_overview_section.min.js.map index 1d62f2c428cd5..61d0acbf99540 100644 --- a/message/amd/build/message_drawer_view_overview_section.min.js.map +++ b/message/amd/build/message_drawer_view_overview_section.min.js.map @@ -1 +1 @@ -{"version":3,"file":"message_drawer_view_overview_section.min.js","sources":["../src/message_drawer_view_overview_section.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 * Controls a section of the overview page in the message drawer.\n *\n * @module core_message/message_drawer_view_overview_section\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/custom_interaction_events',\n 'core/notification',\n 'core/pubsub',\n 'core/str',\n 'core/pending',\n 'core/templates',\n 'core/user_date',\n 'core_message/message_repository',\n 'core_message/message_drawer_events',\n 'core_message/message_drawer_router',\n 'core_message/message_drawer_routes',\n 'core_message/message_drawer_lazy_load_list',\n 'core_message/message_drawer_view_conversation_constants'\n],\nfunction(\n $,\n CustomEvents,\n Notification,\n PubSub,\n Str,\n Pending,\n Templates,\n UserDate,\n MessageRepository,\n MessageDrawerEvents,\n MessageDrawerRouter,\n MessageDrawerRoutes,\n LazyLoadList,\n MessageDrawerViewConversationContants\n) {\n\n var SELECTORS = {\n TOGGLE: '[data-region=\"toggle\"]',\n CONVERSATION: '[data-conversation-id]',\n BLOCKED_ICON_CONTAINER: '[data-region=\"contact-icon-blocked\"]',\n LAST_MESSAGE: '[data-region=\"last-message\"]',\n LAST_MESSAGE_DATE: '[data-region=\"last-message-date\"]',\n MUTED_ICON_CONTAINER: '[data-region=\"muted-icon-container\"]',\n UNREAD_COUNT: '[data-region=\"unread-count\"]',\n SECTION_TOTAL_COUNT: '[data-region=\"section-total-count\"]',\n SECTION_TOTAL_COUNT_CONTAINER: '[data-region=\"section-total-count-container\"]',\n SECTION_UNREAD_COUNT: '[data-region=\"section-unread-count\"]',\n SECTION_UNREAD_COUNT_CONTAINER: '[data-region=\"section-unread-count-container\"]',\n PLACEHOLDER_CONTAINER: '[data-region=\"placeholder-container\"]'\n };\n\n var TEMPLATES = {\n CONVERSATIONS_LIST: 'core_message/message_drawer_conversations_list',\n CONVERSATIONS_LIST_ITEMS_PLACEHOLDER: 'core_message/message_drawer_conversations_list_items_placeholder'\n };\n\n var LOAD_LIMIT = 50;\n var loadedConversationsById = {};\n var deletedConversationsById = {};\n var loadedTotalCounts = false;\n var loadedUnreadCounts = false;\n\n /**\n * Get the section visibility status.\n *\n * @param {Object} root The section container element.\n * @return {Bool} Is section visible.\n */\n var isVisible = function(root) {\n return LazyLoadList.getRoot(root).hasClass('show');\n };\n\n /**\n * Set this section as expanded.\n *\n * @param {Object} root The section container element.\n */\n var setExpanded = function(root) {\n root.addClass('expanded');\n };\n\n /**\n * Set this section as collapsed.\n *\n * @param {Object} root The section container element.\n */\n var setCollapsed = function(root) {\n root.removeClass('expanded');\n };\n\n /**\n * Render the total count value and show it for the user. Also update the placeholder\n * HTML for better visuals.\n *\n * @param {Object} root The section container element.\n * @param {Number} count The total count\n */\n var renderTotalCount = function(root, count) {\n var container = root.find(SELECTORS.SECTION_TOTAL_COUNT_CONTAINER);\n var countElement = container.find(SELECTORS.SECTION_TOTAL_COUNT);\n countElement.text(count);\n container.removeClass('hidden');\n Str.get_string('totalconversations', 'core_message', count).done(function(string) {\n $('#' + container.attr('aria-labelledby')).text(string);\n });\n\n var numPlaceholders = count > 20 ? 20 : count;\n // Array of \"true\" up to the number of placeholders we want.\n var placeholders = Array.apply(null, Array(numPlaceholders)).map(function() {\n return true;\n });\n\n // Replace the current placeholder (loading spinner) with some nicer placeholders that\n // better represent the content.\n Templates.render(TEMPLATES.CONVERSATIONS_LIST_ITEMS_PLACEHOLDER, {placeholders: placeholders})\n .then(function(html) {\n var placeholderContainer = root.find(SELECTORS.PLACEHOLDER_CONTAINER);\n placeholderContainer.html(html);\n return;\n })\n .catch(function() {\n // Silently ignore. Doesn't matter if we can't render the placeholders.\n });\n };\n\n /**\n * Render the unread count value and show it for the user if it's higher than zero.\n *\n * @param {Object} root The section container element.\n * @param {Number} count The unread count\n */\n var renderUnreadCount = function(root, count) {\n var container = root.find(SELECTORS.SECTION_UNREAD_COUNT_CONTAINER);\n var countElement = container.find(SELECTORS.SECTION_UNREAD_COUNT);\n countElement.text(count);\n\n Str.get_string('unreadconversations', 'core_message', count).done(function(string) {\n $('#' + container.attr('aria-labelledby')).text(string);\n });\n\n if (count > 0) {\n container.removeClass('hidden');\n }\n };\n\n /**\n * Create a formatted conversation object from the the one we get from events. The new object\n * will be in a format that matches what we receive from the server.\n *\n * @param {Object} conversation\n * @return {Object} formatted conversation.\n */\n var formatConversationFromEvent = function(conversation) {\n // Recursively lowercase all of the keys for an object.\n var recursivelyLowercaseKeys = function(object) {\n return Object.keys(object).reduce(function(carry, key) {\n if ($.isArray(object[key])) {\n carry[key.toLowerCase()] = object[key].map(recursivelyLowercaseKeys);\n } else {\n carry[key.toLowerCase()] = object[key];\n }\n\n return carry;\n }, {});\n };\n\n // Recursively lowercase all of the keys for the conversation.\n var formatted = recursivelyLowercaseKeys(conversation);\n\n // Make sure all messages have the useridfrom property set.\n formatted.messages = formatted.messages.map(function(message) {\n message.useridfrom = message.userfrom.id;\n return message;\n });\n\n return formatted;\n };\n\n /**\n * Render the messages in the overview page.\n *\n * @param {Array} conversations List of conversations to render.\n * @param {Number} userId Logged in user id.\n * @return {Object} jQuery promise.\n */\n var render = function(conversations, userId) {\n\n // Helper to format the last message for rendering.\n // Returns a promise which resolves to either a string, or null\n // (such as in the event of an empty personal space).\n var pending = new Pending();\n\n var formatMessagePreview = async function(lastMessage) {\n if (!lastMessage) {\n return null;\n }\n // Check the message html for a src attribute, indicative of media.\n // Replace LOAD_LIMIT) {\n conversations = conversations.slice(0, -1);\n } else {\n LazyLoadList.setLoadedAll(root, true);\n }\n\n offset = offset + LOAD_LIMIT;\n\n conversations.forEach(function(conversation) {\n loadedConversationsById[conversation.id] = conversation;\n });\n\n return conversations;\n })\n .catch(Notification.exception);\n };\n };\n\n /**\n * Get the total count container element.\n *\n * @param {Object} root Overview messages container element.\n * @return {Object} Total count container element.\n */\n var getTotalConversationCountElement = function(root) {\n return root.find(SELECTORS.SECTION_TOTAL_COUNT);\n };\n\n /**\n * Get the unread conversations count container element.\n *\n * @param {Object} root Overview messages container element.\n * @return {Object} Unread conversations count container element.\n */\n var getTotalUnreadConversationCountElement = function(root) {\n return root.find(SELECTORS.SECTION_UNREAD_COUNT);\n };\n\n /**\n * Increment the total conversations count.\n *\n * @param {Object} root Overview messages container element.\n */\n var incrementTotalConversationCount = function(root) {\n if (loadedTotalCounts) {\n var element = getTotalConversationCountElement(root);\n var count = parseInt(element.text());\n count = count + 1;\n element.text(count);\n }\n };\n\n /**\n * Decrement the total conversations count.\n *\n * @param {Object} root Overview messages container element.\n */\n var decrementTotalConversationCount = function(root) {\n if (loadedTotalCounts) {\n var element = getTotalConversationCountElement(root);\n var count = parseInt(element.text());\n count = count - 1;\n element.text(count);\n }\n };\n\n /**\n * Decrement the total unread conversations count.\n *\n * @param {Object} root Overview messages container element.\n */\n var decrementTotalUnreadConversationCount = function(root) {\n if (loadedUnreadCounts) {\n var element = getTotalUnreadConversationCountElement(root);\n var count = parseInt(element.text());\n count = count - 1;\n element.text(count);\n\n if (count < 1) {\n element.addClass('hidden');\n }\n }\n };\n\n /**\n * Get a contact / conversation element.\n *\n * @param {Object} root Overview messages container element.\n * @param {Number} conversationId The conversation id.\n * @return {Object} Conversation element.\n */\n var getConversationElement = function(root, conversationId) {\n return root.find('[data-conversation-id=\"' + conversationId + '\"]');\n };\n\n /**\n * Get a contact / conversation element from a user id.\n *\n * @param {Object} root Overview messages container element.\n * @param {Number} userId The user id.\n * @return {Object} Conversation element.\n */\n var getConversationElementFromUserId = function(root, userId) {\n return root.find('[data-user-id=\"' + userId + '\"]');\n };\n\n /**\n * Show the conversation is muted icon.\n *\n * @param {Object} conversationElement The conversation element.\n */\n var muteConversation = function(conversationElement) {\n conversationElement.find(SELECTORS.MUTED_ICON_CONTAINER).removeClass('hidden');\n };\n\n /**\n * Hide the conversation is muted icon.\n *\n * @param {Object} conversationElement The conversation element.\n */\n var unmuteConversation = function(conversationElement) {\n conversationElement.find(SELECTORS.MUTED_ICON_CONTAINER).addClass('hidden');\n };\n\n /**\n * Show the contact is blocked icon.\n *\n * @param {Object} conversationElement The conversation element.\n */\n var blockContact = function(conversationElement) {\n conversationElement.find(SELECTORS.BLOCKED_ICON_CONTAINER).removeClass('hidden');\n };\n\n /**\n * Hide the contact is blocked icon.\n *\n * @param {Object} conversationElement The conversation element.\n */\n var unblockContact = function(conversationElement) {\n conversationElement.find(SELECTORS.BLOCKED_ICON_CONTAINER).addClass('hidden');\n };\n\n /**\n * Create an render new conversation element in the list of conversations.\n *\n * @param {Object} root Overview messages container element.\n * @param {Object} conversation The conversation.\n * @param {Number} userId The logged in user id.\n * @return {Object} jQuery promise\n */\n var createNewConversationFromEvent = function(root, conversation, userId) {\n var existingConversations = root.find(SELECTORS.CONVERSATION);\n\n if (!existingConversations.length) {\n // If we didn't have any conversations then we need to show\n // the content of the list and hide the empty message.\n var listRoot = LazyLoadList.getRoot(root);\n LazyLoadList.showContent(listRoot);\n LazyLoadList.hideEmptyMessage(listRoot);\n }\n\n // Cache the conversation.\n loadedConversationsById[conversation.id] = conversation;\n\n return render([conversation], userId)\n .then(function(html) {\n var contentContainer = LazyLoadList.getContentContainer(root);\n return contentContainer.prepend(html);\n })\n .then(function() {\n return incrementTotalConversationCount(root);\n })\n .catch(Notification.exception);\n };\n\n /**\n * Delete a conversation from the list of conversations.\n *\n * @param {Object} root Overview messages container element.\n * @param {Object} conversationElement The conversation element.\n */\n var deleteConversation = function(root, conversationElement) {\n conversationElement.remove();\n decrementTotalConversationCount(root);\n\n var conversations = root.find(SELECTORS.CONVERSATION);\n if (!conversations.length) {\n // If we don't have any conversations then we need to hide\n // the content of the list and show the empty message.\n var listRoot = LazyLoadList.getRoot(root);\n LazyLoadList.hideContent(listRoot);\n LazyLoadList.showEmptyMessage(listRoot);\n }\n };\n\n /**\n * Mark a conversation as read.\n *\n * @param {Object} root Overview messages container element.\n * @param {Object} conversationElement The conversation element.\n */\n var markConversationAsRead = function(root, conversationElement) {\n var unreadCount = conversationElement.find(SELECTORS.UNREAD_COUNT);\n unreadCount.text('0');\n unreadCount.addClass('hidden');\n decrementTotalUnreadConversationCount(root);\n };\n\n /**\n * Listen to, and handle events in this section.\n *\n * @param {String} namespace Unique identifier for the Routes\n * @param {Object} root The section container element.\n * @param {Function} loadCallback The callback to load items.\n * @param {Array|null} types The conversation types for this section\n * @param {bool} includeFavourites If this section includes favourites\n * @param {String} fromPanel Routing argument to send if the section is loaded in message index left panel.\n */\n var registerEventListeners = function(namespace, root, loadCallback, types, includeFavourites, fromPanel) {\n var listRoot = LazyLoadList.getRoot(root);\n var conversationBelongsToThisSection = function(conversation) {\n // Make sure the type is an int so that the index of check matches correctly.\n var conversationType = parseInt(conversation.type, 10);\n if (\n // If the conversation type isn't one this section cares about then we can ignore it.\n (types && types.indexOf(conversationType) < 0) ||\n // If this is the favourites section and the conversation isn't a favourite then ignore it.\n (includeFavourites && !conversation.isFavourite) ||\n // If this section doesn't include favourites and the conversation is a favourite then ignore it.\n (!includeFavourites && conversation.isFavourite)\n ) {\n return false;\n }\n\n return true;\n };\n\n // Set the minimum height of the section to the height of the toggle. This\n // smooths out the collapse animation.\n var toggle = root.find(SELECTORS.TOGGLE);\n root.css('min-height', toggle.outerHeight());\n\n root.on('show.bs.collapse', function() {\n setExpanded(root);\n LazyLoadList.show(listRoot, loadCallback, function(contentContainer, conversations, userId) {\n return render(conversations, userId)\n .then(function(html) {\n contentContainer.append(html);\n return html;\n })\n .catch(Notification.exception);\n });\n });\n\n root.on('hidden.bs.collapse', function() {\n setCollapsed(root);\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONTACT_BLOCKED, function(userId) {\n var conversationElement = getConversationElementFromUserId(root, userId);\n if (conversationElement.length) {\n blockContact(conversationElement);\n }\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONTACT_UNBLOCKED, function(userId) {\n var conversationElement = getConversationElementFromUserId(root, userId);\n\n if (conversationElement.length) {\n unblockContact(conversationElement);\n }\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_SET_MUTED, function(conversation) {\n var conversationId = conversation.id;\n var conversationElement = getConversationElement(root, conversationId);\n if (conversationElement.length) {\n muteConversation(conversationElement);\n }\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_UNSET_MUTED, function(conversation) {\n var conversationId = conversation.id;\n var conversationElement = getConversationElement(root, conversationId);\n if (conversationElement.length) {\n unmuteConversation(conversationElement);\n }\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE, function(conversation) {\n if (!conversationBelongsToThisSection(conversation)) {\n return;\n }\n\n var pendingPromise = new Pending('core_message/message_drawer_view_overview_section:new');\n var loggedInUserId = conversation.loggedInUserId;\n var conversationId = conversation.id;\n var element = getConversationElement(root, conversationId);\n conversation = formatConversationFromEvent(conversation);\n if (element.length) {\n var contentContainer = LazyLoadList.getContentContainer(root);\n render([conversation], loggedInUserId)\n .then(function(html) {\n if (deletedConversationsById[conversationId]) {\n // This conversation was deleted at some point since the messaging drawer was created.\n if (conversation.messages[0].timeadded < deletedConversationsById[conversationId]) {\n // The 'new' message was added before the conversation was deleted.\n // This is probably stale data.\n return;\n }\n }\n contentContainer.prepend(html);\n element.remove();\n\n return;\n })\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n } else if (conversation.messages.length) {\n createNewConversationFromEvent(root, conversation, loggedInUserId)\n .then(pendingPromise.resolve)\n .catch();\n } else {\n pendingPromise.resolve();\n }\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_DELETED, function(conversationId) {\n var conversationElement = getConversationElement(root, conversationId);\n delete loadedConversationsById[conversationId];\n deletedConversationsById[conversationId] = new Date();\n if (conversationElement.length) {\n deleteConversation(root, conversationElement);\n }\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_READ, function(conversationId) {\n var conversationElement = getConversationElement(root, conversationId);\n if (conversationElement.length) {\n markConversationAsRead(root, conversationElement);\n }\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_SET_FAVOURITE, function(conversation) {\n var conversationElement = null;\n if (conversationBelongsToThisSection(conversation)) {\n conversationElement = getConversationElement(root, conversation.id);\n if (!conversationElement.length) {\n createNewConversationFromEvent(\n root,\n formatConversationFromEvent(conversation),\n conversation.loggedInUserId\n );\n }\n } else {\n conversationElement = getConversationElement(root, conversation.id);\n if (conversationElement.length) {\n deleteConversation(root, conversationElement);\n }\n }\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_UNSET_FAVOURITE, function(conversation) {\n var conversationElement = null;\n if (conversationBelongsToThisSection(conversation)) {\n conversationElement = getConversationElement(root, conversation.id);\n if (!conversationElement.length) {\n createNewConversationFromEvent(\n root,\n formatConversationFromEvent(conversation),\n conversation.loggedInUserId\n );\n }\n } else {\n conversationElement = getConversationElement(root, conversation.id);\n if (conversationElement.length) {\n deleteConversation(root, conversationElement);\n }\n }\n });\n\n CustomEvents.define(root, [CustomEvents.events.activate]);\n root.on(CustomEvents.events.activate, SELECTORS.CONVERSATION, function(e, data) {\n var conversationElement = $(e.target).closest(SELECTORS.CONVERSATION);\n var conversationId = conversationElement.attr('data-conversation-id');\n var conversation = loadedConversationsById[conversationId];\n MessageDrawerRouter.go(namespace, MessageDrawerRoutes.VIEW_CONVERSATION, conversation, fromPanel);\n\n data.originalEvent.preventDefault();\n });\n };\n\n /**\n * Setup the section.\n *\n * @param {String} namespace Unique identifier for the Routes\n * @param {Object} header The header container element.\n * @param {Object} body The section container element.\n * @param {Object} footer The footer container element.\n * @param {Array} types The conversation types that show in this section\n * @param {bool} includeFavourites If this section includes favourites\n * @param {Object} totalCountPromise Resolves wth the total conversations count\n * @param {Object} unreadCountPromise Resolves wth the unread conversations count\n * @param {bool} fromPanel shown in message app panel.\n */\n var show = function(namespace, header, body, footer, types, includeFavourites, totalCountPromise, unreadCountPromise,\n fromPanel) {\n var root = $(body);\n\n if (!root.attr('data-init')) {\n var loadCallback = getLoadCallback(types, includeFavourites, 0);\n registerEventListeners(namespace, root, loadCallback, types, includeFavourites, fromPanel);\n\n if (isVisible(root)) {\n setExpanded(root);\n var listRoot = LazyLoadList.getRoot(root);\n LazyLoadList.show(listRoot, loadCallback, function(contentContainer, conversations, userId) {\n return render(conversations, userId)\n .then(function(html) {\n contentContainer.append(html);\n return html;\n })\n .catch(Notification.exception);\n });\n }\n\n // This is given to us by the calling code because the total counts for all sections\n // are loaded in a single ajax request rather than one request per section.\n totalCountPromise.then(function(count) {\n renderTotalCount(root, count);\n loadedTotalCounts = true;\n return;\n })\n .catch(function() {\n // Silently ignore if we can't updated the counts. No need to bother the user.\n });\n\n // This is given to us by the calling code because the unread counts for all sections\n // are loaded in a single ajax request rather than one request per section.\n unreadCountPromise.then(function(count) {\n renderUnreadCount(root, count);\n loadedUnreadCounts = true;\n return;\n })\n .catch(function() {\n // Silently ignore if we can't updated the counts. No need to bother the user.\n });\n\n root.attr('data-init', true);\n }\n };\n\n return {\n show: show,\n isVisible: isVisible\n };\n});\n"],"names":["define","$","CustomEvents","Notification","PubSub","Str","Pending","Templates","UserDate","MessageRepository","MessageDrawerEvents","MessageDrawerRouter","MessageDrawerRoutes","LazyLoadList","MessageDrawerViewConversationContants","SELECTORS","TEMPLATES","loadedConversationsById","deletedConversationsById","loadedTotalCounts","loadedUnreadCounts","isVisible","root","getRoot","hasClass","setExpanded","addClass","formatConversationFromEvent","conversation","recursivelyLowercaseKeys","object","Object","keys","reduce","carry","key","isArray","toLowerCase","map","formatted","messages","message","useridfrom","userfrom","id","render","conversations","userId","pending","mapPromises","lastMessage","length","async","tmpElement","document","createElement","innerHTML","text","replace","querySelector","messagePreview","indexOf","pix","label","includes","labelString","get_string","renderPix","error","exception","formatMessagePreview","then","formattedConversation","imageurl","name","subname","unreadcount","ismuted","lastmessagedate","timecreated","sentfromcurrentuser","lastmessage","otherUser","type","CONVERSATION_TYPES","SELF","members","PRIVATE","member","userid","showonlinestatus","isonline","isblocked","PUBLIC","lastsendername","fullname","catch","Promise","all","formattedConversations","forEach","Date","toDateString","istoday","html","js","resolve","Deferred","getTotalConversationCountElement","find","decrementTotalUnreadConversationCount","element","getTotalUnreadConversationCountElement","count","parseInt","getConversationElement","conversationId","getConversationElementFromUserId","createNewConversationFromEvent","listRoot","showContent","hideEmptyMessage","getContentContainer","prepend","incrementTotalConversationCount","deleteConversation","conversationElement","remove","decrementTotalConversationCount","hideContent","showEmptyMessage","registerEventListeners","namespace","loadCallback","types","includeFavourites","fromPanel","conversationBelongsToThisSection","conversationType","isFavourite","toggle","css","outerHeight","on","show","contentContainer","append","removeClass","setCollapsed","subscribe","CONTACT_BLOCKED","blockContact","CONTACT_UNBLOCKED","unblockContact","CONVERSATION_SET_MUTED","muteConversation","CONVERSATION_UNSET_MUTED","unmuteConversation","CONVERSATION_NEW_LAST_MESSAGE","pendingPromise","loggedInUserId","timeadded","CONVERSATION_DELETED","CONVERSATION_READ","unreadCount","markConversationAsRead","CONVERSATION_SET_FAVOURITE","CONVERSATION_UNSET_FAVOURITE","events","activate","e","data","target","closest","attr","go","VIEW_CONVERSATION","originalEvent","preventDefault","header","body","footer","totalCountPromise","unreadCountPromise","offset","includeSelfConversations","nonSelfConversationTypes","filter","candidate","getConversations","LOAD_LIMIT","response","slice","setLoadedAll","getLoadCallback","container","done","string","numPlaceholders","placeholders","Array","apply","renderTotalCount","renderUnreadCount"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"message_drawer_view_overview_section.min.js","sources":["../src/message_drawer_view_overview_section.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 * Controls a section of the overview page in the message drawer.\n *\n * @module core_message/message_drawer_view_overview_section\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/custom_interaction_events',\n 'core/notification',\n 'core/pubsub',\n 'core/str',\n 'core/pending',\n 'core/templates',\n 'core/user_date',\n 'core_message/message_repository',\n 'core_message/message_drawer_events',\n 'core_message/message_drawer_router',\n 'core_message/message_drawer_routes',\n 'core_message/message_drawer_lazy_load_list',\n 'core_message/message_drawer_view_conversation_constants'\n],\nfunction(\n $,\n CustomEvents,\n Notification,\n PubSub,\n Str,\n Pending,\n Templates,\n UserDate,\n MessageRepository,\n MessageDrawerEvents,\n MessageDrawerRouter,\n MessageDrawerRoutes,\n LazyLoadList,\n MessageDrawerViewConversationContants\n) {\n\n var SELECTORS = {\n TOGGLE: '[data-region=\"toggle\"]',\n CONVERSATION: '[data-conversation-id]',\n BLOCKED_ICON_CONTAINER: '[data-region=\"contact-icon-blocked\"]',\n LAST_MESSAGE: '[data-region=\"last-message\"]',\n LAST_MESSAGE_DATE: '[data-region=\"last-message-date\"]',\n MUTED_ICON_CONTAINER: '[data-region=\"muted-icon-container\"]',\n UNREAD_COUNT: '[data-region=\"unread-count\"]',\n SECTION_TOTAL_COUNT: '[data-region=\"section-total-count\"]',\n SECTION_TOTAL_COUNT_CONTAINER: '[data-region=\"section-total-count-container\"]',\n SECTION_UNREAD_COUNT: '[data-region=\"section-unread-count\"]',\n SECTION_UNREAD_COUNT_CONTAINER: '[data-region=\"section-unread-count-container\"]',\n PLACEHOLDER_CONTAINER: '[data-region=\"placeholder-container\"]'\n };\n\n var TEMPLATES = {\n CONVERSATIONS_LIST: 'core_message/message_drawer_conversations_list',\n CONVERSATIONS_LIST_ITEMS_PLACEHOLDER: 'core_message/message_drawer_conversations_list_items_placeholder'\n };\n\n var LOAD_LIMIT = 50;\n var loadedConversationsById = {};\n var deletedConversationsById = {};\n var loadedTotalCounts = false;\n var loadedUnreadCounts = false;\n\n /**\n * Get the section visibility status.\n *\n * @param {Object} root The section container element.\n * @return {Bool} Is section visible.\n */\n var isVisible = function(root) {\n return LazyLoadList.getRoot(root).hasClass('show');\n };\n\n /**\n * Set this section as expanded.\n *\n * @param {Object} root The section container element.\n */\n var setExpanded = function(root) {\n root.addClass('expanded');\n };\n\n /**\n * Set this section as collapsed.\n *\n * @param {Object} root The section container element.\n */\n var setCollapsed = function(root) {\n root.removeClass('expanded');\n };\n\n /**\n * Render the total count value and show it for the user. Also update the placeholder\n * HTML for better visuals.\n *\n * @param {Object} root The section container element.\n * @param {Number} count The total count\n */\n var renderTotalCount = function(root, count) {\n var container = root.find(SELECTORS.SECTION_TOTAL_COUNT_CONTAINER);\n var countElement = container.find(SELECTORS.SECTION_TOTAL_COUNT);\n countElement.text(count);\n container.removeClass('hidden');\n Str.get_string('totalconversations', 'core_message', count).done(function(string) {\n $('#' + container.attr('aria-labelledby')).text(string);\n });\n\n var numPlaceholders = count > 20 ? 20 : count;\n // Array of \"true\" up to the number of placeholders we want.\n var placeholders = Array.apply(null, Array(numPlaceholders)).map(function() {\n return true;\n });\n\n // Replace the current placeholder (loading spinner) with some nicer placeholders that\n // better represent the content.\n Templates.render(TEMPLATES.CONVERSATIONS_LIST_ITEMS_PLACEHOLDER, {placeholders: placeholders})\n .then(function(html) {\n var placeholderContainer = root.find(SELECTORS.PLACEHOLDER_CONTAINER);\n placeholderContainer.html(html);\n return;\n })\n .catch(function() {\n // Silently ignore. Doesn't matter if we can't render the placeholders.\n });\n };\n\n /**\n * Render the unread count value and show it for the user if it's higher than zero.\n *\n * @param {Object} root The section container element.\n * @param {Number} count The unread count\n */\n var renderUnreadCount = function(root, count) {\n var container = root.find(SELECTORS.SECTION_UNREAD_COUNT_CONTAINER);\n var countElement = container.find(SELECTORS.SECTION_UNREAD_COUNT);\n countElement.text(count);\n\n Str.get_string('unreadconversations', 'core_message', count).done(function(string) {\n $('#' + container.attr('aria-labelledby')).text(string);\n });\n\n if (count > 0) {\n container.removeClass('hidden');\n }\n };\n\n /**\n * Create a formatted conversation object from the the one we get from events. The new object\n * will be in a format that matches what we receive from the server.\n *\n * @param {Object} conversation\n * @return {Object} formatted conversation.\n */\n var formatConversationFromEvent = function(conversation) {\n // Recursively lowercase all of the keys for an object.\n var recursivelyLowercaseKeys = function(object) {\n return Object.keys(object).reduce(function(carry, key) {\n if ($.isArray(object[key])) {\n carry[key.toLowerCase()] = object[key].map(recursivelyLowercaseKeys);\n } else {\n carry[key.toLowerCase()] = object[key];\n }\n\n return carry;\n }, {});\n };\n\n // Recursively lowercase all of the keys for the conversation.\n var formatted = recursivelyLowercaseKeys(conversation);\n\n // Make sure all messages have the useridfrom property set.\n formatted.messages = formatted.messages.map(function(message) {\n message.useridfrom = message.userfrom.id;\n return message;\n });\n\n return formatted;\n };\n\n /**\n * Render the messages in the overview page.\n *\n * @param {Array} conversations List of conversations to render.\n * @param {Number} userId Logged in user id.\n * @return {Object} jQuery promise.\n */\n var render = function(conversations, userId) {\n\n // Helper to format the last message for rendering.\n // Returns a promise which resolves to either a string, or null\n // (such as in the event of an empty personal space).\n var pending = new Pending();\n\n var formatMessagePreview = async function(lastMessage) {\n if (!lastMessage) {\n return null;\n }\n // Check the message html for a src attribute, indicative of media.\n // Replace LOAD_LIMIT) {\n conversations = conversations.slice(0, -1);\n } else {\n LazyLoadList.setLoadedAll(root, true);\n }\n\n offset = offset + LOAD_LIMIT;\n\n conversations.forEach(function(conversation) {\n loadedConversationsById[conversation.id] = conversation;\n });\n\n return conversations;\n })\n .catch(Notification.exception);\n };\n };\n\n /**\n * Get the total count container element.\n *\n * @param {Object} root Overview messages container element.\n * @return {Object} Total count container element.\n */\n var getTotalConversationCountElement = function(root) {\n return root.find(SELECTORS.SECTION_TOTAL_COUNT);\n };\n\n /**\n * Get the unread conversations count container element.\n *\n * @param {Object} root Overview messages container element.\n * @return {Object} Unread conversations count container element.\n */\n var getTotalUnreadConversationCountElement = function(root) {\n return root.find(SELECTORS.SECTION_UNREAD_COUNT);\n };\n\n /**\n * Increment the total conversations count.\n *\n * @param {Object} root Overview messages container element.\n */\n var incrementTotalConversationCount = function(root) {\n if (loadedTotalCounts) {\n var element = getTotalConversationCountElement(root);\n var count = parseInt(element.text());\n count = count + 1;\n element.text(count);\n }\n };\n\n /**\n * Decrement the total conversations count.\n *\n * @param {Object} root Overview messages container element.\n */\n var decrementTotalConversationCount = function(root) {\n if (loadedTotalCounts) {\n var element = getTotalConversationCountElement(root);\n var count = parseInt(element.text());\n count = count - 1;\n element.text(count);\n }\n };\n\n /**\n * Decrement the total unread conversations count.\n *\n * @param {Object} root Overview messages container element.\n */\n var decrementTotalUnreadConversationCount = function(root) {\n if (loadedUnreadCounts) {\n var element = getTotalUnreadConversationCountElement(root);\n var count = parseInt(element.text());\n count = count - 1;\n element.text(count);\n\n if (count < 1) {\n element.addClass('hidden');\n }\n }\n };\n\n /**\n * Get a contact / conversation element.\n *\n * @param {Object} root Overview messages container element.\n * @param {Number} conversationId The conversation id.\n * @return {Object} Conversation element.\n */\n var getConversationElement = function(root, conversationId) {\n return root.find('[data-conversation-id=\"' + conversationId + '\"]');\n };\n\n /**\n * Get a contact / conversation element from a user id.\n *\n * @param {Object} root Overview messages container element.\n * @param {Number} userId The user id.\n * @return {Object} Conversation element.\n */\n var getConversationElementFromUserId = function(root, userId) {\n return root.find('[data-user-id=\"' + userId + '\"]');\n };\n\n /**\n * Show the conversation is muted icon.\n *\n * @param {Object} conversationElement The conversation element.\n */\n var muteConversation = function(conversationElement) {\n conversationElement.find(SELECTORS.MUTED_ICON_CONTAINER).removeClass('hidden');\n };\n\n /**\n * Hide the conversation is muted icon.\n *\n * @param {Object} conversationElement The conversation element.\n */\n var unmuteConversation = function(conversationElement) {\n conversationElement.find(SELECTORS.MUTED_ICON_CONTAINER).addClass('hidden');\n };\n\n /**\n * Show the contact is blocked icon.\n *\n * @param {Object} conversationElement The conversation element.\n */\n var blockContact = function(conversationElement) {\n conversationElement.find(SELECTORS.BLOCKED_ICON_CONTAINER).removeClass('hidden');\n };\n\n /**\n * Hide the contact is blocked icon.\n *\n * @param {Object} conversationElement The conversation element.\n */\n var unblockContact = function(conversationElement) {\n conversationElement.find(SELECTORS.BLOCKED_ICON_CONTAINER).addClass('hidden');\n };\n\n /**\n * Create an render new conversation element in the list of conversations.\n *\n * @param {Object} root Overview messages container element.\n * @param {Object} conversation The conversation.\n * @param {Number} userId The logged in user id.\n * @return {Object} jQuery promise\n */\n var createNewConversationFromEvent = function(root, conversation, userId) {\n var existingConversations = root.find(SELECTORS.CONVERSATION);\n\n if (!existingConversations.length) {\n // If we didn't have any conversations then we need to show\n // the content of the list and hide the empty message.\n var listRoot = LazyLoadList.getRoot(root);\n LazyLoadList.showContent(listRoot);\n LazyLoadList.hideEmptyMessage(listRoot);\n }\n\n // Cache the conversation.\n loadedConversationsById[conversation.id] = conversation;\n\n return render([conversation], userId)\n .then(function(html) {\n var contentContainer = LazyLoadList.getContentContainer(root);\n return contentContainer.prepend(html);\n })\n .then(function() {\n return incrementTotalConversationCount(root);\n })\n .catch(Notification.exception);\n };\n\n /**\n * Delete a conversation from the list of conversations.\n *\n * @param {Object} root Overview messages container element.\n * @param {Object} conversationElement The conversation element.\n */\n var deleteConversation = function(root, conversationElement) {\n conversationElement.remove();\n decrementTotalConversationCount(root);\n\n var conversations = root.find(SELECTORS.CONVERSATION);\n if (!conversations.length) {\n // If we don't have any conversations then we need to hide\n // the content of the list and show the empty message.\n var listRoot = LazyLoadList.getRoot(root);\n LazyLoadList.hideContent(listRoot);\n LazyLoadList.showEmptyMessage(listRoot);\n }\n };\n\n /**\n * Mark a conversation as read.\n *\n * @param {Object} root Overview messages container element.\n * @param {Object} conversationElement The conversation element.\n */\n var markConversationAsRead = function(root, conversationElement) {\n var unreadCount = conversationElement.find(SELECTORS.UNREAD_COUNT);\n unreadCount.text('0');\n unreadCount.addClass('hidden');\n decrementTotalUnreadConversationCount(root);\n };\n\n /**\n * Listen to, and handle events in this section.\n *\n * @param {String} namespace Unique identifier for the Routes\n * @param {Object} root The section container element.\n * @param {Function} loadCallback The callback to load items.\n * @param {Array|null} types The conversation types for this section\n * @param {bool} includeFavourites If this section includes favourites\n * @param {String} fromPanel Routing argument to send if the section is loaded in message index left panel.\n */\n var registerEventListeners = function(namespace, root, loadCallback, types, includeFavourites, fromPanel) {\n var listRoot = LazyLoadList.getRoot(root);\n var conversationBelongsToThisSection = function(conversation) {\n // Make sure the type is an int so that the index of check matches correctly.\n var conversationType = parseInt(conversation.type, 10);\n if (\n // If the conversation type isn't one this section cares about then we can ignore it.\n (types && types.indexOf(conversationType) < 0) ||\n // If this is the favourites section and the conversation isn't a favourite then ignore it.\n (includeFavourites && !conversation.isFavourite) ||\n // If this section doesn't include favourites and the conversation is a favourite then ignore it.\n (!includeFavourites && conversation.isFavourite)\n ) {\n return false;\n }\n\n return true;\n };\n\n // Set the minimum height of the section to the height of the toggle. This\n // smooths out the collapse animation.\n var toggle = root.find(SELECTORS.TOGGLE);\n root.css('min-height', toggle.outerHeight());\n\n root[0].addEventListener('show.bs.collapse', function() {\n setExpanded(root);\n LazyLoadList.show(listRoot, loadCallback, function(contentContainer, conversations, userId) {\n return render(conversations, userId)\n .then(function(html) {\n contentContainer.append(html);\n return html;\n })\n .catch(Notification.exception);\n });\n });\n\n root[0].addEventListener('hidden.bs.collapse', function() {\n setCollapsed(root);\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONTACT_BLOCKED, function(userId) {\n var conversationElement = getConversationElementFromUserId(root, userId);\n if (conversationElement.length) {\n blockContact(conversationElement);\n }\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONTACT_UNBLOCKED, function(userId) {\n var conversationElement = getConversationElementFromUserId(root, userId);\n\n if (conversationElement.length) {\n unblockContact(conversationElement);\n }\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_SET_MUTED, function(conversation) {\n var conversationId = conversation.id;\n var conversationElement = getConversationElement(root, conversationId);\n if (conversationElement.length) {\n muteConversation(conversationElement);\n }\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_UNSET_MUTED, function(conversation) {\n var conversationId = conversation.id;\n var conversationElement = getConversationElement(root, conversationId);\n if (conversationElement.length) {\n unmuteConversation(conversationElement);\n }\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_NEW_LAST_MESSAGE, function(conversation) {\n if (!conversationBelongsToThisSection(conversation)) {\n return;\n }\n\n var pendingPromise = new Pending('core_message/message_drawer_view_overview_section:new');\n var loggedInUserId = conversation.loggedInUserId;\n var conversationId = conversation.id;\n var element = getConversationElement(root, conversationId);\n conversation = formatConversationFromEvent(conversation);\n if (element.length) {\n var contentContainer = LazyLoadList.getContentContainer(root);\n render([conversation], loggedInUserId)\n .then(function(html) {\n if (deletedConversationsById[conversationId]) {\n // This conversation was deleted at some point since the messaging drawer was created.\n if (conversation.messages[0].timeadded < deletedConversationsById[conversationId]) {\n // The 'new' message was added before the conversation was deleted.\n // This is probably stale data.\n return;\n }\n }\n contentContainer.prepend(html);\n element.remove();\n\n return;\n })\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n } else if (conversation.messages.length) {\n createNewConversationFromEvent(root, conversation, loggedInUserId)\n .then(pendingPromise.resolve)\n .catch();\n } else {\n pendingPromise.resolve();\n }\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_DELETED, function(conversationId) {\n var conversationElement = getConversationElement(root, conversationId);\n delete loadedConversationsById[conversationId];\n deletedConversationsById[conversationId] = new Date();\n if (conversationElement.length) {\n deleteConversation(root, conversationElement);\n }\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_READ, function(conversationId) {\n var conversationElement = getConversationElement(root, conversationId);\n if (conversationElement.length) {\n markConversationAsRead(root, conversationElement);\n }\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_SET_FAVOURITE, function(conversation) {\n var conversationElement = null;\n if (conversationBelongsToThisSection(conversation)) {\n conversationElement = getConversationElement(root, conversation.id);\n if (!conversationElement.length) {\n createNewConversationFromEvent(\n root,\n formatConversationFromEvent(conversation),\n conversation.loggedInUserId\n );\n }\n } else {\n conversationElement = getConversationElement(root, conversation.id);\n if (conversationElement.length) {\n deleteConversation(root, conversationElement);\n }\n }\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_UNSET_FAVOURITE, function(conversation) {\n var conversationElement = null;\n if (conversationBelongsToThisSection(conversation)) {\n conversationElement = getConversationElement(root, conversation.id);\n if (!conversationElement.length) {\n createNewConversationFromEvent(\n root,\n formatConversationFromEvent(conversation),\n conversation.loggedInUserId\n );\n }\n } else {\n conversationElement = getConversationElement(root, conversation.id);\n if (conversationElement.length) {\n deleteConversation(root, conversationElement);\n }\n }\n });\n\n CustomEvents.define(root, [CustomEvents.events.activate]);\n root.on(CustomEvents.events.activate, SELECTORS.CONVERSATION, function(e, data) {\n var conversationElement = $(e.target).closest(SELECTORS.CONVERSATION);\n var conversationId = conversationElement.attr('data-conversation-id');\n var conversation = loadedConversationsById[conversationId];\n MessageDrawerRouter.go(namespace, MessageDrawerRoutes.VIEW_CONVERSATION, conversation, fromPanel);\n\n data.originalEvent.preventDefault();\n });\n };\n\n /**\n * Setup the section.\n *\n * @param {String} namespace Unique identifier for the Routes\n * @param {Object} header The header container element.\n * @param {Object} body The section container element.\n * @param {Object} footer The footer container element.\n * @param {Array} types The conversation types that show in this section\n * @param {bool} includeFavourites If this section includes favourites\n * @param {Object} totalCountPromise Resolves wth the total conversations count\n * @param {Object} unreadCountPromise Resolves wth the unread conversations count\n * @param {bool} fromPanel shown in message app panel.\n */\n var show = function(namespace, header, body, footer, types, includeFavourites, totalCountPromise, unreadCountPromise,\n fromPanel) {\n var root = $(body);\n\n if (!root.attr('data-init')) {\n var loadCallback = getLoadCallback(types, includeFavourites, 0);\n registerEventListeners(namespace, root, loadCallback, types, includeFavourites, fromPanel);\n\n if (isVisible(root)) {\n setExpanded(root);\n var listRoot = LazyLoadList.getRoot(root);\n LazyLoadList.show(listRoot, loadCallback, function(contentContainer, conversations, userId) {\n return render(conversations, userId)\n .then(function(html) {\n contentContainer.append(html);\n return html;\n })\n .catch(Notification.exception);\n });\n }\n\n // This is given to us by the calling code because the total counts for all sections\n // are loaded in a single ajax request rather than one request per section.\n totalCountPromise.then(function(count) {\n renderTotalCount(root, count);\n loadedTotalCounts = true;\n return;\n })\n .catch(function() {\n // Silently ignore if we can't updated the counts. No need to bother the user.\n });\n\n // This is given to us by the calling code because the unread counts for all sections\n // are loaded in a single ajax request rather than one request per section.\n unreadCountPromise.then(function(count) {\n renderUnreadCount(root, count);\n loadedUnreadCounts = true;\n return;\n })\n .catch(function() {\n // Silently ignore if we can't updated the counts. No need to bother the user.\n });\n\n root.attr('data-init', true);\n }\n };\n\n return {\n show: show,\n isVisible: isVisible\n };\n});\n"],"names":["define","$","CustomEvents","Notification","PubSub","Str","Pending","Templates","UserDate","MessageRepository","MessageDrawerEvents","MessageDrawerRouter","MessageDrawerRoutes","LazyLoadList","MessageDrawerViewConversationContants","SELECTORS","TEMPLATES","loadedConversationsById","deletedConversationsById","loadedTotalCounts","loadedUnreadCounts","isVisible","root","getRoot","hasClass","setExpanded","addClass","formatConversationFromEvent","conversation","recursivelyLowercaseKeys","object","Object","keys","reduce","carry","key","isArray","toLowerCase","map","formatted","messages","message","useridfrom","userfrom","id","render","conversations","userId","pending","mapPromises","lastMessage","length","async","tmpElement","document","createElement","innerHTML","text","replace","querySelector","messagePreview","indexOf","pix","label","includes","labelString","get_string","renderPix","error","exception","formatMessagePreview","then","formattedConversation","imageurl","name","subname","unreadcount","ismuted","lastmessagedate","timecreated","sentfromcurrentuser","lastmessage","otherUser","type","CONVERSATION_TYPES","SELF","members","PRIVATE","member","userid","showonlinestatus","isonline","isblocked","PUBLIC","lastsendername","fullname","catch","Promise","all","formattedConversations","forEach","Date","toDateString","istoday","html","js","resolve","Deferred","getTotalConversationCountElement","find","decrementTotalUnreadConversationCount","element","getTotalUnreadConversationCountElement","count","parseInt","getConversationElement","conversationId","getConversationElementFromUserId","createNewConversationFromEvent","listRoot","showContent","hideEmptyMessage","getContentContainer","prepend","incrementTotalConversationCount","deleteConversation","conversationElement","remove","decrementTotalConversationCount","hideContent","showEmptyMessage","registerEventListeners","namespace","loadCallback","types","includeFavourites","fromPanel","conversationBelongsToThisSection","conversationType","isFavourite","toggle","css","outerHeight","addEventListener","show","contentContainer","append","removeClass","setCollapsed","subscribe","CONTACT_BLOCKED","blockContact","CONTACT_UNBLOCKED","unblockContact","CONVERSATION_SET_MUTED","muteConversation","CONVERSATION_UNSET_MUTED","unmuteConversation","CONVERSATION_NEW_LAST_MESSAGE","pendingPromise","loggedInUserId","timeadded","CONVERSATION_DELETED","CONVERSATION_READ","unreadCount","markConversationAsRead","CONVERSATION_SET_FAVOURITE","CONVERSATION_UNSET_FAVOURITE","events","activate","on","e","data","target","closest","attr","go","VIEW_CONVERSATION","originalEvent","preventDefault","header","body","footer","totalCountPromise","unreadCountPromise","offset","includeSelfConversations","nonSelfConversationTypes","filter","candidate","getConversations","LOAD_LIMIT","response","slice","setLoadedAll","getLoadCallback","container","done","string","numPlaceholders","placeholders","Array","apply","renderTotalCount","renderUnreadCount"],"mappings":"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"} \ No newline at end of file diff --git a/message/amd/src/message_drawer.js b/message/amd/src/message_drawer.js index 019aaf9fa1be3..fc837a6aad5aa 100644 --- a/message/amd/src/message_drawer.js +++ b/message/amd/src/message_drawer.js @@ -241,17 +241,21 @@ function( // These are theme-specific to help us fix random behat fails. // These events target those events defined in BS3 and BS4 onwards. - root.on('hide.bs.collapse', '.collapse', function(e) { - var pendingPromise = new Pending(); - $(e.target).one('hidden.bs.collapse', function() { - pendingPromise.resolve(); + root[0].querySelectorAll('.collapse').forEach((collapse) => { + collapse.addEventListener('hide.bs.collapse', (e) => { + var pendingPromise = new Pending(); + e.target.addEventListener('hidden.bs.collapse', function() { + pendingPromise.resolve(); + }, {once: true}); }); }); - root.on('show.bs.collapse', '.collapse', function(e) { - var pendingPromise = new Pending(); - $(e.target).one('shown.bs.collapse', function() { - pendingPromise.resolve(); + root[0].querySelectorAll('.collapse').forEach((collapse) => { + collapse.addEventListener('show.bs.collapse', (e) => { + var pendingPromise = new Pending(); + e.target.addEventListener('shown.bs.collapse', function() { + pendingPromise.resolve(); + }, {once: true}); }); }); diff --git a/message/amd/src/message_drawer_view_contacts.js b/message/amd/src/message_drawer_view_contacts.js index ab255c78a17db..dca76ef9afc88 100644 --- a/message/amd/src/message_drawer_view_contacts.js +++ b/message/amd/src/message_drawer_view_contacts.js @@ -128,11 +128,11 @@ function( var showContactsAction = getShowContactsAction(body); var showRequestsAction = getShowRequestsAction(body); - showContactsAction.on('show.bs.tab', function() { + showContactsAction[0].addEventListener('show.bs.tab', function() { ContactsSection.show(contactsSection); }); - showRequestsAction.on('show.bs.tab', function() { + showRequestsAction[0].addEventListener('show.bs.tab', function() { RequestsSection.show(requestsSection); }); diff --git a/message/amd/src/message_drawer_view_overview_section.js b/message/amd/src/message_drawer_view_overview_section.js index f891a00460d4c..6c68d6423e06e 100644 --- a/message/amd/src/message_drawer_view_overview_section.js +++ b/message/amd/src/message_drawer_view_overview_section.js @@ -612,7 +612,7 @@ function( var toggle = root.find(SELECTORS.TOGGLE); root.css('min-height', toggle.outerHeight()); - root.on('show.bs.collapse', function() { + root[0].addEventListener('show.bs.collapse', function() { setExpanded(root); LazyLoadList.show(listRoot, loadCallback, function(contentContainer, conversations, userId) { return render(conversations, userId) @@ -624,7 +624,7 @@ function( }); }); - root.on('hidden.bs.collapse', function() { + root[0].addEventListener('hidden.bs.collapse', function() { setCollapsed(root); }); diff --git a/mod/assign/amd/build/actionbar/grading/extra_filters_dropdown.min.js b/mod/assign/amd/build/actionbar/grading/extra_filters_dropdown.min.js index 58d4d99f7b121..fe17ba8dfc651 100644 --- a/mod/assign/amd/build/actionbar/grading/extra_filters_dropdown.min.js +++ b/mod/assign/amd/build/actionbar/grading/extra_filters_dropdown.min.js @@ -1,10 +1,11 @@ -define("mod_assign/actionbar/grading/extra_filters_dropdown",["exports","core/local/dropdown/dialog","core_user/repository","jquery"],(function(_exports,_dialog,_repository,_jquery){var obj; +define("mod_assign/actionbar/grading/extra_filters_dropdown",["exports","core/local/dropdown/dialog","core_user/repository"],(function(_exports,_dialog,_repository){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0; /** * Module for the extra filters dropdown on the submissions page. * * @module mod_assign/actionbar/grading/extra_filters_dropdown * @copyright 2024 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj};const Selectors_extraFiltersDropdown=".dropdown.extrafilters",Selectors_extraFiltersClose='a[data-action="close"]',Selectors_workflowFilterElement='select[name="workflowfilter"]',Selectors_markerFilterElement='select[name="markingallocationfilter"]',Selectors_suspendedParticipantsFilterCheckbox='input[type="checkbox"][name="suspendedparticipantsfilter"]',Selectors_suspendedParticipantsFilterHidden='input[type="hidden"][name="suspendedparticipantsfilter"]',restoreAppliedWorkflowFilter=async extraFiltersDropdown=>{const appliedWorkflowFilter=await(0,_repository.getUserPreference)("assign_workflowfilter");extraFiltersDropdown.getElement().querySelector(Selectors_workflowFilterElement).value=appliedWorkflowFilter},restoreAppliedMarkerFilter=async extraFiltersDropdown=>{const markerFilterSelect=extraFiltersDropdown.getElement().querySelector(Selectors_markerFilterElement);if(markerFilterSelect){const appliedMarkerFilter=await(0,_repository.getUserPreference)("assign_markerfilter");markerFilterSelect.value=appliedMarkerFilter}},restoreAppliedSuspendedParticipantsFilter=async extraFiltersDropdown=>{const suspendedParticipantsFilterCheckbox=extraFiltersDropdown.getElement().querySelector(Selectors_suspendedParticipantsFilterCheckbox);if(suspendedParticipantsFilterCheckbox){const suspendedParticipantsFilterHidden=suspendedParticipantsFilterCheckbox.parentNode.querySelector(Selectors_suspendedParticipantsFilterHidden),showOnlyActiveParticipants=await(0,_repository.getUserPreference)("grade_report_showonlyactiveenrol");suspendedParticipantsFilterCheckbox.checked=!showOnlyActiveParticipants,suspendedParticipantsFilterHidden.disabled=!showOnlyActiveParticipants}};_exports.init=()=>{const extraFiltersDropdown=(0,_dialog.getDropdownDialog)(Selectors_extraFiltersDropdown);extraFiltersDropdown&&(extraFiltersDropdown=>{extraFiltersDropdown.getElement().addEventListener("click",(e=>{e.target.closest(Selectors_extraFiltersClose)&&(e.preventDefault(),extraFiltersDropdown.setVisible(!1))})),extraFiltersDropdown.getElement().addEventListener("change",(e=>{const suspendedParticipantsFilterCheckbox=e.target.closest(Selectors_suspendedParticipantsFilterCheckbox);suspendedParticipantsFilterCheckbox&&(suspendedParticipantsFilterCheckbox.parentNode.querySelector(Selectors_suspendedParticipantsFilterHidden).disabled=suspendedParticipantsFilterCheckbox.checked)})),(0,_jquery.default)(extraFiltersDropdown.getElement()).on("hide.bs.dropdown",(()=>{restoreAppliedWorkflowFilter(extraFiltersDropdown),restoreAppliedMarkerFilter(extraFiltersDropdown),restoreAppliedSuspendedParticipantsFilter(extraFiltersDropdown)}))})(extraFiltersDropdown)}})); + */ +const Selectors_extraFiltersDropdown=".dropdown.extrafilters",Selectors_extraFiltersClose='a[data-action="close"]',Selectors_workflowFilterElement='select[name="workflowfilter"]',Selectors_markerFilterElement='select[name="markingallocationfilter"]',Selectors_suspendedParticipantsFilterCheckbox='input[type="checkbox"][name="suspendedparticipantsfilter"]',Selectors_suspendedParticipantsFilterHidden='input[type="hidden"][name="suspendedparticipantsfilter"]',restoreAppliedWorkflowFilter=async extraFiltersDropdown=>{const appliedWorkflowFilter=await(0,_repository.getUserPreference)("assign_workflowfilter");extraFiltersDropdown.getElement().querySelector(Selectors_workflowFilterElement).value=appliedWorkflowFilter},restoreAppliedMarkerFilter=async extraFiltersDropdown=>{const markerFilterSelect=extraFiltersDropdown.getElement().querySelector(Selectors_markerFilterElement);if(markerFilterSelect){const appliedMarkerFilter=await(0,_repository.getUserPreference)("assign_markerfilter");markerFilterSelect.value=appliedMarkerFilter}},restoreAppliedSuspendedParticipantsFilter=async extraFiltersDropdown=>{const suspendedParticipantsFilterCheckbox=extraFiltersDropdown.getElement().querySelector(Selectors_suspendedParticipantsFilterCheckbox);if(suspendedParticipantsFilterCheckbox){const suspendedParticipantsFilterHidden=suspendedParticipantsFilterCheckbox.parentNode.querySelector(Selectors_suspendedParticipantsFilterHidden),showOnlyActiveParticipants=await(0,_repository.getUserPreference)("grade_report_showonlyactiveenrol");suspendedParticipantsFilterCheckbox.checked=!showOnlyActiveParticipants,suspendedParticipantsFilterHidden.disabled=!showOnlyActiveParticipants}};_exports.init=()=>{const extraFiltersDropdown=(0,_dialog.getDropdownDialog)(Selectors_extraFiltersDropdown);extraFiltersDropdown&&(extraFiltersDropdown=>{extraFiltersDropdown.getElement().addEventListener("click",(e=>{e.target.closest(Selectors_extraFiltersClose)&&(e.preventDefault(),extraFiltersDropdown.setVisible(!1))})),extraFiltersDropdown.getElement().addEventListener("change",(e=>{const suspendedParticipantsFilterCheckbox=e.target.closest(Selectors_suspendedParticipantsFilterCheckbox);suspendedParticipantsFilterCheckbox&&(suspendedParticipantsFilterCheckbox.parentNode.querySelector(Selectors_suspendedParticipantsFilterHidden).disabled=suspendedParticipantsFilterCheckbox.checked)})),extraFiltersDropdown.getElement().addEventListener("hide.bs.dropdown",(()=>{restoreAppliedWorkflowFilter(extraFiltersDropdown),restoreAppliedMarkerFilter(extraFiltersDropdown),restoreAppliedSuspendedParticipantsFilter(extraFiltersDropdown)}))})(extraFiltersDropdown)}})); //# sourceMappingURL=extra_filters_dropdown.min.js.map \ No newline at end of file diff --git a/mod/assign/amd/build/actionbar/grading/extra_filters_dropdown.min.js.map b/mod/assign/amd/build/actionbar/grading/extra_filters_dropdown.min.js.map index a9dd34f8c61eb..136448f322ba0 100644 --- a/mod/assign/amd/build/actionbar/grading/extra_filters_dropdown.min.js.map +++ b/mod/assign/amd/build/actionbar/grading/extra_filters_dropdown.min.js.map @@ -1 +1 @@ -{"version":3,"file":"extra_filters_dropdown.min.js","sources":["../../../src/actionbar/grading/extra_filters_dropdown.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\nimport {getDropdownDialog} from 'core/local/dropdown/dialog';\nimport {getUserPreference} from 'core_user/repository';\nimport $ from 'jquery';\n\n/**\n * Module for the extra filters dropdown on the submissions page.\n *\n * @module mod_assign/actionbar/grading/extra_filters_dropdown\n * @copyright 2024 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/** @constant {Object} The object containing the relevant selectors. */\nconst Selectors = {\n extraFiltersDropdown: '.dropdown.extrafilters',\n extraFiltersClose: 'a[data-action=\"close\"]',\n workflowFilterElement: 'select[name=\"workflowfilter\"]',\n markerFilterElement: 'select[name=\"markingallocationfilter\"]',\n suspendedParticipantsFilterCheckbox: 'input[type=\"checkbox\"][name=\"suspendedparticipantsfilter\"]',\n suspendedParticipantsFilterHidden: 'input[type=\"hidden\"][name=\"suspendedparticipantsfilter\"]'\n};\n\n/**\n * Register event listeners for the extra filters dropdown.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst registerEventListeners = (extraFiltersDropdown) => {\n // Click event listener to the extra filters dropdown element.\n extraFiltersDropdown.getElement().addEventListener('click', e => {\n // The target is the 'Close' button.\n if (e.target.closest(Selectors.extraFiltersClose)) {\n e.preventDefault();\n extraFiltersDropdown.setVisible(false);\n }\n });\n\n // Change event listener to the extra filters dropdown element.\n extraFiltersDropdown.getElement().addEventListener('change', e => {\n const suspendedParticipantsFilterCheckbox = e.target.closest(Selectors.suspendedParticipantsFilterCheckbox);\n // The target is the 'Suspended participants' filter checkbox.\n if (suspendedParticipantsFilterCheckbox) {\n // The 'Suspended participants' filter uses a hidden input and a checkbox. The hidden input is used to\n // submit '0' as a workaround when the checkbox is unchecked since unchecked checkboxes are not submitted\n // with the form. Therefore, we need to enable or disable the hidden input based on the checkbox state.\n const suspendedParticipantsFilterHidden = suspendedParticipantsFilterCheckbox.parentNode\n .querySelector(Selectors.suspendedParticipantsFilterHidden);\n suspendedParticipantsFilterHidden.disabled = suspendedParticipantsFilterCheckbox.checked;\n }\n });\n\n // Event listener triggered upon hiding of the dropdown.\n $(extraFiltersDropdown.getElement()).on('hide.bs.dropdown', () => {\n // Restore the filters to their stored preference values once the dropdown is closed.\n restoreAppliedWorkflowFilter(extraFiltersDropdown);\n restoreAppliedMarkerFilter(extraFiltersDropdown);\n restoreAppliedSuspendedParticipantsFilter(extraFiltersDropdown);\n });\n};\n\n/**\n * Restores the currently applied workflow filter to its stored preference value.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst restoreAppliedWorkflowFilter = async(extraFiltersDropdown) => {\n const appliedWorkflowFilter = await getUserPreference('assign_workflowfilter');\n const workflowFilterSelect = extraFiltersDropdown.getElement().querySelector(Selectors.workflowFilterElement);\n workflowFilterSelect.value = appliedWorkflowFilter;\n};\n\n/**\n * Restores the currently applied marker filter to its stored preference value.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst restoreAppliedMarkerFilter = async(extraFiltersDropdown) => {\n const markerFilterSelect = extraFiltersDropdown.getElement().querySelector(Selectors.markerFilterElement);\n if (markerFilterSelect) {\n const appliedMarkerFilter = await getUserPreference('assign_markerfilter');\n markerFilterSelect.value = appliedMarkerFilter;\n }\n};\n\n/**\n * Restores the currently suspended participants filter to its stored preference value.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst restoreAppliedSuspendedParticipantsFilter = async(extraFiltersDropdown) => {\n const suspendedParticipantsFilterCheckbox = extraFiltersDropdown.getElement()\n .querySelector(Selectors.suspendedParticipantsFilterCheckbox);\n if (suspendedParticipantsFilterCheckbox) {\n const suspendedParticipantsFilterHidden = suspendedParticipantsFilterCheckbox.parentNode\n .querySelector(Selectors.suspendedParticipantsFilterHidden);\n const showOnlyActiveParticipants = await getUserPreference('grade_report_showonlyactiveenrol');\n suspendedParticipantsFilterCheckbox.checked = !showOnlyActiveParticipants;\n suspendedParticipantsFilterHidden.disabled = !showOnlyActiveParticipants;\n }\n};\n\n/**\n * Initialize module.\n */\nexport const init = () => {\n const extraFiltersDropdown = getDropdownDialog(Selectors.extraFiltersDropdown);\n if (extraFiltersDropdown) {\n registerEventListeners(extraFiltersDropdown);\n }\n};\n"],"names":["Selectors","restoreAppliedWorkflowFilter","async","appliedWorkflowFilter","extraFiltersDropdown","getElement","querySelector","value","restoreAppliedMarkerFilter","markerFilterSelect","appliedMarkerFilter","restoreAppliedSuspendedParticipantsFilter","suspendedParticipantsFilterCheckbox","suspendedParticipantsFilterHidden","parentNode","showOnlyActiveParticipants","checked","disabled","addEventListener","e","target","closest","preventDefault","setVisible","on","registerEventListeners"],"mappings":";;;;;;;gJA4BMA,+BACoB,yBADpBA,4BAEiB,yBAFjBA,gCAGqB,gCAHrBA,8BAImB,yCAJnBA,8CAKmC,6DALnCA,4CAMiC,2DA8CjCC,6BAA+BC,MAAAA,6BAC3BC,4BAA8B,iCAAkB,yBACzBC,qBAAqBC,aAAaC,cAAcN,iCACxDO,MAAQJ,uBAQ3BK,2BAA6BN,MAAAA,6BACzBO,mBAAqBL,qBAAqBC,aAAaC,cAAcN,kCACvES,mBAAoB,OACdC,0BAA4B,iCAAkB,uBACpDD,mBAAmBF,MAAQG,sBAS7BC,0CAA4CT,MAAAA,6BACxCU,oCAAsCR,qBAAqBC,aAC5DC,cAAcN,kDACfY,oCAAqC,OAC/BC,kCAAoCD,oCAAoCE,WACzER,cAAcN,6CACbe,iCAAmC,iCAAkB,oCAC3DH,oCAAoCI,SAAWD,2BAC/CF,kCAAkCI,UAAYF,2CAOlC,WACVX,sBAAuB,6BAAkBJ,gCAC3CI,sBA/EwBA,CAAAA,uBAE5BA,qBAAqBC,aAAaa,iBAAiB,SAASC,IAEpDA,EAAEC,OAAOC,QAAQrB,+BACjBmB,EAAEG,iBACFlB,qBAAqBmB,YAAW,OAKxCnB,qBAAqBC,aAAaa,iBAAiB,UAAUC,UACnDP,oCAAsCO,EAAEC,OAAOC,QAAQrB,+CAEzDY,sCAI0CA,oCAAoCE,WACzER,cAAcN,6CACeiB,SAAWL,oCAAoCI,gCAKvFZ,qBAAqBC,cAAcmB,GAAG,oBAAoB,KAExDvB,6BAA6BG,sBAC7BI,2BAA2BJ,sBAC3BO,0CAA0CP,0BAmD1CqB,CAAuBrB"} \ No newline at end of file +{"version":3,"file":"extra_filters_dropdown.min.js","sources":["../../../src/actionbar/grading/extra_filters_dropdown.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\nimport {getDropdownDialog} from 'core/local/dropdown/dialog';\nimport {getUserPreference} from 'core_user/repository';\n\n/**\n * Module for the extra filters dropdown on the submissions page.\n *\n * @module mod_assign/actionbar/grading/extra_filters_dropdown\n * @copyright 2024 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/** @constant {Object} The object containing the relevant selectors. */\nconst Selectors = {\n extraFiltersDropdown: '.dropdown.extrafilters',\n extraFiltersClose: 'a[data-action=\"close\"]',\n workflowFilterElement: 'select[name=\"workflowfilter\"]',\n markerFilterElement: 'select[name=\"markingallocationfilter\"]',\n suspendedParticipantsFilterCheckbox: 'input[type=\"checkbox\"][name=\"suspendedparticipantsfilter\"]',\n suspendedParticipantsFilterHidden: 'input[type=\"hidden\"][name=\"suspendedparticipantsfilter\"]'\n};\n\n/**\n * Register event listeners for the extra filters dropdown.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst registerEventListeners = (extraFiltersDropdown) => {\n // Click event listener to the extra filters dropdown element.\n extraFiltersDropdown.getElement().addEventListener('click', e => {\n // The target is the 'Close' button.\n if (e.target.closest(Selectors.extraFiltersClose)) {\n e.preventDefault();\n extraFiltersDropdown.setVisible(false);\n }\n });\n\n // Change event listener to the extra filters dropdown element.\n extraFiltersDropdown.getElement().addEventListener('change', e => {\n const suspendedParticipantsFilterCheckbox = e.target.closest(Selectors.suspendedParticipantsFilterCheckbox);\n // The target is the 'Suspended participants' filter checkbox.\n if (suspendedParticipantsFilterCheckbox) {\n // The 'Suspended participants' filter uses a hidden input and a checkbox. The hidden input is used to\n // submit '0' as a workaround when the checkbox is unchecked since unchecked checkboxes are not submitted\n // with the form. Therefore, we need to enable or disable the hidden input based on the checkbox state.\n const suspendedParticipantsFilterHidden = suspendedParticipantsFilterCheckbox.parentNode\n .querySelector(Selectors.suspendedParticipantsFilterHidden);\n suspendedParticipantsFilterHidden.disabled = suspendedParticipantsFilterCheckbox.checked;\n }\n });\n\n // Event listener triggered upon hiding of the dropdown.\n extraFiltersDropdown.getElement().addEventListener('hide.bs.dropdown', () => {\n // Restore the filters to their stored preference values once the dropdown is closed.\n restoreAppliedWorkflowFilter(extraFiltersDropdown);\n restoreAppliedMarkerFilter(extraFiltersDropdown);\n restoreAppliedSuspendedParticipantsFilter(extraFiltersDropdown);\n });\n};\n\n/**\n * Restores the currently applied workflow filter to its stored preference value.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst restoreAppliedWorkflowFilter = async(extraFiltersDropdown) => {\n const appliedWorkflowFilter = await getUserPreference('assign_workflowfilter');\n const workflowFilterSelect = extraFiltersDropdown.getElement().querySelector(Selectors.workflowFilterElement);\n workflowFilterSelect.value = appliedWorkflowFilter;\n};\n\n/**\n * Restores the currently applied marker filter to its stored preference value.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst restoreAppliedMarkerFilter = async(extraFiltersDropdown) => {\n const markerFilterSelect = extraFiltersDropdown.getElement().querySelector(Selectors.markerFilterElement);\n if (markerFilterSelect) {\n const appliedMarkerFilter = await getUserPreference('assign_markerfilter');\n markerFilterSelect.value = appliedMarkerFilter;\n }\n};\n\n/**\n * Restores the currently suspended participants filter to its stored preference value.\n *\n * @param {DropdownDialog} extraFiltersDropdown The dropdown dialog instance.\n */\nconst restoreAppliedSuspendedParticipantsFilter = async(extraFiltersDropdown) => {\n const suspendedParticipantsFilterCheckbox = extraFiltersDropdown.getElement()\n .querySelector(Selectors.suspendedParticipantsFilterCheckbox);\n if (suspendedParticipantsFilterCheckbox) {\n const suspendedParticipantsFilterHidden = suspendedParticipantsFilterCheckbox.parentNode\n .querySelector(Selectors.suspendedParticipantsFilterHidden);\n const showOnlyActiveParticipants = await getUserPreference('grade_report_showonlyactiveenrol');\n suspendedParticipantsFilterCheckbox.checked = !showOnlyActiveParticipants;\n suspendedParticipantsFilterHidden.disabled = !showOnlyActiveParticipants;\n }\n};\n\n/**\n * Initialize module.\n */\nexport const init = () => {\n const extraFiltersDropdown = getDropdownDialog(Selectors.extraFiltersDropdown);\n if (extraFiltersDropdown) {\n registerEventListeners(extraFiltersDropdown);\n }\n};\n"],"names":["Selectors","restoreAppliedWorkflowFilter","async","appliedWorkflowFilter","extraFiltersDropdown","getElement","querySelector","value","restoreAppliedMarkerFilter","markerFilterSelect","appliedMarkerFilter","restoreAppliedSuspendedParticipantsFilter","suspendedParticipantsFilterCheckbox","suspendedParticipantsFilterHidden","parentNode","showOnlyActiveParticipants","checked","disabled","addEventListener","e","target","closest","preventDefault","setVisible","registerEventListeners"],"mappings":";;;;;;;;MA2BMA,+BACoB,yBADpBA,4BAEiB,yBAFjBA,gCAGqB,gCAHrBA,8BAImB,yCAJnBA,8CAKmC,6DALnCA,4CAMiC,2DA8CjCC,6BAA+BC,MAAAA,6BAC3BC,4BAA8B,iCAAkB,yBACzBC,qBAAqBC,aAAaC,cAAcN,iCACxDO,MAAQJ,uBAQ3BK,2BAA6BN,MAAAA,6BACzBO,mBAAqBL,qBAAqBC,aAAaC,cAAcN,kCACvES,mBAAoB,OACdC,0BAA4B,iCAAkB,uBACpDD,mBAAmBF,MAAQG,sBAS7BC,0CAA4CT,MAAAA,6BACxCU,oCAAsCR,qBAAqBC,aAC5DC,cAAcN,kDACfY,oCAAqC,OAC/BC,kCAAoCD,oCAAoCE,WACzER,cAAcN,6CACbe,iCAAmC,iCAAkB,oCAC3DH,oCAAoCI,SAAWD,2BAC/CF,kCAAkCI,UAAYF,2CAOlC,WACVX,sBAAuB,6BAAkBJ,gCAC3CI,sBA/EwBA,CAAAA,uBAE5BA,qBAAqBC,aAAaa,iBAAiB,SAASC,IAEpDA,EAAEC,OAAOC,QAAQrB,+BACjBmB,EAAEG,iBACFlB,qBAAqBmB,YAAW,OAKxCnB,qBAAqBC,aAAaa,iBAAiB,UAAUC,UACnDP,oCAAsCO,EAAEC,OAAOC,QAAQrB,+CAEzDY,sCAI0CA,oCAAoCE,WACzER,cAAcN,6CACeiB,SAAWL,oCAAoCI,YAKzFZ,qBAAqBC,aAAaa,iBAAiB,oBAAoB,KAEnEjB,6BAA6BG,sBAC7BI,2BAA2BJ,sBAC3BO,0CAA0CP,0BAmD1CoB,CAAuBpB"} \ No newline at end of file diff --git a/mod/assign/amd/src/actionbar/grading/extra_filters_dropdown.js b/mod/assign/amd/src/actionbar/grading/extra_filters_dropdown.js index 3509d16ed52d6..5d31c52a31f6c 100644 --- a/mod/assign/amd/src/actionbar/grading/extra_filters_dropdown.js +++ b/mod/assign/amd/src/actionbar/grading/extra_filters_dropdown.js @@ -15,7 +15,6 @@ import {getDropdownDialog} from 'core/local/dropdown/dialog'; import {getUserPreference} from 'core_user/repository'; -import $ from 'jquery'; /** * Module for the extra filters dropdown on the submissions page. @@ -65,7 +64,7 @@ const registerEventListeners = (extraFiltersDropdown) => { }); // Event listener triggered upon hiding of the dropdown. - $(extraFiltersDropdown.getElement()).on('hide.bs.dropdown', () => { + extraFiltersDropdown.getElement().addEventListener('hide.bs.dropdown', () => { // Restore the filters to their stored preference values once the dropdown is closed. restoreAppliedWorkflowFilter(extraFiltersDropdown); restoreAppliedMarkerFilter(extraFiltersDropdown); 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..698940fb71c65 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","theme_boost/bootstrap/tab","core/pending","core/local/aria/focuslock"],(function(_exports,_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,_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)}))})),document.addEventListener("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(),(()=>{document.addEventListener("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)}}})),document.addEventListener("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..8a2e4dc0241b8 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 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.addEventListener('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.addEventListener('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.addEventListener('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.addEventListener('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","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 12943a99d75c7..48d8a1bc3780a 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",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")})),document.addEventListener("click",(e=>{(0,_jquery.default)(e.target).closest('[data-toggle="popover"]').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("core/modal:bodyRendered",(e=>{[...e.target.querySelectorAll('[data-bs-toggle="popover"]')].map((popoverTriggerEl=>new Bootstrap.Popover(popoverTriggerEl,popoverConfig)))})),document.addEventListener("keydown",(e=>{const popoverTrigger=e.target.closest('[data-bs-toggle="popover"]');"Escape"===e.key&&popoverTrigger&&Bootstrap.Popover.getInstance(popoverTrigger).hide(),"Enter"===e.key&&popoverTrigger&&Bootstrap.Popover.getInstance(popoverTrigger).show()})),document.addEventListener("click",(e=>{const popoverTrigger=e.target.closest('[data-bs-toggle="popover"]');if(!popoverTrigger)return;const popover=Bootstrap.Popover.getInstance(popoverTrigger);popover._isShown()||popover.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 38980aa2697aa..9429724a3788b 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',\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 document.addEventListener('click', e => {\n $(e.target).closest('[data-toggle=\"popover\"]').popover('show');\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+BA+FMA,eAAiB,IAAIC,iBAAQ,mDAMnCC,KAAKC,OAnEgB,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,UAyDhBC,uBA/CM,QAAQC,QAAQ,CACdC,UAAW,OACXC,SAAU,0BACVC,QAAS,QACTC,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,WAG5BJ,SAASkB,iBAAiB,SAAS3B,wBAC7BA,EAAEE,QAAQ2B,QAAQ,2BAA2BhB,QAAQ,+BASzD,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 // Enable dynamically created popovers inside modals.\n document.addEventListener('core/modal:bodyRendered', (e) => {\n const modal = e.target;\n const popoverTriggerList = modal.querySelectorAll('[data-bs-toggle=\"popover\"]');\n [...popoverTriggerList].map(popoverTriggerEl => new Bootstrap.Popover(popoverTriggerEl, popoverConfig));\n });\n\n document.addEventListener('keydown', e => {\n const popoverTrigger = e.target.closest('[data-bs-toggle=\"popover\"]');\n if (e.key === 'Escape' && popoverTrigger) {\n Bootstrap.Popover.getInstance(popoverTrigger).hide();\n }\n if (e.key === 'Enter' && popoverTrigger) {\n Bootstrap.Popover.getInstance(popoverTrigger).show();\n }\n });\n document.addEventListener('click', e => {\n const popoverTrigger = e.target.closest('[data-bs-toggle=\"popover\"]');\n if (!popoverTrigger) {\n return;\n }\n const popover = Bootstrap.Popover.getInstance(popoverTrigger);\n if (!popover._isShown()) {\n popover.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","popoverTrigger","closest","key","getInstance","hide","show","popover","_isShown","enablePopovers","tooltipTriggerEl","Tooltip","resolve"],"mappings":";;;;;;;;gSAsGMA,eAAiB,IAAIC,iBAAQ,mDAMnCC,KAAKC,OA3EgB,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,UAgEhBC,GAvDuB,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,iBAGxFlB,SAASI,iBAAiB,2BAA4BC,QACpCA,EAAEE,OACiBN,iBAAiB,+BAC1BC,KAAI6B,kBAAoB,IAAIC,UAAUC,QAAQF,iBAAkBb,oBAG5FlB,SAASI,iBAAiB,WAAWC,UAC3B6B,eAAiB7B,EAAEE,OAAO4B,QAAQ,8BAC1B,WAAV9B,EAAE+B,KAAoBF,gBACtBF,UAAUC,QAAQI,YAAYH,gBAAgBI,OAEpC,UAAVjC,EAAE+B,KAAmBF,gBACrBF,UAAUC,QAAQI,YAAYH,gBAAgBK,UAGtDvC,SAASI,iBAAiB,SAASC,UACzB6B,eAAiB7B,EAAEE,OAAO4B,QAAQ,kCACnCD,4BAGCM,QAAUR,UAAUC,QAAQI,YAAYH,gBACzCM,QAAQC,YACTD,QAAQD,WA0BpBG,OAhB+B1C,SAASC,iBAAiB,+BAC7BC,KAAIyC,kBAAoB,IAAIX,UAAUY,QAAQD,oBAkD1E/C,eAAeiD"} \ No newline at end of file diff --git a/theme/boost/amd/src/aria.js b/theme/boost/amd/src/aria.js index 39d874f263be9..44da06c96a9e7 100644 --- a/theme/boost/amd/src/aria.js +++ b/theme/boost/amd/src/aria.js @@ -21,7 +21,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import $ from 'jquery'; +import Tab from 'theme_boost/bootstrap/tab'; import Pending from 'core/pending'; import * as FocusLockManager from 'core/local/aria/focuslock'; @@ -194,7 +194,7 @@ const dropdownFix = () => { }); // Trap focus if the dropdown is a dialog. - $(document).on('shown.bs.dropdown', e => { + document.addEventListener('shown.bs.dropdown', e => { const dialog = e.target.querySelector('.dropdown-menu[role="dialog"]'); if (dialog) { // Use setTimeout to make sure the dialog is positioned correctly to prevent random scrolling. @@ -205,7 +205,7 @@ const dropdownFix = () => { }); // Untrap focus when the dialog dropdown is closed. - $(document).on('hidden.bs.dropdown', e => { + document.addEventListener('hidden.bs.dropdown', e => { const dialog = e.target.querySelector('.dropdown-menu[role="dialog"]'); if (dialog) { FocusLockManager.untrapFocus(); @@ -233,7 +233,7 @@ const dropdownFix = () => { * A lot of Bootstrap's out of the box features don't work if dropdown items are not focusable. */ const comboboxFix = () => { - $(document).on('show.bs.dropdown', e => { + document.addEventListener('show.bs.dropdown', e => { if (e.relatedTarget.matches('[role="combobox"]')) { const combobox = e.relatedTarget; const listbox = document.querySelector(`#${combobox.getAttribute('aria-controls')}[role="listbox"]`); @@ -257,7 +257,7 @@ const comboboxFix = () => { } }); - $(document).on('hidden.bs.dropdown', e => { + document.addEventListener('hidden.bs.dropdown', e => { if (e.relatedTarget.matches('[role="combobox"]')) { const combobox = e.relatedTarget; const listbox = document.querySelector(`#${combobox.getAttribute('aria-controls')}[role="listbox"]`); @@ -486,7 +486,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 90fd67640e162..a101d3bc31f6b 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,30 +55,39 @@ const rememberTabs = () => { * */ const enablePopovers = () => { - $('body').popover({ + const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]'); + const popoverConfig = { container: 'body', - selector: '[data-toggle="popover"]', trigger: 'focus', - whitelist: Object.assign(DefaultWhitelist, { - table: [], - thead: [], - tbody: [], - tr: [], - th: [], - td: [], - }), + allowList: Object.assign(DefaultAllowlist, {table: [], thead: [], tbody: [], tr: [], th: [], td: []}), + }; + [...popoverTriggerList].map(popoverTriggerEl => new Bootstrap.Popover(popoverTriggerEl, popoverConfig)); + + // Enable dynamically created popovers inside modals. + document.addEventListener('core/modal:bodyRendered', (e) => { + const modal = e.target; + const popoverTriggerList = modal.querySelectorAll('[data-bs-toggle="popover"]'); + [...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'); + const popoverTrigger = e.target.closest('[data-bs-toggle="popover"]'); + if (e.key === 'Escape' && popoverTrigger) { + Bootstrap.Popover.getInstance(popoverTrigger).hide(); } - if (e.key === 'Enter' && e.target.closest('[data-toggle="popover"]')) { - $(e.target).popover('show'); + if (e.key === 'Enter' && popoverTrigger) { + Bootstrap.Popover.getInstance(popoverTrigger).show(); } }); document.addEventListener('click', e => { - $(e.target).closest('[data-toggle="popover"]').popover('show'); + const popoverTrigger = e.target.closest('[data-bs-toggle="popover"]'); + if (!popoverTrigger) { + return; + } + const popover = Bootstrap.Popover.getInstance(popoverTrigger); + if (!popover._isShown()) { + popover.show(); + } }); }; @@ -87,10 +96,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'); @@ -110,33 +117,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();