diff --git a/admin/settings/courses.php b/admin/settings/courses.php index 8936f7cf416..b6ba36886b0 100644 --- a/admin/settings/courses.php +++ b/admin/settings/courses.php @@ -61,12 +61,6 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) { array('moodle/restore:restorecourse') ) ); - $ADMIN->add('courses', - new admin_externalpage('activitychooser', new lang_string('activitychooserrecommendations', 'course'), - new moodle_url('/course/recommendations.php'), - array('moodle/course:recommendactivity') - ) - ); // Course Default Settings Page. // NOTE: these settings must be applied after all other settings because they depend on them. @@ -187,6 +181,30 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) { $CFG->wwwroot . '/course/pending.php', array('moodle/site:approvecourse'))); } + // Add a category for the Activity Chooser. + $ADMIN->add('courses', new admin_category('activitychooser', new lang_string('activitychoosercategory', 'course'))); + $temp = new admin_settingpage('activitychoosersettings', new lang_string('activitychoosersettings', 'course')); + $temp->add( + new admin_setting_configselect( + 'activitychoosertabmode', + new lang_string('activitychoosertabmode', 'course'), + new lang_string('activitychoosertabmode_desc', 'course'), + 0, + [ + 0 => new lang_string('activitychoosertabmodeone', 'course'), + 1 => new lang_string('activitychoosertabmodetwo', 'course'), + 2 => new lang_string('activitychoosertabmodethree', 'course'), + ] + ) + ); + $ADMIN->add('activitychooser', $temp); + $ADMIN->add('activitychooser', + new admin_externalpage('activitychooserrecommended', new lang_string('activitychooserrecommendations', 'course'), + new moodle_url('/course/recommendations.php'), + array('moodle/course:recommendactivity') + ) + ); + // Add a category for backups. $ADMIN->add('courses', new admin_category('backups', new lang_string('backups','admin'))); diff --git a/course/amd/build/activitychooser.min.js b/course/amd/build/activitychooser.min.js index b3f553fdbd4..da936c155e8 100644 --- a/course/amd/build/activitychooser.min.js +++ b/course/amd/build/activitychooser.min.js @@ -1,2 +1,2 @@ -function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core_course/activitychooser",["exports","core_course/local/activitychooser/dialogue","core_course/local/activitychooser/repository","core_course/local/activitychooser/selectors","core/custom_interaction_events","core/templates","core/modal_factory","core/str","core/pending"],function(a,b,c,d,e,f,g,h,i){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=l(b);c=l(c);d=j(d);e=j(e);f=l(f);g=l(g);i=j(i);function j(a){return a&&a.__esModule?a:{default:a}}function k(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;k=function(){return a};return a}function l(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=k();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var f=d?Object.getOwnPropertyDescriptor(a,e):null;if(f&&(f.get||f.set)){Object.defineProperty(c,e,f)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function m(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function n(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var h=a.apply(b,c);function f(a){m(h,d,e,f,g,"next",a)}function g(a){m(h,d,e,f,g,"throw",a)}f(void 0)})}}var o=function(a){var b=new i.default;p(a);b.resolve()};a.init=o;var p=function(a){var g=["click",e.default.events.activate,e.default.events.keyboardActivate],h=function(){var b=null;return function(){if(!b){b=new Promise(function(b){b(c.activityModules(a))})}return b}}();e.default.define(document,g);g.forEach(function(a){document.addEventListener(a,function(){var a=n(regeneratorRuntime.mark(function a(c){var e,g,i,j,k,l,m,n;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:if(!c.target.closest(d.default.elements.sectionmodchooser)){a.next=16;break}e=c.target.closest(d.default.elements.section);g=c.target.closest(d.default.elements.sectionmodchooser);i=e||g;k=new Promise(function(a){j=a});l=s(k);a.next=8;return h();case 8:m=a.sent;n=q(m,i.dataset.sectionid);b.displayChooser(l,n,u(m,i.dataset.sectionid));a.t0=j;a.next=14;return f.render("core_course/activitychooser",r(n));case 14:a.t1=a.sent;(0,a.t0)(a.t1);case 16:case"end":return a.stop();}}},a)}));return function(){return a.apply(this,arguments)}}())})},q=function(a,b){var c=JSON.parse(JSON.stringify(a));c.content_items.forEach(function(a){a.link+="§ion="+b});return c.content_items},r=function(a){var b=a.filter(function(a){return!0===a.favourite}),c=a.filter(function(a){return!0===a.recommended}),d=!!b.length,e=!!(c.length&&!1===d);return{default:a,favourites:b,recommended:c,favouritesFirst:d,recommendedFirst:e,fallback:!1===d&&!1===e}},s=function(a){return g.create({type:g.types.DEFAULT,title:(0,h.get_string)("addresourceoractivity"),body:a,large:!0,templateContext:{classes:"modchooser"}}).then(function(a){a.show();return a})},t=function(a,b){a.tabIndex=-1;a.classList.add("d-none");if(a.classList.contains("active")){a.classList.remove("active");a.setAttribute("aria-selected","false");var f=b.querySelector(d.default.regions.favouriteTab);f.classList.remove("active");var c=b.querySelector(d.default.regions.recommendedTabNav),e=b.querySelector(d.default.regions.defaultTabNav);if(!1===c.classList.contains("d-none")){c.classList.add("active");c.setAttribute("aria-selected","true");c.tabIndex=0;c.focus();var g=b.querySelector(d.default.regions.recommendedTab);g.classList.add("active")}else{e.classList.add("active");e.setAttribute("aria-selected","true");e.tabIndex=0;e.focus();var h=b.querySelector(d.default.regions.defaultTab);h.classList.add("active")}}},u=function(a,b){return function(){var c=n(regeneratorRuntime.mark(function c(e,g,h){var i,j,k,l,m,n,o,p,r,s,u;return regeneratorRuntime.wrap(function(c){while(1){switch(c.prev=c.next){case 0:i=h.querySelector(d.default.render.favourites);j=h.querySelectorAll("[data-internal=\"".concat(e,"\"] ").concat(d.default.actions.optionActions.manageFavourite));k=h.querySelector(d.default.regions.favouriteTabNav);l=a.content_items.find(function(a){var b=a.name;return b===e});m={};if(!l){c.next=27;break}if(!g){c.next=21;break}l.favourite=!0;m.content_items=a.content_items.filter(function(a){return!0===a.favourite});n=q(m,b);c.next=12;return f.renderForPromise("core_course/local/activitychooser/favourites",{favourites:n});case 12:o=c.sent;p=o.html;r=o.js;c.next=17;return f.replaceNodeContents(i,p,r);case 17:Array.from(j).forEach(function(a){a.classList.remove("text-muted");a.classList.add("text-primary");a.dataset.favourited="true";a.setAttribute("aria-pressed",!0);a.firstElementChild.classList.remove("fa-star-o");a.firstElementChild.classList.add("fa-star")});k.classList.remove("d-none");c.next=27;break;case 21:l.favourite=!1;s=i.querySelector("[data-internal=\"".concat(e,"\"]"));s.parentNode.removeChild(s);Array.from(j).forEach(function(a){a.classList.add("text-muted");a.classList.remove("text-primary");a.dataset.favourited="false";a.setAttribute("aria-pressed",!1);a.firstElementChild.classList.remove("fa-star");a.firstElementChild.classList.add("fa-star-o")});u=a.content_items.filter(function(a){return!0===a.favourite});if(0===u.length){t(k,h)}case 27:case"end":return c.stop();}}},c)}));return function(){return c.apply(this,arguments)}}()}}); +function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core_course/activitychooser",["exports","core_course/local/activitychooser/dialogue","core_course/local/activitychooser/repository","core_course/local/activitychooser/selectors","core/custom_interaction_events","core/templates","core/modal_factory","core/str","core/pending"],function(a,b,c,d,e,f,g,h,i){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=l(b);c=l(c);d=j(d);e=j(e);f=l(f);g=l(g);i=j(i);function j(a){return a&&a.__esModule?a:{default:a}}function k(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;k=function(){return a};return a}function l(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=k();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var f=d?Object.getOwnPropertyDescriptor(a,e):null;if(f&&(f.get||f.set)){Object.defineProperty(c,e,f)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function m(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function n(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var h=a.apply(b,c);function f(a){m(h,d,e,f,g,"next",a)}function g(a){m(h,d,e,f,g,"throw",a)}f(void 0)})}}var o=2,p=function(a,b){var c=new i.default;q(a,b);c.resolve()};a.init=p;var q=function(a,g){var h=["click",e.default.events.activate,e.default.events.keyboardActivate],i=function(){var b=null;return function(){if(!b){b=new Promise(function(b){b(c.activityModules(a))})}return b}}();e.default.define(document,h);h.forEach(function(a){document.addEventListener(a,function(){var a=n(regeneratorRuntime.mark(function a(c){var e,h,j,k,l,m,n,o;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:if(!c.target.closest(d.default.elements.sectionmodchooser)){a.next=16;break}e=c.target.closest(d.default.elements.section);h=c.target.closest(d.default.elements.sectionmodchooser);j=e||h;l=new Promise(function(a){k=a});m=t(l);a.next=8;return i();case 8:n=a.sent;o=r(n,j.dataset.sectionid);b.displayChooser(m,o,v(n,j.dataset.sectionid));a.t0=k;a.next=14;return f.render("core_course/activitychooser",s(o,g));case 14:a.t1=a.sent;(0,a.t0)(a.t1);case 16:case"end":return a.stop();}}},a)}));return function(){return a.apply(this,arguments)}}())})},r=function(a,b){var c=JSON.parse(JSON.stringify(a));c.content_items.forEach(function(a){a.link+="§ion="+b});return c.content_items},s=function(a,b){var c=[],d=[],e=!0,f=!1,g=!1,h=parseInt(b.tabmode),i=a.filter(function(a){return!0===a.favourite}),j=a.filter(function(a){return!0===a.recommended});if((h===0||h===o)&&h!==1){c=a.filter(function(a){return a.archetype===0});d=a.filter(function(a){return a.archetype===1});f=!0;g=!0;if(h===o){e=!1}}var k=!!i.length,l=!1===e&&!1===k,m=!0===e&&!1===k;return{default:a,showAll:e,activities:c,showActivities:f,activitiesFirst:l,resources:d,showResources:g,favourites:i,recommended:j,favouritesFirst:k,fallback:m}},t=function(a){return g.create({type:g.types.DEFAULT,title:(0,h.get_string)("addresourceoractivity"),body:a,large:!0,templateContext:{classes:"modchooser"}}).then(function(a){a.show();return a})},u=function(a,b){a.tabIndex=-1;a.classList.add("d-none");if(a.classList.contains("active")){a.classList.remove("active");a.setAttribute("aria-selected","false");var f=b.querySelector(d.default.regions.favouriteTab);f.classList.remove("active");var c=b.querySelector(d.default.regions.defaultTabNav),e=b.querySelector(d.default.regions.activityTabNav);if(!1===c.classList.contains("d-none")){c.classList.add("active");c.setAttribute("aria-selected","true");c.tabIndex=0;c.focus();var g=b.querySelector(d.default.regions.defaultTab);g.classList.add("active")}else{e.classList.add("active");e.setAttribute("aria-selected","true");e.tabIndex=0;e.focus();var h=b.querySelector(d.default.regions.activityTab);h.classList.add("active")}}},v=function(a,b){return function(){var c=n(regeneratorRuntime.mark(function c(e,g,h){var i,j,k,l,m,n,o,p,q,s,t;return regeneratorRuntime.wrap(function(c){while(1){switch(c.prev=c.next){case 0:i=h.querySelector(d.default.render.favourites);j=h.querySelectorAll("[data-internal=\"".concat(e,"\"] ").concat(d.default.actions.optionActions.manageFavourite));k=h.querySelector(d.default.regions.favouriteTabNav);l=a.content_items.find(function(a){var b=a.name;return b===e});m={};if(!l){c.next=27;break}if(!g){c.next=21;break}l.favourite=!0;m.content_items=a.content_items.filter(function(a){return!0===a.favourite});n=r(m,b);c.next=12;return f.renderForPromise("core_course/local/activitychooser/favourites",{favourites:n});case 12:o=c.sent;p=o.html;q=o.js;c.next=17;return f.replaceNodeContents(i,p,q);case 17:Array.from(j).forEach(function(a){a.classList.remove("text-muted");a.classList.add("text-primary");a.dataset.favourited="true";a.setAttribute("aria-pressed",!0);a.firstElementChild.classList.remove("fa-star-o");a.firstElementChild.classList.add("fa-star")});k.classList.remove("d-none");c.next=27;break;case 21:l.favourite=!1;s=i.querySelector("[data-internal=\"".concat(e,"\"]"));s.parentNode.removeChild(s);Array.from(j).forEach(function(a){a.classList.add("text-muted");a.classList.remove("text-primary");a.dataset.favourited="false";a.setAttribute("aria-pressed",!1);a.firstElementChild.classList.remove("fa-star");a.firstElementChild.classList.add("fa-star-o")});t=a.content_items.filter(function(a){return!0===a.favourite});if(0===t.length){u(k,h)}case 27:case"end":return c.stop();}}},c)}));return function(){return c.apply(this,arguments)}}()}}); //# sourceMappingURL=activitychooser.min.js.map diff --git a/course/amd/build/activitychooser.min.js.map b/course/amd/build/activitychooser.min.js.map index 78345bccbe7..83b317bff29 100644 --- a/course/amd/build/activitychooser.min.js.map +++ b/course/amd/build/activitychooser.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/activitychooser.js"],"names":["init","courseId","pendingPromise","Pending","registerListenerEvents","resolve","events","CustomEvents","activate","keyboardActivate","fetchModuleData","innerPromise","Promise","Repository","activityModules","define","document","forEach","event","addEventListener","e","target","closest","selectors","elements","sectionmodchooser","sectionDiv","section","button","caller","bodyPromise","bodyPromiseResolver","sectionModal","buildModal","data","builtModuleData","sectionIdMapper","dataset","sectionid","ChooserDialogue","displayChooser","partiallyAppliedFavouriteManager","Templates","render","templateDataBuilder","webServiceData","id","newData","JSON","parse","stringify","content_items","module","link","favourites","filter","mod","favourite","recommended","favouritesFirst","length","recommendedFirst","fallback","ModalFactory","create","type","types","DEFAULT","title","body","large","templateContext","classes","then","modal","show","nullFavouriteDomManager","favouriteTabNav","modalBody","tabIndex","classList","add","contains","remove","setAttribute","favouriteTab","querySelector","regions","recommendedTabNav","defaultTabNav","focus","recommendedTab","defaultTab","moduleData","sectionId","internal","favouriteArea","favouriteButtons","querySelectorAll","actions","optionActions","manageFavourite","result","find","name","newFaves","builtFaves","renderForPromise","html","js","replaceNodeContents","Array","from","element","favourited","firstElementChild","nodeToRemove","parentNode","removeChild"],"mappings":"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","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 modules in a course.\n *\n * @module core_course/activitychooser\n * @package core_course\n * @copyright 2020 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as ChooserDialogue from 'core_course/local/activitychooser/dialogue';\nimport * as Repository from 'core_course/local/activitychooser/repository';\nimport selectors from 'core_course/local/activitychooser/selectors';\nimport CustomEvents from 'core/custom_interaction_events';\nimport * as Templates from 'core/templates';\nimport * as ModalFactory from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\n\n/**\n * Set up the activity chooser.\n *\n * @method init\n * @param {Number} courseId Course ID to use later on in fetchModules()\n */\nexport const init = courseId => {\n const pendingPromise = new Pending();\n\n registerListenerEvents(courseId);\n\n pendingPromise.resolve();\n};\n\n/**\n * Once a selection has been made make the modal & module information and pass it along\n *\n * @method registerListenerEvents\n * @param {Number} courseId\n */\nconst registerListenerEvents = (courseId) => {\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n\n const fetchModuleData = (() => {\n let innerPromise = null;\n\n return () => {\n if (!innerPromise) {\n innerPromise = new Promise((resolve) => {\n resolve(Repository.activityModules(courseId));\n });\n }\n\n return innerPromise;\n };\n })();\n\n CustomEvents.define(document, events);\n\n // Display module chooser event listeners.\n events.forEach((event) => {\n document.addEventListener(event, async(e) => {\n if (e.target.closest(selectors.elements.sectionmodchooser)) {\n // We need to know who called this.\n // Standard courses use the ID in the main section info.\n const sectionDiv = e.target.closest(selectors.elements.section);\n // Front page courses need some special handling.\n const button = e.target.closest(selectors.elements.sectionmodchooser);\n // If we don't have a section ID use the fallback ID.\n const caller = sectionDiv || button;\n\n // We want to show the modal instantly but loading whilst waiting for our data.\n let bodyPromiseResolver;\n const bodyPromise = new Promise(resolve => {\n bodyPromiseResolver = resolve;\n });\n\n const sectionModal = buildModal(bodyPromise);\n\n // Now we have a modal we should start fetching data.\n const data = await fetchModuleData();\n\n // Apply the section id to all the module instance links.\n const builtModuleData = sectionIdMapper(data, caller.dataset.sectionid);\n\n ChooserDialogue.displayChooser(\n sectionModal,\n builtModuleData,\n partiallyAppliedFavouriteManager(data, caller.dataset.sectionid),\n );\n\n bodyPromiseResolver(await Templates.render(\n 'core_course/activitychooser',\n templateDataBuilder(builtModuleData)\n ));\n }\n });\n });\n};\n\n/**\n * Given the web service data and an ID we want to make a deep copy\n * of the WS data then add on the section ID to the addoption URL\n *\n * @method sectionIdMapper\n * @param {Object} webServiceData Our original data from the Web service call\n * @param {Number} id The ID of the section we need to append to the links\n * @return {Array} [modules] with URL's built\n */\nconst sectionIdMapper = (webServiceData, id) => {\n // We need to take a fresh deep copy of the original data as an object is a reference type.\n const newData = JSON.parse(JSON.stringify(webServiceData));\n newData.content_items.forEach((module) => {\n module.link += '§ion=' + id;\n });\n return newData.content_items;\n};\n\n/**\n * Given an array of modules we want to figure out where & how to place them into our template object\n *\n * @method templateDataBuilder\n * @param {Array} data our modules to manipulate into a Templatable object\n * @return {Object} Our built object ready to render out\n */\nconst templateDataBuilder = (data) => {\n // Filter the incoming data to find favourite & recommended modules.\n const favourites = data.filter(mod => mod.favourite === true);\n const recommended = data.filter(mod => mod.recommended === true);\n\n // Given the results of the above filters lets figure out what tab to set active.\n\n // We have some favourites.\n const favouritesFirst = !!favourites.length;\n // Check if we have no favourites but have some recommended.\n const recommendedFirst = !!(recommended.length && favouritesFirst === false);\n // We have nothing fallback to show all modules.\n const fallback = favouritesFirst === false && recommendedFirst === false;\n\n return {\n 'default': data,\n favourites: favourites,\n recommended: recommended,\n favouritesFirst: favouritesFirst,\n recommendedFirst: recommendedFirst,\n fallback: fallback,\n };\n};\n\n/**\n * Given an object we want to build a modal ready to show\n *\n * @method buildModal\n * @param {Promise} bodyPromise\n * @return {Object} The modal ready to display immediately and render body in later.\n */\nconst buildModal = bodyPromise => {\n return ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: getString('addresourceoractivity'),\n body: bodyPromise,\n large: true,\n templateContext: {\n classes: 'modchooser'\n }\n })\n .then(modal => {\n modal.show();\n return modal;\n });\n};\n\n/**\n * A small helper function to handle the case where there are no more favourites\n * and we need to mess a bit with the available tabs in the chooser\n *\n * @method nullFavouriteDomManager\n * @param {HTMLElement} favouriteTabNav Dom node of the favourite tab nav\n * @param {HTMLElement} modalBody Our current modals' body\n */\nconst nullFavouriteDomManager = (favouriteTabNav, modalBody) => {\n favouriteTabNav.tabIndex = -1;\n favouriteTabNav.classList.add('d-none');\n // Need to set active to an available tab.\n if (favouriteTabNav.classList.contains('active')) {\n favouriteTabNav.classList.remove('active');\n favouriteTabNav.setAttribute('aria-selected', 'false');\n const favouriteTab = modalBody.querySelector(selectors.regions.favouriteTab);\n favouriteTab.classList.remove('active');\n const recommendedTabNav = modalBody.querySelector(selectors.regions.recommendedTabNav);\n const defaultTabNav = modalBody.querySelector(selectors.regions.defaultTabNav);\n if (recommendedTabNav.classList.contains('d-none') === false) {\n recommendedTabNav.classList.add('active');\n recommendedTabNav.setAttribute('aria-selected', 'true');\n recommendedTabNav.tabIndex = 0;\n recommendedTabNav.focus();\n const recommendedTab = modalBody.querySelector(selectors.regions.recommendedTab);\n recommendedTab.classList.add('active');\n } else {\n defaultTabNav.classList.add('active');\n defaultTabNav.setAttribute('aria-selected', 'true');\n defaultTabNav.tabIndex = 0;\n defaultTabNav.focus();\n const defaultTab = modalBody.querySelector(selectors.regions.defaultTab);\n defaultTab.classList.add('active');\n }\n\n }\n};\n\n/**\n * Export a curried function where the builtModules has been applied.\n * We have our array of modules so we can rerender the favourites area and have all of the items sorted.\n *\n * @method partiallyAppliedFavouriteManager\n * @param {Array} moduleData This is our raw WS data that we need to manipulate\n * @param {Number} sectionId We need this to add the sectionID to the URL's in the faves area after rerender\n * @return {Function} partially applied function so we can manipulate DOM nodes easily & update our internal array\n */\nconst partiallyAppliedFavouriteManager = (moduleData, sectionId) => {\n /**\n * Curried function that is being returned.\n *\n * @param {String} internal Internal name of the module to manage\n * @param {Boolean} favourite Is the caller adding a favourite or removing one?\n * @param {HTMLElement} modalBody What we need to update whilst we are here\n */\n return async(internal, favourite, modalBody) => {\n const favouriteArea = modalBody.querySelector(selectors.render.favourites);\n\n // eslint-disable-next-line max-len\n const favouriteButtons = modalBody.querySelectorAll(`[data-internal=\"${internal}\"] ${selectors.actions.optionActions.manageFavourite}`);\n const favouriteTabNav = modalBody.querySelector(selectors.regions.favouriteTabNav);\n const result = moduleData.content_items.find(({name}) => name === internal);\n const newFaves = {};\n if (result) {\n if (favourite) {\n result.favourite = true;\n\n // eslint-disable-next-line camelcase\n newFaves.content_items = moduleData.content_items.filter(mod => mod.favourite === true);\n\n const builtFaves = sectionIdMapper(newFaves, sectionId);\n\n const {html, js} = await Templates.renderForPromise('core_course/local/activitychooser/favourites',\n {favourites: builtFaves});\n\n await Templates.replaceNodeContents(favouriteArea, html, js);\n\n Array.from(favouriteButtons).forEach((element) => {\n element.classList.remove('text-muted');\n element.classList.add('text-primary');\n element.dataset.favourited = 'true';\n element.setAttribute('aria-pressed', true);\n element.firstElementChild.classList.remove('fa-star-o');\n element.firstElementChild.classList.add('fa-star');\n });\n\n favouriteTabNav.classList.remove('d-none');\n } else {\n result.favourite = false;\n\n const nodeToRemove = favouriteArea.querySelector(`[data-internal=\"${internal}\"]`);\n\n nodeToRemove.parentNode.removeChild(nodeToRemove);\n\n Array.from(favouriteButtons).forEach((element) => {\n element.classList.add('text-muted');\n element.classList.remove('text-primary');\n element.dataset.favourited = 'false';\n element.setAttribute('aria-pressed', false);\n element.firstElementChild.classList.remove('fa-star');\n element.firstElementChild.classList.add('fa-star-o');\n });\n const newFaves = moduleData.content_items.filter(mod => mod.favourite === true);\n\n if (newFaves.length === 0) {\n nullFavouriteDomManager(favouriteTabNav, modalBody);\n }\n }\n }\n };\n};\n"],"file":"activitychooser.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/activitychooser.js"],"names":["ACTIVITIESRESOURCES","init","courseId","chooserConfig","pendingPromise","Pending","registerListenerEvents","resolve","events","CustomEvents","activate","keyboardActivate","fetchModuleData","innerPromise","Promise","Repository","activityModules","define","document","forEach","event","addEventListener","e","target","closest","selectors","elements","sectionmodchooser","sectionDiv","section","button","caller","bodyPromise","bodyPromiseResolver","sectionModal","buildModal","data","builtModuleData","sectionIdMapper","dataset","sectionid","ChooserDialogue","displayChooser","partiallyAppliedFavouriteManager","Templates","render","templateDataBuilder","webServiceData","id","newData","JSON","parse","stringify","content_items","module","link","activities","resources","showAll","showActivities","showResources","tabMode","parseInt","tabmode","favourites","filter","mod","favourite","recommended","archetype","favouritesFirst","length","activitiesFirst","fallback","ModalFactory","create","type","types","DEFAULT","title","body","large","templateContext","classes","then","modal","show","nullFavouriteDomManager","favouriteTabNav","modalBody","tabIndex","classList","add","contains","remove","setAttribute","favouriteTab","querySelector","regions","defaultTabNav","activitiesTabNav","activityTabNav","focus","defaultTab","activitiesTab","activityTab","moduleData","sectionId","internal","favouriteArea","favouriteButtons","querySelectorAll","actions","optionActions","manageFavourite","result","find","name","newFaves","builtFaves","renderForPromise","html","js","replaceNodeContents","Array","from","element","favourited","firstElementChild","nodeToRemove","parentNode","removeChild"],"mappings":"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","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 modules in a course.\n *\n * @module core_course/activitychooser\n * @package core_course\n * @copyright 2020 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as ChooserDialogue from 'core_course/local/activitychooser/dialogue';\nimport * as Repository from 'core_course/local/activitychooser/repository';\nimport selectors from 'core_course/local/activitychooser/selectors';\nimport CustomEvents from 'core/custom_interaction_events';\nimport * as Templates from 'core/templates';\nimport * as ModalFactory from 'core/modal_factory';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\n\n// Set up some JS module wide constants that can be added to in the future.\n\n// Tab config options.\nconst ALLACTIVITIESRESOURCES = 0;\nconst ONLYALL = 1;\nconst ACTIVITIESRESOURCES = 2;\n\n// Module types.\nconst ACTIVITY = 0;\nconst RESOURCE = 1;\n\n/**\n * Set up the activity chooser.\n *\n * @method init\n * @param {Number} courseId Course ID to use later on in fetchModules()\n * @param {Object} chooserConfig Any PHP config settings that we may need to reference\n */\nexport const init = (courseId, chooserConfig) => {\n const pendingPromise = new Pending();\n\n registerListenerEvents(courseId, chooserConfig);\n\n pendingPromise.resolve();\n};\n\n/**\n * Once a selection has been made make the modal & module information and pass it along\n *\n * @method registerListenerEvents\n * @param {Number} courseId\n * @param {Object} chooserConfig Any PHP config settings that we may need to reference\n */\nconst registerListenerEvents = (courseId, chooserConfig) => {\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n\n const fetchModuleData = (() => {\n let innerPromise = null;\n\n return () => {\n if (!innerPromise) {\n innerPromise = new Promise((resolve) => {\n resolve(Repository.activityModules(courseId));\n });\n }\n\n return innerPromise;\n };\n })();\n\n CustomEvents.define(document, events);\n\n // Display module chooser event listeners.\n events.forEach((event) => {\n document.addEventListener(event, async(e) => {\n if (e.target.closest(selectors.elements.sectionmodchooser)) {\n // We need to know who called this.\n // Standard courses use the ID in the main section info.\n const sectionDiv = e.target.closest(selectors.elements.section);\n // Front page courses need some special handling.\n const button = e.target.closest(selectors.elements.sectionmodchooser);\n // If we don't have a section ID use the fallback ID.\n const caller = sectionDiv || button;\n\n // We want to show the modal instantly but loading whilst waiting for our data.\n let bodyPromiseResolver;\n const bodyPromise = new Promise(resolve => {\n bodyPromiseResolver = resolve;\n });\n\n const sectionModal = buildModal(bodyPromise);\n\n // Now we have a modal we should start fetching data.\n const data = await fetchModuleData();\n\n // Apply the section id to all the module instance links.\n const builtModuleData = sectionIdMapper(data, caller.dataset.sectionid);\n\n ChooserDialogue.displayChooser(\n sectionModal,\n builtModuleData,\n partiallyAppliedFavouriteManager(data, caller.dataset.sectionid),\n );\n\n bodyPromiseResolver(await Templates.render(\n 'core_course/activitychooser',\n templateDataBuilder(builtModuleData, chooserConfig)\n ));\n }\n });\n });\n};\n\n/**\n * Given the web service data and an ID we want to make a deep copy\n * of the WS data then add on the section ID to the addoption URL\n *\n * @method sectionIdMapper\n * @param {Object} webServiceData Our original data from the Web service call\n * @param {Number} id The ID of the section we need to append to the links\n * @return {Array} [modules] with URL's built\n */\nconst sectionIdMapper = (webServiceData, id) => {\n // We need to take a fresh deep copy of the original data as an object is a reference type.\n const newData = JSON.parse(JSON.stringify(webServiceData));\n newData.content_items.forEach((module) => {\n module.link += '§ion=' + id;\n });\n return newData.content_items;\n};\n\n/**\n * Given an array of modules we want to figure out where & how to place them into our template object\n *\n * @method templateDataBuilder\n * @param {Array} data our modules to manipulate into a Templatable object\n * @param {Object} chooserConfig Any PHP config settings that we may need to reference\n * @return {Object} Our built object ready to render out\n */\nconst templateDataBuilder = (data, chooserConfig) => {\n // Setup of various bits and pieces we need to mutate before throwing it to the wolves.\n let activities = [];\n let resources = [];\n let showAll = true;\n let showActivities = false;\n let showResources = false;\n\n // Tab mode can be the following [All, Resources & Activities, All & Activities & Resources].\n const tabMode = parseInt(chooserConfig.tabmode);\n\n // Filter the incoming data to find favourite & recommended modules.\n const favourites = data.filter(mod => mod.favourite === true);\n const recommended = data.filter(mod => mod.recommended === true);\n\n // Both of these modes need Activity & Resource tabs.\n if ((tabMode === ALLACTIVITIESRESOURCES || tabMode === ACTIVITIESRESOURCES) && tabMode !== ONLYALL) {\n // Filter the incoming data to find activities then resources.\n activities = data.filter(mod => mod.archetype === ACTIVITY);\n resources = data.filter(mod => mod.archetype === RESOURCE);\n showActivities = true;\n showResources = true;\n\n // We want all of the previous information but no 'All' tab.\n if (tabMode === ACTIVITIESRESOURCES) {\n showAll = false;\n }\n }\n\n // Given the results of the above filters lets figure out what tab to set active.\n // We have some favourites.\n const favouritesFirst = !!favourites.length;\n // We are in tabMode 2 without any favourites.\n const activitiesFirst = showAll === false && favouritesFirst === false;\n // We have nothing fallback to show all modules.\n const fallback = showAll === true && favouritesFirst === false;\n\n return {\n 'default': data,\n showAll: showAll,\n activities: activities,\n showActivities: showActivities,\n activitiesFirst: activitiesFirst,\n resources: resources,\n showResources: showResources,\n favourites: favourites,\n recommended: recommended,\n favouritesFirst: favouritesFirst,\n fallback: fallback,\n };\n};\n\n/**\n * Given an object we want to build a modal ready to show\n *\n * @method buildModal\n * @param {Promise} bodyPromise\n * @return {Object} The modal ready to display immediately and render body in later.\n */\nconst buildModal = bodyPromise => {\n return ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: getString('addresourceoractivity'),\n body: bodyPromise,\n large: true,\n templateContext: {\n classes: 'modchooser'\n }\n })\n .then(modal => {\n modal.show();\n return modal;\n });\n};\n\n/**\n * A small helper function to handle the case where there are no more favourites\n * and we need to mess a bit with the available tabs in the chooser\n *\n * @method nullFavouriteDomManager\n * @param {HTMLElement} favouriteTabNav Dom node of the favourite tab nav\n * @param {HTMLElement} modalBody Our current modals' body\n */\nconst nullFavouriteDomManager = (favouriteTabNav, modalBody) => {\n favouriteTabNav.tabIndex = -1;\n favouriteTabNav.classList.add('d-none');\n // Need to set active to an available tab.\n if (favouriteTabNav.classList.contains('active')) {\n favouriteTabNav.classList.remove('active');\n favouriteTabNav.setAttribute('aria-selected', 'false');\n const favouriteTab = modalBody.querySelector(selectors.regions.favouriteTab);\n favouriteTab.classList.remove('active');\n const defaultTabNav = modalBody.querySelector(selectors.regions.defaultTabNav);\n const activitiesTabNav = modalBody.querySelector(selectors.regions.activityTabNav);\n if (defaultTabNav.classList.contains('d-none') === false) {\n defaultTabNav.classList.add('active');\n defaultTabNav.setAttribute('aria-selected', 'true');\n defaultTabNav.tabIndex = 0;\n defaultTabNav.focus();\n const defaultTab = modalBody.querySelector(selectors.regions.defaultTab);\n defaultTab.classList.add('active');\n } else {\n activitiesTabNav.classList.add('active');\n activitiesTabNav.setAttribute('aria-selected', 'true');\n activitiesTabNav.tabIndex = 0;\n activitiesTabNav.focus();\n const activitiesTab = modalBody.querySelector(selectors.regions.activityTab);\n activitiesTab.classList.add('active');\n }\n\n }\n};\n\n/**\n * Export a curried function where the builtModules has been applied.\n * We have our array of modules so we can rerender the favourites area and have all of the items sorted.\n *\n * @method partiallyAppliedFavouriteManager\n * @param {Array} moduleData This is our raw WS data that we need to manipulate\n * @param {Number} sectionId We need this to add the sectionID to the URL's in the faves area after rerender\n * @return {Function} partially applied function so we can manipulate DOM nodes easily & update our internal array\n */\nconst partiallyAppliedFavouriteManager = (moduleData, sectionId) => {\n /**\n * Curried function that is being returned.\n *\n * @param {String} internal Internal name of the module to manage\n * @param {Boolean} favourite Is the caller adding a favourite or removing one?\n * @param {HTMLElement} modalBody What we need to update whilst we are here\n */\n return async(internal, favourite, modalBody) => {\n const favouriteArea = modalBody.querySelector(selectors.render.favourites);\n\n // eslint-disable-next-line max-len\n const favouriteButtons = modalBody.querySelectorAll(`[data-internal=\"${internal}\"] ${selectors.actions.optionActions.manageFavourite}`);\n const favouriteTabNav = modalBody.querySelector(selectors.regions.favouriteTabNav);\n const result = moduleData.content_items.find(({name}) => name === internal);\n const newFaves = {};\n if (result) {\n if (favourite) {\n result.favourite = true;\n\n // eslint-disable-next-line camelcase\n newFaves.content_items = moduleData.content_items.filter(mod => mod.favourite === true);\n\n const builtFaves = sectionIdMapper(newFaves, sectionId);\n\n const {html, js} = await Templates.renderForPromise('core_course/local/activitychooser/favourites',\n {favourites: builtFaves});\n\n await Templates.replaceNodeContents(favouriteArea, html, js);\n\n Array.from(favouriteButtons).forEach((element) => {\n element.classList.remove('text-muted');\n element.classList.add('text-primary');\n element.dataset.favourited = 'true';\n element.setAttribute('aria-pressed', true);\n element.firstElementChild.classList.remove('fa-star-o');\n element.firstElementChild.classList.add('fa-star');\n });\n\n favouriteTabNav.classList.remove('d-none');\n } else {\n result.favourite = false;\n\n const nodeToRemove = favouriteArea.querySelector(`[data-internal=\"${internal}\"]`);\n\n nodeToRemove.parentNode.removeChild(nodeToRemove);\n\n Array.from(favouriteButtons).forEach((element) => {\n element.classList.add('text-muted');\n element.classList.remove('text-primary');\n element.dataset.favourited = 'false';\n element.setAttribute('aria-pressed', false);\n element.firstElementChild.classList.remove('fa-star');\n element.firstElementChild.classList.add('fa-star-o');\n });\n const newFaves = moduleData.content_items.filter(mod => mod.favourite === true);\n\n if (newFaves.length === 0) {\n nullFavouriteDomManager(favouriteTabNav, modalBody);\n }\n }\n }\n };\n};\n"],"file":"activitychooser.min.js"} \ 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 abfc5d39367..dde9eec09ea 100644 --- a/course/amd/build/local/activitychooser/dialogue.min.js +++ b/course/amd/build/local/activitychooser/dialogue.min.js @@ -1,2 +1,2 @@ -function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}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(a,b,c,d,e,f,g,h,i,j){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.displayChooser=void 0;b=m(b);c=l(c);d=m(d);e=l(e);h=l(h);i=m(i);function k(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;k=function(){return a};return a}function l(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=k();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var f=d?Object.getOwnPropertyDescriptor(a,e):null;if(f&&(f.get||f.set)){Object.defineProperty(c,e,f)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function m(a){return a&&a.__esModule?a:{default:a}}function n(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function o(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var h=a.apply(b,c);function f(a){n(h,d,e,f,g,"next",a)}function g(a){n(h,d,e,f,g,"throw",a)}f(void 0)})}}function p(a,b){return u(a)||t(a,b)||r(a,b)||q()}function q(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function r(a,b){if(!a)return;if("string"==typeof a)return s(a,b);var c=Object.prototype.toString.call(a).slice(8,-1);if("Object"===c&&a.constructor)c=a.constructor.name;if("Map"===c||"Set"===c)return Array.from(c);if("Arguments"===c||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(c))return s(a,b)}function s(a,b){if(null==b||b>a.length)b=a.length;for(var c=0,d=Array(b);ca.length)b=a.length;for(var c=0,d=Array(b);c.\n\n/**\n * A type of dialogue used as for choosing options.\n *\n * @module core_course/local/chooser/dialogue\n * @package core\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';\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 */\nconst showModuleHelp = (carousel, moduleData) => {\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 */\nconst registerListenerEvents = (modal, mappedModules, partialFavourite) => {\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 showModuleHelp(carousel, moduleData);\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);\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.getBody()[0], mappedModules, searchInput.value);\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(body, 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 toggleFocusableChooserOption(firstChooserOption, true);\n initTabsKeyboardNavigation(body);\n initChooserOptionsKeyboardNavigation(body, mappedModules, sectionChooserOptions);\n return body;\n })\n .catch();\n\n};\n\n/**\n * Initialise the keyboard navigation controls for the tab list items.\n *\n * @method initTabsKeyboardNavigation\n * @param {HTMLElement} body Our modal that we are working with\n */\nconst initTabsKeyboardNavigation = (body) => {\n // Set up the tab handlers.\n const favTabNav = body.querySelector(selectors.regions.favouriteTabNav);\n const recommendedTabNav = body.querySelector(selectors.regions.recommendedTabNav);\n const defaultTabNav = body.querySelector(selectors.regions.defaultTabNav);\n const tabNavArray = [favTabNav, recommendedTabNav, defaultTabNav];\n tabNavArray.forEach((element) => {\n return element.addEventListener('keydown', (e) => {\n // The first visible navigation tab link.\n const firstLink = e.target.parentElement.querySelector(selectors.elements.visibletabs);\n // The last navigation tab link. It would always be the default activities tab link.\n const lastLink = e.target.parentElement.lastElementChild;\n\n if (e.keyCode === arrowRight) {\n const nextLink = e.target.nextElementSibling;\n if (nextLink === null) {\n e.target.tabIndex = -1;\n firstLink.tabIndex = 0;\n firstLink.focus();\n } else if (nextLink.classList.contains('d-none')) {\n e.target.tabIndex = -1;\n lastLink.tabIndex = 0;\n lastLink.focus();\n } else {\n e.target.tabIndex = -1;\n nextLink.tabIndex = 0;\n nextLink.focus();\n }\n }\n if (e.keyCode === arrowLeft) {\n const previousLink = e.target.previousElementSibling;\n if (previousLink === null) {\n e.target.tabIndex = -1;\n lastLink.tabIndex = 0;\n lastLink.focus();\n } else if (previousLink.classList.contains('d-none')) {\n e.target.tabIndex = -1;\n firstLink.tabIndex = 0;\n firstLink.focus();\n } else {\n e.target.tabIndex = -1;\n previousLink.tabIndex = 0;\n previousLink.focus();\n }\n }\n if (e.keyCode === home) {\n e.target.tabIndex = -1;\n firstLink.tabIndex = 0;\n firstLink.focus();\n }\n if (e.keyCode === end) {\n e.target.tabIndex = -1;\n lastLink.tabIndex = 0;\n lastLink.focus();\n }\n if (e.keyCode === space) {\n e.preventDefault();\n e.target.click();\n }\n });\n });\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 */\nconst initChooserOptionsKeyboardNavigation = (body, mappedModules, chooserOptionsContainer) => {\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 showModuleHelp(carousel, moduleData);\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 {HTMLElement} modalBody The body of the 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(modalBody, mappedModules, searchQuery) => {\n const searchResultsContainer = modalBody.querySelector(selectors.regions.searchResults);\n const chooserContainer = modalBody.querySelector(selectors.regions.chooser);\n const clearSearchButton = modalBody.querySelector(selectors.elements.clearsearch);\n const searchIcon = modalBody.querySelector(selectors.elements.searchicon);\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);\n }\n // Display the \"clear\" search button in the activity chooser search bar.\n searchIcon.classList.add('d-none');\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 searchIcon.classList.remove('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);\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 */\nexport const displayChooser = (modalPromise, sectionModules, partialFavourite) => {\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);\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 })\n .catch();\n};\n"],"file":"dialogue.min.js"} \ No newline at end of file +{"version":3,"sources":["../../../src/local/activitychooser/dialogue.js"],"names":["showModuleHelp","carousel","moduleData","help","find","selectors","regions","innerHTML","classList","add","spinnerPromise","transitionPromiseResolver","transitionPromise","Promise","resolve","contentPromise","Templates","renderForPromise","all","then","html","js","replaceNodeContents","querySelector","chooserSummary","header","focus","catch","Notification","exception","one","manageFavouriteState","modalBody","caller","partialFavourite","isFavourite","dataset","favourited","id","name","internal","Repository","unfavouriteModule","favouriteModule","registerListenerEvents","modal","mappedModules","bodyClickListener","e","target","closest","actions","optionActions","showSummary","getBody","module","chooserOption","container","moduleName","modname","get","manageFavourite","activeSectionId","elements","activetab","getAttribute","sectionChooserOptions","getSectionChooserOptions","firstChooserOption","toggleFocusableChooserOption","initChooserOptionsKeyboardNavigation","matches","closeOption","on","allModules","modules","getModuleSelector","clearSearch","searchInput","search","value","toggleSearchResultsView","getBodyPromise","body","interval","pause","keyboard","addEventListener","initTabsKeyboardNavigation","favTabNav","favouriteTabNav","recommendedTabNav","defaultTabNav","activityTabNav","resourceTabNav","forEach","element","firstLink","parentElement","visibletabs","lastLink","lastElementChild","keyCode","arrowRight","nextLink","nextElementSibling","tabIndex","contains","arrowLeft","previousLink","previousElementSibling","home","end","space","preventDefault","click","chooserOptionsContainer","chooserOptions","querySelectorAll","Array","from","enter","currentOption","nextOption","firstOption","firstElementChild","toFocusOption","clickErrorHandler","focusChooserOption","previousOption","lastOption","currentChooserOption","previousChooserOption","isFocusable","chooserOptionLink","addChooser","chooserOptionHelp","chooserOptionFavourite","item","fallback","renderSearchResults","searchResultsContainer","searchResultsData","templateData","length","searchQuery","searchResults","chooserContainer","chooser","clearSearchButton","clearsearch","searchIcon","searchicon","searchModules","searchResultItemsContainer","searchResultItems","firstSearchResultItem","remove","setAttribute","removeAttribute","searchTerm","toLowerCase","activity","activityName","title","activityDesc","includes","push","setupKeyboardAccessibility","getModal","tab","activeSectionChooserOptions","prevActiveSectionId","relatedTarget","prevActiveSectionChooserOptions","disableFocusAllChooserOptions","allChooserOptions","displayChooser","modalPromise","sectionModules","Map","set","componentname","link","getRoot","ModalEvents","hidden","destroy"],"mappings":"wqBAwBA,OACA,OACA,OACA,OAGA,OACA,O,u3DAUMA,CAAAA,CAAc,CAAG,SAACC,CAAD,CAAWC,CAAX,CAA0B,CAC7C,GAAMC,CAAAA,CAAI,CAAGF,CAAQ,CAACG,IAAT,CAAcC,UAAUC,OAAV,CAAkBH,IAAhC,EAAsC,CAAtC,CAAb,CACAA,CAAI,CAACI,SAAL,CAAiB,EAAjB,CACAJ,CAAI,CAACK,SAAL,CAAeC,GAAf,CAAmB,QAAnB,EAH6C,GAMvCC,CAAAA,CAAc,CAAG,yBAAmBP,CAAnB,CANsB,CASzCQ,CAAyB,CAAG,IATa,CAUvCC,CAAiB,CAAG,GAAIC,CAAAA,OAAJ,CAAY,SAAAC,CAAO,CAAI,CAC7CH,CAAyB,CAAGG,CAC/B,CAFyB,CAVmB,CAevCC,CAAc,CAAGC,CAAS,CAACC,gBAAV,CAA2B,wCAA3B,CAAqEf,CAArE,CAfsB,CAkB7CW,OAAO,CAACK,GAAR,CAAY,CAACH,CAAD,CAAiBL,CAAjB,CAAiCE,CAAjC,CAAZ,EACKO,IADL,CACU,gCAAGC,CAAH,GAAGA,IAAH,CAASC,CAAT,GAASA,EAAT,OAAkBL,CAAAA,CAAS,CAACM,mBAAV,CAA8BnB,CAA9B,CAAoCiB,CAApC,CAA0CC,CAA1C,CAAlB,CADV,EAEKF,IAFL,CAEU,UAAM,CACRhB,CAAI,CAACoB,aAAL,CAAmBlB,UAAUC,OAAV,CAAkBkB,cAAlB,CAAiCC,MAApD,EAA4DC,KAA5D,GACA,MAAOvB,CAAAA,CACV,CALL,EAMKwB,KANL,CAMWC,UAAaC,SANxB,EASA5B,CAAQ,CAAC6B,GAAT,CAAa,kBAAb,CAAiC,UAAM,CACnCnB,CAAyB,EAC5B,CAFD,EAIAV,CAAQ,CAACA,QAAT,CAAkB,MAAlB,CACH,C,CAWK8B,CAAoB,4CAAG,WAAMC,CAAN,CAAiBC,CAAjB,CAAyBC,CAAzB,+FACnBC,CADmB,CACLF,CAAM,CAACG,OAAP,CAAeC,UADV,CAEnBC,CAFmB,CAEdL,CAAM,CAACG,OAAP,CAAeE,EAFD,CAGnBC,CAHmB,CAGZN,CAAM,CAACG,OAAP,CAAeG,IAHH,CAInBC,CAJmB,CAIRP,CAAM,CAACG,OAAP,CAAeI,QAJP,MAML,MAAhB,GAAAL,CANqB,kCAOfM,CAAAA,CAAU,CAACC,iBAAX,CAA6BH,CAA7B,CAAmCD,CAAnC,CAPe,QASrBJ,CAAgB,CAACM,CAAD,IAAkBR,CAAlB,CAAhB,CATqB,wCAWfS,CAAAA,CAAU,CAACE,eAAX,CAA2BJ,CAA3B,CAAiCD,CAAjC,CAXe,SAarBJ,CAAgB,CAACM,CAAD,IAAiBR,CAAjB,CAAhB,CAbqB,yCAAH,uD,CA0BpBY,CAAsB,CAAG,SAACC,CAAD,CAAQC,CAAR,CAAuBZ,CAAvB,CAA4C,CACvE,GAAMa,CAAAA,CAAiB,4CAAG,WAAMC,CAAN,2GACtB,GAAIA,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB7C,UAAU8C,OAAV,CAAkBC,aAAlB,CAAgCC,WAAjD,CAAJ,CAAmE,CACzDpD,CADyD,CAC9C,cAAE4C,CAAK,CAACS,OAAN,GAAgB,CAAhB,EAAmB/B,aAAnB,CAAiClB,UAAUC,OAAV,CAAkBL,QAAnD,CAAF,CAD8C,CAGzDsD,CAHyD,CAGhDP,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB7C,UAAUC,OAAV,CAAkBkD,aAAlB,CAAgCC,SAAjD,CAHgD,CAIzDC,CAJyD,CAI5CH,CAAM,CAACnB,OAAP,CAAeuB,OAJ6B,CAKzDzD,CALyD,CAK5C4C,CAAa,CAACc,GAAd,CAAkBF,CAAlB,CAL4C,CAM/D1D,CAAc,CAACC,CAAD,CAAWC,CAAX,CACjB,CARqB,IAUlB8C,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB7C,UAAU8C,OAAV,CAAkBC,aAAlB,CAAgCS,eAAjD,CAVkB,kBAWZ5B,CAXY,CAWHe,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB7C,UAAU8C,OAAV,CAAkBC,aAAlB,CAAgCS,eAAjD,CAXG,gBAYZ9B,CAAAA,CAAoB,CAACc,CAAK,CAACS,OAAN,GAAgB,CAAhB,CAAD,CAAqBrB,CAArB,CAA6BC,CAA7B,CAZR,QAaZ4B,CAbY,CAaMjB,CAAK,CAACS,OAAN,GAAgB,CAAhB,EAAmB/B,aAAnB,CAAiClB,UAAU0D,QAAV,CAAmBC,SAApD,EAA+DC,YAA/D,CAA4E,MAA5E,CAbN,CAcZC,CAdY,CAcYrB,CAAK,CAACS,OAAN,GAAgB,CAAhB,EACzB/B,aADyB,CACXlB,UAAUC,OAAV,CAAkB6D,wBAAlB,CAA2CL,CAA3C,CADW,CAdZ,CAgBZM,CAhBY,CAgBSF,CAAqB,CAC3C3C,aADsB,CACRlB,UAAUC,OAAV,CAAkBkD,aAAlB,CAAgCC,SADxB,CAhBT,CAkBlBY,CAA4B,CAACD,CAAD,IAA5B,CACAE,CAAoC,CAACzB,CAAK,CAACS,OAAN,GAAgB,CAAhB,CAAD,CAAqBR,CAArB,CAAoCoB,CAApC,CAApC,CAnBkB,QAuBtB,GAAIlB,CAAC,CAACC,MAAF,CAASsB,OAAT,CAAiBlE,UAAU8C,OAAV,CAAkBqB,WAAnC,CAAJ,CAAqD,CAC3CvE,CAD2C,CAChC,cAAE4C,CAAK,CAACS,OAAN,GAAgB,CAAhB,EAAmB/B,aAAnB,CAAiClB,UAAUC,OAAV,CAAkBL,QAAnD,CAAF,CADgC,CAIjDA,CAAQ,CAACA,QAAT,CAAkB,MAAlB,EACAA,CAAQ,CAACwE,EAAT,CAAY,kBAAZ,CAAgC,UAAM,IAC5BC,CAAAA,CAAU,CAAG7B,CAAK,CAACS,OAAN,GAAgB,CAAhB,EAAmB/B,aAAnB,CAAiClB,UAAUC,OAAV,CAAkBqE,OAAnD,CADe,CAE5B1C,CAAM,CAAGyC,CAAU,CAACnD,aAAX,CAAyBlB,UAAUC,OAAV,CAAkBsE,iBAAlB,CAAoC5B,CAAC,CAACC,MAAF,CAASb,OAAT,CAAiBuB,OAArD,CAAzB,CAFmB,CAGlC1B,CAAM,CAACP,KAAP,EACH,CAJD,CAKH,CAGD,GAAIsB,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB7C,UAAU8C,OAAV,CAAkB0B,WAAnC,CAAJ,CAAqD,CAE3CC,CAF2C,CAE7BjC,CAAK,CAACS,OAAN,GAAgB,CAAhB,EAAmB/B,aAAnB,CAAiClB,UAAU8C,OAAV,CAAkB4B,MAAnD,CAF6B,CAGjDD,CAAW,CAACE,KAAZ,CAAoB,EAApB,CACAF,CAAW,CAACpD,KAAZ,GACAuD,CAAuB,CAACpC,CAAK,CAACS,OAAN,GAAgB,CAAhB,CAAD,CAAqBR,CAArB,CAAoCgC,CAAW,CAACE,KAAhD,CAC1B,CA1CqB,yCAAH,uDAAvB,CA6CAnC,CAAK,CAACqC,cAAN,GAGC/D,IAHD,CAGM,SAAAgE,CAAI,QAAIA,CAAAA,CAAI,CAAC,CAAD,CAAR,CAHV,EAMChE,IAND,CAMM,SAAAgE,CAAI,CAAI,CACV,cAAEA,CAAI,CAAC5D,aAAL,CAAmBlB,UAAUC,OAAV,CAAkBL,QAArC,CAAF,EACKA,QADL,CACc,CACNmF,QAAQ,GADF,CAENC,KAAK,GAFC,CAGNC,QAAQ,GAHF,CADd,EAOA,MAAOH,CAAAA,CACV,CAfD,EAkBChE,IAlBD,CAkBM,SAAAgE,CAAI,CAAI,CACVA,CAAI,CAACI,gBAAL,CAAsB,OAAtB,CAA+BxC,CAA/B,EACA,MAAOoC,CAAAA,CACV,CArBD,EAwBChE,IAxBD,CAwBM,SAAAgE,CAAI,CAAI,CACV,GAAML,CAAAA,CAAW,CAAGK,CAAI,CAAC5D,aAAL,CAAmBlB,UAAU8C,OAAV,CAAkB4B,MAArC,CAApB,CAEAD,CAAW,CAACS,gBAAZ,CAA6B,OAA7B,CAAsC,eAAS,UAAM,CAEjDN,CAAuB,CAACE,CAAD,CAAOrC,CAAP,CAAsBgC,CAAW,CAACE,KAAlC,CAC1B,CAHqC,CAGnC,GAHmC,CAAtC,EAIA,MAAOG,CAAAA,CACV,CAhCD,EAmCChE,IAnCD,CAmCM,SAAAgE,CAAI,CAAI,IAEJrB,CAAAA,CAAe,CAAGqB,CAAI,CAAC5D,aAAL,CAAmBlB,UAAU0D,QAAV,CAAmBC,SAAtC,EAAiDC,YAAjD,CAA8D,MAA9D,CAFd,CAGJC,CAAqB,CAAGiB,CAAI,CAAC5D,aAAL,CAAmBlB,UAAUC,OAAV,CAAkB6D,wBAAlB,CAA2CL,CAA3C,CAAnB,CAHpB,CAIJM,CAAkB,CAAGF,CAAqB,CAAC3C,aAAtB,CAAoClB,UAAUC,OAAV,CAAkBkD,aAAlB,CAAgCC,SAApE,CAJjB,CAMVY,CAA4B,CAACD,CAAD,IAA5B,CACAoB,CAA0B,CAACL,CAAD,CAA1B,CACAb,CAAoC,CAACa,CAAD,CAAOrC,CAAP,CAAsBoB,CAAtB,CAApC,CAEA,MAAOiB,CAAAA,CACV,CA9CD,EA+CCxD,KA/CD,EAiDH,C,CAQK6D,CAA0B,CAAG,SAACL,CAAD,CAAU,IAEnCM,CAAAA,CAAS,CAAGN,CAAI,CAAC5D,aAAL,CAAmBlB,UAAUC,OAAV,CAAkBoF,eAArC,CAFuB,CAGnCC,CAAiB,CAAGR,CAAI,CAAC5D,aAAL,CAAmBlB,UAAUC,OAAV,CAAkBqF,iBAArC,CAHe,CAInCC,CAAa,CAAGT,CAAI,CAAC5D,aAAL,CAAmBlB,UAAUC,OAAV,CAAkBsF,aAArC,CAJmB,CAKnCC,CAAc,CAAGV,CAAI,CAAC5D,aAAL,CAAmBlB,UAAUC,OAAV,CAAkBuF,cAArC,CALkB,CAMnCC,CAAc,CAAGX,CAAI,CAAC5D,aAAL,CAAmBlB,UAAUC,OAAV,CAAkBwF,cAArC,CANkB,CAOrB,CAACL,CAAD,CAAYE,CAAZ,CAA+BC,CAA/B,CAA8CC,CAA9C,CAA8DC,CAA9D,CACpB,CAAYC,OAAZ,CAAoB,SAACC,CAAD,CAAa,CAC7B,MAAOA,CAAAA,CAAO,CAACT,gBAAR,CAAyB,SAAzB,CAAoC,SAACvC,CAAD,CAAO,IAExCiD,CAAAA,CAAS,CAAGjD,CAAC,CAACC,MAAF,CAASiD,aAAT,CAAuB3E,aAAvB,CAAqClB,UAAU0D,QAAV,CAAmBoC,WAAxD,CAF4B,CAIxCC,CAAQ,CAAGpD,CAAC,CAACC,MAAF,CAASiD,aAAT,CAAuBG,gBAJM,CAM9C,GAAIrD,CAAC,CAACsD,OAAF,GAAcC,YAAlB,CAA8B,CAC1B,GAAMC,CAAAA,CAAQ,CAAGxD,CAAC,CAACC,MAAF,CAASwD,kBAA1B,CACA,GAAiB,IAAb,GAAAD,CAAJ,CAAuB,CACnBxD,CAAC,CAACC,MAAF,CAASyD,QAAT,CAAoB,CAAC,CAArB,CACAT,CAAS,CAACS,QAAV,CAAqB,CAArB,CACAT,CAAS,CAACvE,KAAV,EACH,CAJD,IAIO,IAAI8E,CAAQ,CAAChG,SAAT,CAAmBmG,QAAnB,CAA4B,QAA5B,CAAJ,CAA2C,CAC9C3D,CAAC,CAACC,MAAF,CAASyD,QAAT,CAAoB,CAAC,CAArB,CACAN,CAAQ,CAACM,QAAT,CAAoB,CAApB,CACAN,CAAQ,CAAC1E,KAAT,EACH,CAJM,IAIA,CACHsB,CAAC,CAACC,MAAF,CAASyD,QAAT,CAAoB,CAAC,CAArB,CACAF,CAAQ,CAACE,QAAT,CAAoB,CAApB,CACAF,CAAQ,CAAC9E,KAAT,EACH,CACJ,CACD,GAAIsB,CAAC,CAACsD,OAAF,GAAcM,WAAlB,CAA6B,CACzB,GAAMC,CAAAA,CAAY,CAAG7D,CAAC,CAACC,MAAF,CAAS6D,sBAA9B,CACA,GAAqB,IAAjB,GAAAD,CAAJ,CAA2B,CACvB7D,CAAC,CAACC,MAAF,CAASyD,QAAT,CAAoB,CAAC,CAArB,CACAN,CAAQ,CAACM,QAAT,CAAoB,CAApB,CACAN,CAAQ,CAAC1E,KAAT,EACH,CAJD,IAIO,IAAImF,CAAY,CAACrG,SAAb,CAAuBmG,QAAvB,CAAgC,QAAhC,CAAJ,CAA+C,CAClD3D,CAAC,CAACC,MAAF,CAASyD,QAAT,CAAoB,CAAC,CAArB,CACAT,CAAS,CAACS,QAAV,CAAqB,CAArB,CACAT,CAAS,CAACvE,KAAV,EACH,CAJM,IAIA,CACHsB,CAAC,CAACC,MAAF,CAASyD,QAAT,CAAoB,CAAC,CAArB,CACAG,CAAY,CAACH,QAAb,CAAwB,CAAxB,CACAG,CAAY,CAACnF,KAAb,EACH,CACJ,CACD,GAAIsB,CAAC,CAACsD,OAAF,GAAcS,MAAlB,CAAwB,CACpB/D,CAAC,CAACC,MAAF,CAASyD,QAAT,CAAoB,CAAC,CAArB,CACAT,CAAS,CAACS,QAAV,CAAqB,CAArB,CACAT,CAAS,CAACvE,KAAV,EACH,CACD,GAAIsB,CAAC,CAACsD,OAAF,GAAcU,KAAlB,CAAuB,CACnBhE,CAAC,CAACC,MAAF,CAASyD,QAAT,CAAoB,CAAC,CAArB,CACAN,CAAQ,CAACM,QAAT,CAAoB,CAApB,CACAN,CAAQ,CAAC1E,KAAT,EACH,CACD,GAAIsB,CAAC,CAACsD,OAAF,GAAcW,OAAlB,CAAyB,CACrBjE,CAAC,CAACkE,cAAF,GACAlE,CAAC,CAACC,MAAF,CAASkE,KAAT,EACH,CACJ,CApDM,CAqDV,CAtDD,CAuDH,C,CAUK7C,CAAoC,CAAG,SAACa,CAAD,CAAOrC,CAAP,CAAsBsE,CAAtB,CAAkD,CAC3F,GAAMC,CAAAA,CAAc,CAAGD,CAAuB,CAACE,gBAAxB,CAAyCjH,UAAUC,OAAV,CAAkBkD,aAAlB,CAAgCC,SAAzE,CAAvB,CAEA8D,KAAK,CAACC,IAAN,CAAWH,CAAX,EAA2BtB,OAA3B,CAAmC,SAACC,CAAD,CAAa,CAC5C,MAAOA,CAAAA,CAAO,CAACT,gBAAR,CAAyB,SAAzB,CAAoC,SAACvC,CAAD,CAAO,CAG9C,GAAIA,CAAC,CAACsD,OAAF,GAAcmB,OAAd,EAAuBzE,CAAC,CAACsD,OAAF,GAAcW,OAAzC,CAAgD,CAC5C,GAAIjE,CAAC,CAACC,MAAF,CAASsB,OAAT,CAAiBlE,UAAU8C,OAAV,CAAkBC,aAAlB,CAAgCC,WAAjD,CAAJ,CAAmE,CAC/DL,CAAC,CAACkE,cAAF,GAD+D,GAEzD3D,CAAAA,CAAM,CAAGP,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB7C,UAAUC,OAAV,CAAkBkD,aAAlB,CAAgCC,SAAjD,CAFgD,CAGzDC,CAAU,CAAGH,CAAM,CAACnB,OAAP,CAAeuB,OAH6B,CAIzDzD,CAAU,CAAG4C,CAAa,CAACc,GAAd,CAAkBF,CAAlB,CAJ4C,CAKzDzD,CAAQ,CAAG,cAAEkF,CAAI,CAAC5D,aAAL,CAAmBlB,UAAUC,OAAV,CAAkBL,QAArC,CAAF,CAL8C,CAM/DA,CAAQ,CAACA,QAAT,CAAkB,CACdmF,QAAQ,GADM,CAEdC,KAAK,GAFS,CAGdC,QAAQ,GAHM,CAAlB,EAKAtF,CAAc,CAACC,CAAD,CAAWC,CAAX,CACjB,CACJ,CAGD,GAAI8C,CAAC,CAACsD,OAAF,GAAcC,YAAlB,CAA8B,CAC1BvD,CAAC,CAACkE,cAAF,GAD0B,GAEpBQ,CAAAA,CAAa,CAAG1E,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB7C,UAAUC,OAAV,CAAkBkD,aAAlB,CAAgCC,SAAjD,CAFI,CAGpBkE,CAAU,CAAGD,CAAa,CAACjB,kBAHP,CAIpBmB,CAAW,CAAGR,CAAuB,CAACS,iBAJlB,CAKpBC,CAAa,CAAGC,CAAiB,CAACJ,CAAD,CAAaC,CAAb,CALb,CAM1BI,CAAkB,CAACF,CAAD,CAAgBJ,CAAhB,CACrB,CAGD,GAAI1E,CAAC,CAACsD,OAAF,GAAcM,WAAlB,CAA6B,CACzB5D,CAAC,CAACkE,cAAF,GADyB,GAEnBQ,CAAAA,CAAa,CAAG1E,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB7C,UAAUC,OAAV,CAAkBkD,aAAlB,CAAgCC,SAAjD,CAFG,CAGnBwE,CAAc,CAAGP,CAAa,CAACZ,sBAHZ,CAInBoB,CAAU,CAAGd,CAAuB,CAACf,gBAJlB,CAKnByB,CAAa,CAAGC,CAAiB,CAACE,CAAD,CAAiBC,CAAjB,CALd,CAMzBF,CAAkB,CAACF,CAAD,CAAgBJ,CAAhB,CACrB,CAED,GAAI1E,CAAC,CAACsD,OAAF,GAAcS,MAAlB,CAAwB,CACpB/D,CAAC,CAACkE,cAAF,GADoB,GAEdQ,CAAAA,CAAa,CAAG1E,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB7C,UAAUC,OAAV,CAAkBkD,aAAlB,CAAgCC,SAAjD,CAFF,CAGdmE,CAAW,CAAGR,CAAuB,CAACS,iBAHxB,CAIpBG,CAAkB,CAACJ,CAAD,CAAcF,CAAd,CACrB,CAED,GAAI1E,CAAC,CAACsD,OAAF,GAAcU,KAAlB,CAAuB,CACnBhE,CAAC,CAACkE,cAAF,GADmB,GAEbQ,CAAAA,CAAa,CAAG1E,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiB7C,UAAUC,OAAV,CAAkBkD,aAAlB,CAAgCC,SAAjD,CAFH,CAGbyE,CAAU,CAAGd,CAAuB,CAACf,gBAHxB,CAInB2B,CAAkB,CAACE,CAAD,CAAaR,CAAb,CACrB,CACJ,CApDM,CAqDV,CAtDD,CAuDH,C,CASKM,CAAkB,CAAG,SAACG,CAAD,CAAwD,IAAjCC,CAAAA,CAAiC,wDAAT,IAAS,CAC/E,GAA8B,IAA1B,GAAAA,CAAJ,CAAoC,CAChC/D,CAA4B,CAAC+D,CAAD,IAC/B,CAED/D,CAA4B,CAAC8D,CAAD,IAA5B,CACAA,CAAoB,CAACzG,KAArB,EACH,C,CASK2C,CAA4B,CAAG,SAACb,CAAD,CAAgB6E,CAAhB,CAAgC,IAC3DC,CAAAA,CAAiB,CAAG9E,CAAa,CAACjC,aAAd,CAA4BlB,UAAU8C,OAAV,CAAkBoF,UAA9C,CADuC,CAE3DC,CAAiB,CAAGhF,CAAa,CAACjC,aAAd,CAA4BlB,UAAU8C,OAAV,CAAkBC,aAAlB,CAAgCC,WAA5D,CAFuC,CAG3DoF,CAAsB,CAAGjF,CAAa,CAACjC,aAAd,CAA4BlB,UAAU8C,OAAV,CAAkBC,aAAlB,CAAgCS,eAA5D,CAHkC,CAKjE,GAAIwE,CAAJ,CAAiB,CAEb7E,CAAa,CAACkD,QAAd,CAAyB,CAAzB,CACA4B,CAAiB,CAAC5B,QAAlB,CAA6B,CAA7B,CACA8B,CAAiB,CAAC9B,QAAlB,CAA6B,CAA7B,CACA+B,CAAsB,CAAC/B,QAAvB,CAAkC,CACrC,CAND,IAMO,CAEHlD,CAAa,CAACkD,QAAd,CAAyB,CAAC,CAA1B,CACA4B,CAAiB,CAAC5B,QAAlB,CAA6B,CAAC,CAA9B,CACA8B,CAAiB,CAAC9B,QAAlB,CAA6B,CAAC,CAA9B,CACA+B,CAAsB,CAAC/B,QAAvB,CAAkC,CAAC,CACtC,CACJ,C,CAUKqB,CAAiB,CAAG,SAACW,CAAD,CAAOC,CAAP,CAAoB,CAC1C,GAAa,IAAT,GAAAD,CAAJ,CAAmB,CACf,MAAOA,CAAAA,CACV,CAFD,IAEO,CACH,MAAOC,CAAAA,CACV,CACJ,C,CASKC,CAAmB,4CAAG,WAAMC,CAAN,CAA8BC,CAA9B,+FAClBC,CADkB,CACH,CACjB,oBAAuBD,CAAiB,CAACE,MADxB,CAEjB,cAAiBF,CAFA,CADG,gBAMC9H,CAAAA,CAAS,CAACC,gBAAV,CAA2B,kDAA3B,CAA+E8H,CAA/E,CAND,iBAMjB3H,CANiB,GAMjBA,IANiB,CAMXC,CANW,GAMXA,EANW,gBAOlBL,CAAAA,CAAS,CAACM,mBAAV,CAA8BuH,CAA9B,CAAsDzH,CAAtD,CAA4DC,CAA5D,CAPkB,yCAAH,uD,CAkBnB4D,CAAuB,4CAAG,WAAMjD,CAAN,CAAiBc,CAAjB,CAAgCmG,CAAhC,qGACtBJ,CADsB,CACG7G,CAAS,CAACT,aAAV,CAAwBlB,UAAUC,OAAV,CAAkB4I,aAA1C,CADH,CAEtBC,CAFsB,CAEHnH,CAAS,CAACT,aAAV,CAAwBlB,UAAUC,OAAV,CAAkB8I,OAA1C,CAFG,CAGtBC,CAHsB,CAGFrH,CAAS,CAACT,aAAV,CAAwBlB,UAAU0D,QAAV,CAAmBuF,WAA3C,CAHE,CAItBC,CAJsB,CAITvH,CAAS,CAACT,aAAV,CAAwBlB,UAAU0D,QAAV,CAAmByF,UAA3C,CAJS,MAMH,CAArB,CAAAP,CAAW,CAACD,MANY,mBAOlBF,CAPkB,CAOEW,CAAa,CAAC3G,CAAD,CAAgBmG,CAAhB,CAPf,gBAQlBL,CAAAA,CAAmB,CAACC,CAAD,CAAyBC,CAAzB,CARD,QASlBY,CATkB,CASWb,CAAsB,CAACtH,aAAvB,CAAqClB,UAAUC,OAAV,CAAkBqJ,iBAAvD,CATX,CAUlBC,CAVkB,CAUMF,CAA0B,CAACnI,aAA3B,CAAyClB,UAAUC,OAAV,CAAkBkD,aAAlB,CAAgCC,SAAzE,CAVN,CAWxB,GAAImG,CAAJ,CAA2B,CAEvBvF,CAA4B,CAACuF,CAAD,IAA5B,CAEAtF,CAAoC,CAACtC,CAAD,CAAYc,CAAZ,CAA2B4G,CAA3B,CACvC,CAEDH,CAAU,CAAC/I,SAAX,CAAqBC,GAArB,CAAyB,QAAzB,EACA4I,CAAiB,CAAC7I,SAAlB,CAA4BqJ,MAA5B,CAAmC,QAAnC,EAEAV,CAAgB,CAACW,YAAjB,CAA8B,QAA9B,CAAwC,QAAxC,EAEAjB,CAAsB,CAACkB,eAAvB,CAAuC,QAAvC,EAvBwB,wBA0BxBV,CAAiB,CAAC7I,SAAlB,CAA4BC,GAA5B,CAAgC,QAAhC,EACA8I,CAAU,CAAC/I,SAAX,CAAqBqJ,MAArB,CAA4B,QAA5B,EAEAhB,CAAsB,CAACiB,YAAvB,CAAoC,QAApC,CAA8C,QAA9C,EAEAX,CAAgB,CAACY,eAAjB,CAAiC,QAAjC,EA/BwB,yCAAH,uD,CA2CvBN,CAAa,CAAG,SAAC9E,CAAD,CAAUqF,CAAV,CAAyB,CAC3C,GAAmB,EAAf,GAAAA,CAAJ,CAAuB,CACnB,MAAOrF,CAAAA,CACV,CACDqF,CAAU,CAAGA,CAAU,CAACC,WAAX,EAAb,CACA,GAAMf,CAAAA,CAAa,CAAG,EAAtB,CACAvE,CAAO,CAACoB,OAAR,CAAgB,SAACmE,CAAD,CAAc,IACpBC,CAAAA,CAAY,CAAGD,CAAQ,CAACE,KAAT,CAAeH,WAAf,EADK,CAEpBI,CAAY,CAAGH,CAAQ,CAAC/J,IAAT,CAAc8J,WAAd,EAFK,CAG1B,GAAIE,CAAY,CAACG,QAAb,CAAsBN,CAAtB,GAAqCK,CAAY,CAACC,QAAb,CAAsBN,CAAtB,CAAzC,CAA4E,CACxEd,CAAa,CAACqB,IAAd,CAAmBL,CAAnB,CACH,CACJ,CAND,EAQA,MAAOhB,CAAAA,CACV,C,CASKsB,CAA0B,CAAG,SAAC3H,CAAD,CAAQC,CAAR,CAA0B,CACzDD,CAAK,CAAC4H,QAAN,GAAiB,CAAjB,EAAoB/D,QAApB,CAA+B,CAAC,CAAhC,CAEA7D,CAAK,CAACqC,cAAN,GAAuB/D,IAAvB,CAA4B,SAAAgE,CAAI,CAAI,CAChC,cAAE9E,UAAU0D,QAAV,CAAmB2G,GAArB,EAA0BjG,EAA1B,CAA6B,cAA7B,CAA6C,SAACzB,CAAD,CAAO,IAC1Cc,CAAAA,CAAe,CAAGd,CAAC,CAACC,MAAF,CAASgB,YAAT,CAAsB,MAAtB,CADwB,CAE1C0G,CAA2B,CAAGxF,CAAI,CAAC,CAAD,CAAJ,CAC/B5D,aAD+B,CACjBlB,UAAUC,OAAV,CAAkB6D,wBAAlB,CAA2CL,CAA3C,CADiB,CAFY,CAI1CM,CAAkB,CAAGuG,CAA2B,CACjDpJ,aADsB,CACRlB,UAAUC,OAAV,CAAkBkD,aAAlB,CAAgCC,SADxB,CAJqB,CAM1CmH,CAAmB,CAAG5H,CAAC,CAAC6H,aAAF,CAAgB5G,YAAhB,CAA6B,MAA7B,CANoB,CAO1C6G,CAA+B,CAAG3F,CAAI,CAAC,CAAD,CAAJ,CACnC5D,aADmC,CACrBlB,UAAUC,OAAV,CAAkB6D,wBAAlB,CAA2CyG,CAA3C,CADqB,CAPQ,CAWhDG,CAA6B,CAACD,CAAD,CAA7B,CAEAzG,CAA4B,CAACD,CAAD,IAA5B,CACAE,CAAoC,CAACa,CAAI,CAAC,CAAD,CAAL,CAAUrC,CAAV,CAAyB6H,CAAzB,CACvC,CAfD,CAiBH,CAlBD,EAkBGhJ,KAlBH,CAkBSC,UAAaC,SAlBtB,CAmBH,C,CAQKkJ,CAA6B,CAAG,SAAC7G,CAAD,CAA2B,CAC7D,GAAM8G,CAAAA,CAAiB,CAAG9G,CAAqB,CAACoD,gBAAtB,CAAuCjH,UAAUC,OAAV,CAAkBkD,aAAlB,CAAgCC,SAAvE,CAA1B,CACAuH,CAAiB,CAACjF,OAAlB,CAA0B,SAACvC,CAAD,CAAmB,CACzCa,CAA4B,CAACb,CAAD,IAC/B,CAFD,CAGH,C,kBAU6B,QAAjByH,CAAAA,cAAiB,CAACC,CAAD,CAAeC,CAAf,CAA+BjJ,CAA/B,CAAoD,CAE9E,GAAMY,CAAAA,CAAa,CAAG,GAAIsI,CAAAA,GAA1B,CACAD,CAAc,CAACpF,OAAf,CAAuB,SAACxC,CAAD,CAAY,CAC/BT,CAAa,CAACuI,GAAd,CAAkB9H,CAAM,CAAC+H,aAAP,CAAuB,GAAvB,CAA6B/H,CAAM,CAACgI,IAAtD,CAA4DhI,CAA5D,CACH,CAFD,EAKA2H,CAAY,CAAC/J,IAAb,CAAkB,SAAA0B,CAAK,CAAI,CACvBD,CAAsB,CAACC,CAAD,CAAQC,CAAR,CAAuBZ,CAAvB,CAAtB,CAGAsI,CAA0B,CAAC3H,CAAD,CAAQC,CAAR,CAA1B,CAGAD,CAAK,CAAC2I,OAAN,GAAgB/G,EAAhB,CAAmBgH,CAAW,CAACC,MAA/B,CAAuC,UAAM,CACzC7I,CAAK,CAAC8I,OAAN,EACH,CAFD,EAIA,MAAO9I,CAAAA,CACV,CAZD,EAYGlB,KAZH,EAaH,C","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/chooser/dialogue\n * @package core\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';\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 */\nconst showModuleHelp = (carousel, moduleData) => {\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 */\nconst registerListenerEvents = (modal, mappedModules, partialFavourite) => {\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 showModuleHelp(carousel, moduleData);\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);\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.getBody()[0], mappedModules, searchInput.value);\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(body, 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 initTabsKeyboardNavigation(body);\n initChooserOptionsKeyboardNavigation(body, mappedModules, sectionChooserOptions);\n\n return body;\n })\n .catch();\n\n};\n\n/**\n * Initialise the keyboard navigation controls for the tab list items.\n *\n * @method initTabsKeyboardNavigation\n * @param {HTMLElement} body Our modal that we are working with\n */\nconst initTabsKeyboardNavigation = (body) => {\n // Set up the tab handlers.\n const favTabNav = body.querySelector(selectors.regions.favouriteTabNav);\n const recommendedTabNav = body.querySelector(selectors.regions.recommendedTabNav);\n const defaultTabNav = body.querySelector(selectors.regions.defaultTabNav);\n const activityTabNav = body.querySelector(selectors.regions.activityTabNav);\n const resourceTabNav = body.querySelector(selectors.regions.resourceTabNav);\n const tabNavArray = [favTabNav, recommendedTabNav, defaultTabNav, activityTabNav, resourceTabNav];\n tabNavArray.forEach((element) => {\n return element.addEventListener('keydown', (e) => {\n // The first visible navigation tab link.\n const firstLink = e.target.parentElement.querySelector(selectors.elements.visibletabs);\n // The last navigation tab link. It would always be the default activities tab link.\n const lastLink = e.target.parentElement.lastElementChild;\n\n if (e.keyCode === arrowRight) {\n const nextLink = e.target.nextElementSibling;\n if (nextLink === null) {\n e.target.tabIndex = -1;\n firstLink.tabIndex = 0;\n firstLink.focus();\n } else if (nextLink.classList.contains('d-none')) {\n e.target.tabIndex = -1;\n lastLink.tabIndex = 0;\n lastLink.focus();\n } else {\n e.target.tabIndex = -1;\n nextLink.tabIndex = 0;\n nextLink.focus();\n }\n }\n if (e.keyCode === arrowLeft) {\n const previousLink = e.target.previousElementSibling;\n if (previousLink === null) {\n e.target.tabIndex = -1;\n lastLink.tabIndex = 0;\n lastLink.focus();\n } else if (previousLink.classList.contains('d-none')) {\n e.target.tabIndex = -1;\n firstLink.tabIndex = 0;\n firstLink.focus();\n } else {\n e.target.tabIndex = -1;\n previousLink.tabIndex = 0;\n previousLink.focus();\n }\n }\n if (e.keyCode === home) {\n e.target.tabIndex = -1;\n firstLink.tabIndex = 0;\n firstLink.focus();\n }\n if (e.keyCode === end) {\n e.target.tabIndex = -1;\n lastLink.tabIndex = 0;\n lastLink.focus();\n }\n if (e.keyCode === space) {\n e.preventDefault();\n e.target.click();\n }\n });\n });\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 */\nconst initChooserOptionsKeyboardNavigation = (body, mappedModules, chooserOptionsContainer) => {\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 showModuleHelp(carousel, moduleData);\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 {HTMLElement} modalBody The body of the 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(modalBody, mappedModules, searchQuery) => {\n const searchResultsContainer = modalBody.querySelector(selectors.regions.searchResults);\n const chooserContainer = modalBody.querySelector(selectors.regions.chooser);\n const clearSearchButton = modalBody.querySelector(selectors.elements.clearsearch);\n const searchIcon = modalBody.querySelector(selectors.elements.searchicon);\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);\n }\n // Display the \"clear\" search button in the activity chooser search bar.\n searchIcon.classList.add('d-none');\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 searchIcon.classList.remove('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);\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 */\nexport const displayChooser = (modalPromise, sectionModules, partialFavourite) => {\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);\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"],"file":"dialogue.min.js"} \ No newline at end of file diff --git a/course/amd/build/local/activitychooser/selectors.min.js b/course/amd/build/local/activitychooser/selectors.min.js index 1fddca81df3..0b4a6edf1ea 100644 --- a/course/amd/build/local/activitychooser/selectors.min.js +++ b/course/amd/build/local/activitychooser/selectors.min.js @@ -1,2 +1,2 @@ -define ("core_course/local/activitychooser/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b=function(a,b){return"[data-".concat(a,"=\"").concat(b,"\"]")},c={regions:{chooser:b("region","chooser-container"),getSectionChooserOptions:function getSectionChooserOptions(a){return"".concat(a," ").concat(b("region","chooser-options-container"))},chooserOption:{container:b("region","chooser-option-container"),actions:b("region","chooser-option-actions-container"),info:b("region","chooser-option-info-container")},chooserSummary:{container:b("region","chooser-option-summary-container"),content:b("region","chooser-option-summary-content-container"),header:b("region","summary-header"),actions:b("region","chooser-option-summary-actions-container")},carousel:b("region","carousel"),help:b("region","help"),modules:b("region","modules"),favouriteTabNav:b("region","favourite-tab-nav"),recommendedTabNav:b("region","recommended-tab-nav"),defaultTabNav:b("region","default-tab-nav"),favouriteTab:b("region","favourites"),recommendedTab:b("region","recommended"),defaultTab:b("region","default"),getModuleSelector:function getModuleSelector(a){return"[role=\"menuitem\"][data-modname=\"".concat(a,"\"]")},searchResults:b("region","search-results-container"),searchResultItems:b("region","search-result-items-container")},actions:{optionActions:{showSummary:b("action","show-option-summary"),manageFavourite:b("action","manage-module-favourite")},addChooser:b("action","add-chooser-option"),closeOption:b("action","close-chooser-option-summary"),hide:b("action","hide"),search:b("action","search"),clearSearch:b("action","clearsearch")},render:{favourites:b("render","favourites-area")},elements:{section:".section",sectionmodchooser:"button.section-modchooser-link",sitemenu:".block_site_main_menu",sitetopic:"div.sitetopic",tab:"a[data-toggle=\"tab\"]",activetab:"a[data-toggle=\"tab\"][aria-selected=\"true\"]",visibletabs:"a[data-toggle=\"tab\"]:not(.d-none)",searchicon:".input-group-append .search-icon",clearsearch:".input-group-append .clear"}};a.default=c;return a.default}); +define ("core_course/local/activitychooser/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b=function(a,b){return"[data-".concat(a,"=\"").concat(b,"\"]")},c={regions:{chooser:b("region","chooser-container"),getSectionChooserOptions:function getSectionChooserOptions(a){return"".concat(a," ").concat(b("region","chooser-options-container"))},chooserOption:{container:b("region","chooser-option-container"),actions:b("region","chooser-option-actions-container"),info:b("region","chooser-option-info-container")},chooserSummary:{container:b("region","chooser-option-summary-container"),content:b("region","chooser-option-summary-content-container"),header:b("region","summary-header"),actions:b("region","chooser-option-summary-actions-container")},carousel:b("region","carousel"),help:b("region","help"),modules:b("region","modules"),favouriteTabNav:b("region","favourite-tab-nav"),recommendedTabNav:b("region","recommended-tab-nav"),defaultTabNav:b("region","default-tab-nav"),activityTabNav:b("region","activity-tab-nav"),resourceTabNav:b("region","resources-tab-nav"),favouriteTab:b("region","favourites"),recommendedTab:b("region","recommended"),defaultTab:b("region","default"),activityTab:b("region","activity"),resourceTab:b("region","resources"),getModuleSelector:function getModuleSelector(a){return"[role=\"menuitem\"][data-modname=\"".concat(a,"\"]")},searchResults:b("region","search-results-container"),searchResultItems:b("region","search-result-items-container")},actions:{optionActions:{showSummary:b("action","show-option-summary"),manageFavourite:b("action","manage-module-favourite")},addChooser:b("action","add-chooser-option"),closeOption:b("action","close-chooser-option-summary"),hide:b("action","hide"),search:b("action","search"),clearSearch:b("action","clearsearch")},render:{favourites:b("render","favourites-area")},elements:{section:".section",sectionmodchooser:"button.section-modchooser-link",sitemenu:".block_site_main_menu",sitetopic:"div.sitetopic",tab:"a[data-toggle=\"tab\"]",activetab:"a[data-toggle=\"tab\"][aria-selected=\"true\"]",visibletabs:"a[data-toggle=\"tab\"]:not(.d-none)",searchicon:".input-group-append .search-icon",clearsearch:".input-group-append .clear"}};a.default=c;return a.default}); //# sourceMappingURL=selectors.min.js.map diff --git a/course/amd/build/local/activitychooser/selectors.min.js.map b/course/amd/build/local/activitychooser/selectors.min.js.map index bfa0617c9f3..177b53e8f34 100644 --- a/course/amd/build/local/activitychooser/selectors.min.js.map +++ b/course/amd/build/local/activitychooser/selectors.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../../../src/local/activitychooser/selectors.js"],"names":["getDataSelector","name","value","regions","chooser","getSectionChooserOptions","containerid","chooserOption","container","actions","info","chooserSummary","content","header","carousel","help","modules","favouriteTabNav","recommendedTabNav","defaultTabNav","favouriteTab","recommendedTab","defaultTab","getModuleSelector","modname","searchResults","searchResultItems","optionActions","showSummary","manageFavourite","addChooser","closeOption","hide","search","clearSearch","render","favourites","elements","section","sectionmodchooser","sitemenu","sitetopic","tab","activetab","visibletabs","searchicon","clearsearch"],"mappings":"gKA+BMA,CAAAA,CAAe,CAAG,SAACC,CAAD,CAAOC,CAAP,CAAiB,CACrC,sBAAgBD,CAAhB,eAAyBC,CAAzB,OACH,C,GAEc,CACXC,OAAO,CAAE,CACLC,OAAO,CAAEJ,CAAe,CAAC,QAAD,CAAW,mBAAX,CADnB,CAELK,wBAAwB,CAAE,kCAAAC,CAAW,kBAAOA,CAAP,aAAsBN,CAAe,CAAC,QAAD,CAAW,2BAAX,CAArC,EAFhC,CAGLO,aAAa,CAAE,CACXC,SAAS,CAAER,CAAe,CAAC,QAAD,CAAW,0BAAX,CADf,CAEXS,OAAO,CAAET,CAAe,CAAC,QAAD,CAAW,kCAAX,CAFb,CAGXU,IAAI,CAAEV,CAAe,CAAC,QAAD,CAAW,+BAAX,CAHV,CAHV,CAQLW,cAAc,CAAE,CACZH,SAAS,CAAER,CAAe,CAAC,QAAD,CAAW,kCAAX,CADd,CAEZY,OAAO,CAAEZ,CAAe,CAAC,QAAD,CAAW,0CAAX,CAFZ,CAGZa,MAAM,CAAEb,CAAe,CAAC,QAAD,CAAW,gBAAX,CAHX,CAIZS,OAAO,CAAET,CAAe,CAAC,QAAD,CAAW,0CAAX,CAJZ,CARX,CAcLc,QAAQ,CAAEd,CAAe,CAAC,QAAD,CAAW,UAAX,CAdpB,CAeLe,IAAI,CAAEf,CAAe,CAAC,QAAD,CAAW,MAAX,CAfhB,CAgBLgB,OAAO,CAAEhB,CAAe,CAAC,QAAD,CAAW,SAAX,CAhBnB,CAiBLiB,eAAe,CAAEjB,CAAe,CAAC,QAAD,CAAW,mBAAX,CAjB3B,CAkBLkB,iBAAiB,CAAElB,CAAe,CAAC,QAAD,CAAW,qBAAX,CAlB7B,CAmBLmB,aAAa,CAAEnB,CAAe,CAAC,QAAD,CAAW,iBAAX,CAnBzB,CAoBLoB,YAAY,CAAEpB,CAAe,CAAC,QAAD,CAAW,YAAX,CApBxB,CAqBLqB,cAAc,CAAErB,CAAe,CAAC,QAAD,CAAW,aAAX,CArB1B,CAsBLsB,UAAU,CAAEtB,CAAe,CAAC,QAAD,CAAW,SAAX,CAtBtB,CAuBLuB,iBAAiB,CAAE,2BAAAC,CAAO,qDAAuCA,CAAvC,QAvBrB,CAwBLC,aAAa,CAAEzB,CAAe,CAAC,QAAD,CAAW,0BAAX,CAxBzB,CAyBL0B,iBAAiB,CAAE1B,CAAe,CAAC,QAAD,CAAW,+BAAX,CAzB7B,CADE,CA4BXS,OAAO,CAAE,CACLkB,aAAa,CAAE,CACXC,WAAW,CAAE5B,CAAe,CAAC,QAAD,CAAW,qBAAX,CADjB,CAEX6B,eAAe,CAAE7B,CAAe,CAAC,QAAD,CAAW,yBAAX,CAFrB,CADV,CAKL8B,UAAU,CAAE9B,CAAe,CAAC,QAAD,CAAW,oBAAX,CALtB,CAML+B,WAAW,CAAE/B,CAAe,CAAC,QAAD,CAAW,8BAAX,CANvB,CAOLgC,IAAI,CAAEhC,CAAe,CAAC,QAAD,CAAW,MAAX,CAPhB,CAQLiC,MAAM,CAAEjC,CAAe,CAAC,QAAD,CAAW,QAAX,CARlB,CASLkC,WAAW,CAAElC,CAAe,CAAC,QAAD,CAAW,aAAX,CATvB,CA5BE,CAuCXmC,MAAM,CAAE,CACJC,UAAU,CAAEpC,CAAe,CAAC,QAAD,CAAW,iBAAX,CADvB,CAvCG,CA0CXqC,QAAQ,CAAE,CACNC,OAAO,CAAE,UADH,CAENC,iBAAiB,CAAE,gCAFb,CAGNC,QAAQ,CAAE,uBAHJ,CAINC,SAAS,CAAE,eAJL,CAKNC,GAAG,CAAE,wBALC,CAMNC,SAAS,CAAE,gDANL,CAONC,WAAW,CAAE,qCAPP,CAQNC,UAAU,CAAE,kCARN,CASNC,WAAW,CAAE,4BATP,CA1CC,C","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 * Define all of the selectors we will be using on the grading interface.\n *\n * @module core_course/local/chooser/selectors\n * @package core_course\n * @copyright 2019 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * A small helper function to build queryable data selectors.\n * @method getDataSelector\n * @param {String} name\n * @param {String} value\n * @return {string}\n */\nconst getDataSelector = (name, value) => {\n return `[data-${name}=\"${value}\"]`;\n};\n\nexport default {\n regions: {\n chooser: getDataSelector('region', 'chooser-container'),\n getSectionChooserOptions: containerid => `${containerid} ${getDataSelector('region', 'chooser-options-container')}`,\n chooserOption: {\n container: getDataSelector('region', 'chooser-option-container'),\n actions: getDataSelector('region', 'chooser-option-actions-container'),\n info: getDataSelector('region', 'chooser-option-info-container'),\n },\n chooserSummary: {\n container: getDataSelector('region', 'chooser-option-summary-container'),\n content: getDataSelector('region', 'chooser-option-summary-content-container'),\n header: getDataSelector('region', 'summary-header'),\n actions: getDataSelector('region', 'chooser-option-summary-actions-container'),\n },\n carousel: getDataSelector('region', 'carousel'),\n help: getDataSelector('region', 'help'),\n modules: getDataSelector('region', 'modules'),\n favouriteTabNav: getDataSelector('region', 'favourite-tab-nav'),\n recommendedTabNav: getDataSelector('region', 'recommended-tab-nav'),\n defaultTabNav: getDataSelector('region', 'default-tab-nav'),\n favouriteTab: getDataSelector('region', 'favourites'),\n recommendedTab: getDataSelector('region', 'recommended'),\n defaultTab: getDataSelector('region', 'default'),\n getModuleSelector: modname => `[role=\"menuitem\"][data-modname=\"${modname}\"]`,\n searchResults: getDataSelector('region', 'search-results-container'),\n searchResultItems: getDataSelector('region', 'search-result-items-container'),\n },\n actions: {\n optionActions: {\n showSummary: getDataSelector('action', 'show-option-summary'),\n manageFavourite: getDataSelector('action', 'manage-module-favourite'),\n },\n addChooser: getDataSelector('action', 'add-chooser-option'),\n closeOption: getDataSelector('action', 'close-chooser-option-summary'),\n hide: getDataSelector('action', 'hide'),\n search: getDataSelector('action', 'search'),\n clearSearch: getDataSelector('action', 'clearsearch'),\n },\n render: {\n favourites: getDataSelector('render', 'favourites-area'),\n },\n elements: {\n section: '.section',\n sectionmodchooser: 'button.section-modchooser-link',\n sitemenu: '.block_site_main_menu',\n sitetopic: 'div.sitetopic',\n tab: 'a[data-toggle=\"tab\"]',\n activetab: 'a[data-toggle=\"tab\"][aria-selected=\"true\"]',\n visibletabs: 'a[data-toggle=\"tab\"]:not(.d-none)',\n searchicon: '.input-group-append .search-icon',\n clearsearch: '.input-group-append .clear'\n },\n};\n"],"file":"selectors.min.js"} \ No newline at end of file +{"version":3,"sources":["../../../src/local/activitychooser/selectors.js"],"names":["getDataSelector","name","value","regions","chooser","getSectionChooserOptions","containerid","chooserOption","container","actions","info","chooserSummary","content","header","carousel","help","modules","favouriteTabNav","recommendedTabNav","defaultTabNav","activityTabNav","resourceTabNav","favouriteTab","recommendedTab","defaultTab","activityTab","resourceTab","getModuleSelector","modname","searchResults","searchResultItems","optionActions","showSummary","manageFavourite","addChooser","closeOption","hide","search","clearSearch","render","favourites","elements","section","sectionmodchooser","sitemenu","sitetopic","tab","activetab","visibletabs","searchicon","clearsearch"],"mappings":"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","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 * Define all of the selectors we will be using on the grading interface.\n *\n * @module core_course/local/chooser/selectors\n * @package core_course\n * @copyright 2019 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * A small helper function to build queryable data selectors.\n * @method getDataSelector\n * @param {String} name\n * @param {String} value\n * @return {string}\n */\nconst getDataSelector = (name, value) => {\n return `[data-${name}=\"${value}\"]`;\n};\n\nexport default {\n regions: {\n chooser: getDataSelector('region', 'chooser-container'),\n getSectionChooserOptions: containerid => `${containerid} ${getDataSelector('region', 'chooser-options-container')}`,\n chooserOption: {\n container: getDataSelector('region', 'chooser-option-container'),\n actions: getDataSelector('region', 'chooser-option-actions-container'),\n info: getDataSelector('region', 'chooser-option-info-container'),\n },\n chooserSummary: {\n container: getDataSelector('region', 'chooser-option-summary-container'),\n content: getDataSelector('region', 'chooser-option-summary-content-container'),\n header: getDataSelector('region', 'summary-header'),\n actions: getDataSelector('region', 'chooser-option-summary-actions-container'),\n },\n carousel: getDataSelector('region', 'carousel'),\n help: getDataSelector('region', 'help'),\n modules: getDataSelector('region', 'modules'),\n favouriteTabNav: getDataSelector('region', 'favourite-tab-nav'),\n recommendedTabNav: getDataSelector('region', 'recommended-tab-nav'),\n defaultTabNav: getDataSelector('region', 'default-tab-nav'),\n activityTabNav: getDataSelector('region', 'activity-tab-nav'),\n resourceTabNav: getDataSelector('region', 'resources-tab-nav'),\n favouriteTab: getDataSelector('region', 'favourites'),\n recommendedTab: getDataSelector('region', 'recommended'),\n defaultTab: getDataSelector('region', 'default'),\n activityTab: getDataSelector('region', 'activity'),\n resourceTab: getDataSelector('region', 'resources'),\n getModuleSelector: modname => `[role=\"menuitem\"][data-modname=\"${modname}\"]`,\n searchResults: getDataSelector('region', 'search-results-container'),\n searchResultItems: getDataSelector('region', 'search-result-items-container'),\n },\n actions: {\n optionActions: {\n showSummary: getDataSelector('action', 'show-option-summary'),\n manageFavourite: getDataSelector('action', 'manage-module-favourite'),\n },\n addChooser: getDataSelector('action', 'add-chooser-option'),\n closeOption: getDataSelector('action', 'close-chooser-option-summary'),\n hide: getDataSelector('action', 'hide'),\n search: getDataSelector('action', 'search'),\n clearSearch: getDataSelector('action', 'clearsearch'),\n },\n render: {\n favourites: getDataSelector('render', 'favourites-area'),\n },\n elements: {\n section: '.section',\n sectionmodchooser: 'button.section-modchooser-link',\n sitemenu: '.block_site_main_menu',\n sitetopic: 'div.sitetopic',\n tab: 'a[data-toggle=\"tab\"]',\n activetab: 'a[data-toggle=\"tab\"][aria-selected=\"true\"]',\n visibletabs: 'a[data-toggle=\"tab\"]:not(.d-none)',\n searchicon: '.input-group-append .search-icon',\n clearsearch: '.input-group-append .clear'\n },\n};\n"],"file":"selectors.min.js"} \ No newline at end of file diff --git a/course/amd/src/activitychooser.js b/course/amd/src/activitychooser.js index a66560e6177..0aa10d505a0 100644 --- a/course/amd/src/activitychooser.js +++ b/course/amd/src/activitychooser.js @@ -31,16 +31,28 @@ import * as ModalFactory from 'core/modal_factory'; import {get_string as getString} from 'core/str'; import Pending from 'core/pending'; +// Set up some JS module wide constants that can be added to in the future. + +// Tab config options. +const ALLACTIVITIESRESOURCES = 0; +const ONLYALL = 1; +const ACTIVITIESRESOURCES = 2; + +// Module types. +const ACTIVITY = 0; +const RESOURCE = 1; + /** * Set up the activity chooser. * * @method init * @param {Number} courseId Course ID to use later on in fetchModules() + * @param {Object} chooserConfig Any PHP config settings that we may need to reference */ -export const init = courseId => { +export const init = (courseId, chooserConfig) => { const pendingPromise = new Pending(); - registerListenerEvents(courseId); + registerListenerEvents(courseId, chooserConfig); pendingPromise.resolve(); }; @@ -50,8 +62,9 @@ export const init = courseId => { * * @method registerListenerEvents * @param {Number} courseId + * @param {Object} chooserConfig Any PHP config settings that we may need to reference */ -const registerListenerEvents = (courseId) => { +const registerListenerEvents = (courseId, chooserConfig) => { const events = [ 'click', CustomEvents.events.activate, @@ -108,7 +121,7 @@ const registerListenerEvents = (courseId) => { bodyPromiseResolver(await Templates.render( 'core_course/activitychooser', - templateDataBuilder(builtModuleData) + templateDataBuilder(builtModuleData, chooserConfig) )); } }); @@ -138,28 +151,57 @@ const sectionIdMapper = (webServiceData, id) => { * * @method templateDataBuilder * @param {Array} data our modules to manipulate into a Templatable object + * @param {Object} chooserConfig Any PHP config settings that we may need to reference * @return {Object} Our built object ready to render out */ -const templateDataBuilder = (data) => { +const templateDataBuilder = (data, chooserConfig) => { + // Setup of various bits and pieces we need to mutate before throwing it to the wolves. + let activities = []; + let resources = []; + let showAll = true; + let showActivities = false; + let showResources = false; + + // Tab mode can be the following [All, Resources & Activities, All & Activities & Resources]. + const tabMode = parseInt(chooserConfig.tabmode); + // Filter the incoming data to find favourite & recommended modules. const favourites = data.filter(mod => mod.favourite === true); const recommended = data.filter(mod => mod.recommended === true); - // Given the results of the above filters lets figure out what tab to set active. + // Both of these modes need Activity & Resource tabs. + if ((tabMode === ALLACTIVITIESRESOURCES || tabMode === ACTIVITIESRESOURCES) && tabMode !== ONLYALL) { + // Filter the incoming data to find activities then resources. + activities = data.filter(mod => mod.archetype === ACTIVITY); + resources = data.filter(mod => mod.archetype === RESOURCE); + showActivities = true; + showResources = true; + // We want all of the previous information but no 'All' tab. + if (tabMode === ACTIVITIESRESOURCES) { + showAll = false; + } + } + + // Given the results of the above filters lets figure out what tab to set active. // We have some favourites. const favouritesFirst = !!favourites.length; - // Check if we have no favourites but have some recommended. - const recommendedFirst = !!(recommended.length && favouritesFirst === false); + // We are in tabMode 2 without any favourites. + const activitiesFirst = showAll === false && favouritesFirst === false; // We have nothing fallback to show all modules. - const fallback = favouritesFirst === false && recommendedFirst === false; + const fallback = showAll === true && favouritesFirst === false; return { 'default': data, + showAll: showAll, + activities: activities, + showActivities: showActivities, + activitiesFirst: activitiesFirst, + resources: resources, + showResources: showResources, favourites: favourites, recommended: recommended, favouritesFirst: favouritesFirst, - recommendedFirst: recommendedFirst, fallback: fallback, }; }; @@ -204,22 +246,22 @@ const nullFavouriteDomManager = (favouriteTabNav, modalBody) => { favouriteTabNav.setAttribute('aria-selected', 'false'); const favouriteTab = modalBody.querySelector(selectors.regions.favouriteTab); favouriteTab.classList.remove('active'); - const recommendedTabNav = modalBody.querySelector(selectors.regions.recommendedTabNav); const defaultTabNav = modalBody.querySelector(selectors.regions.defaultTabNav); - if (recommendedTabNav.classList.contains('d-none') === false) { - recommendedTabNav.classList.add('active'); - recommendedTabNav.setAttribute('aria-selected', 'true'); - recommendedTabNav.tabIndex = 0; - recommendedTabNav.focus(); - const recommendedTab = modalBody.querySelector(selectors.regions.recommendedTab); - recommendedTab.classList.add('active'); - } else { + const activitiesTabNav = modalBody.querySelector(selectors.regions.activityTabNav); + if (defaultTabNav.classList.contains('d-none') === false) { defaultTabNav.classList.add('active'); defaultTabNav.setAttribute('aria-selected', 'true'); defaultTabNav.tabIndex = 0; defaultTabNav.focus(); const defaultTab = modalBody.querySelector(selectors.regions.defaultTab); defaultTab.classList.add('active'); + } else { + activitiesTabNav.classList.add('active'); + activitiesTabNav.setAttribute('aria-selected', 'true'); + activitiesTabNav.tabIndex = 0; + activitiesTabNav.focus(); + const activitiesTab = modalBody.querySelector(selectors.regions.activityTab); + activitiesTab.classList.add('active'); } } diff --git a/course/amd/src/local/activitychooser/dialogue.js b/course/amd/src/local/activitychooser/dialogue.js index 42a19b32f2f..3cbc5e86583 100644 --- a/course/amd/src/local/activitychooser/dialogue.js +++ b/course/amd/src/local/activitychooser/dialogue.js @@ -194,9 +194,11 @@ const registerListenerEvents = (modal, mappedModules, partialFavourite) => { const activeSectionId = body.querySelector(selectors.elements.activetab).getAttribute("href"); const sectionChooserOptions = body.querySelector(selectors.regions.getSectionChooserOptions(activeSectionId)); const firstChooserOption = sectionChooserOptions.querySelector(selectors.regions.chooserOption.container); + toggleFocusableChooserOption(firstChooserOption, true); initTabsKeyboardNavigation(body); initChooserOptionsKeyboardNavigation(body, mappedModules, sectionChooserOptions); + return body; }) .catch(); @@ -214,7 +216,9 @@ const initTabsKeyboardNavigation = (body) => { const favTabNav = body.querySelector(selectors.regions.favouriteTabNav); const recommendedTabNav = body.querySelector(selectors.regions.recommendedTabNav); const defaultTabNav = body.querySelector(selectors.regions.defaultTabNav); - const tabNavArray = [favTabNav, recommendedTabNav, defaultTabNav]; + const activityTabNav = body.querySelector(selectors.regions.activityTabNav); + const resourceTabNav = body.querySelector(selectors.regions.resourceTabNav); + const tabNavArray = [favTabNav, recommendedTabNav, defaultTabNav, activityTabNav, resourceTabNav]; tabNavArray.forEach((element) => { return element.addEventListener('keydown', (e) => { // The first visible navigation tab link. @@ -556,6 +560,5 @@ export const displayChooser = (modalPromise, sectionModules, partialFavourite) = }); return modal; - }) - .catch(); + }).catch(); }; diff --git a/course/amd/src/local/activitychooser/selectors.js b/course/amd/src/local/activitychooser/selectors.js index 3a15f75a2af..6b54bbcde26 100644 --- a/course/amd/src/local/activitychooser/selectors.js +++ b/course/amd/src/local/activitychooser/selectors.js @@ -54,9 +54,13 @@ export default { favouriteTabNav: getDataSelector('region', 'favourite-tab-nav'), recommendedTabNav: getDataSelector('region', 'recommended-tab-nav'), defaultTabNav: getDataSelector('region', 'default-tab-nav'), + activityTabNav: getDataSelector('region', 'activity-tab-nav'), + resourceTabNav: getDataSelector('region', 'resources-tab-nav'), favouriteTab: getDataSelector('region', 'favourites'), recommendedTab: getDataSelector('region', 'recommended'), defaultTab: getDataSelector('region', 'default'), + activityTab: getDataSelector('region', 'activity'), + resourceTab: getDataSelector('region', 'resources'), getModuleSelector: modname => `[role="menuitem"][data-modname="${modname}"]`, searchResults: getDataSelector('region', 'search-results-container'), searchResultItems: getDataSelector('region', 'search-result-items-container'), diff --git a/course/renderer.php b/course/renderer.php index 8d58bc8998c..af5d2890b8c 100644 --- a/course/renderer.php +++ b/course/renderer.php @@ -158,7 +158,11 @@ class core_course_renderer extends plugin_renderer_base { return ''; } - $this->page->requires->js_call_amd('core_course/activitychooser', 'init', [$courseid]); + // Build an object of config settings that we can then hook into in the Activity Chooser. + $chooserconfig = (object) [ + 'tabmode' => get_config('core', 'activitychoosertabmode'), + ]; + $this->page->requires->js_call_amd('core_course/activitychooser', 'init', [$courseid, $chooserconfig]); return ''; } diff --git a/course/templates/activitychooser.mustache b/course/templates/activitychooser.mustache index 45d9a251b66..fe5fd22359f 100644 --- a/course/templates/activitychooser.mustache +++ b/course/templates/activitychooser.mustache @@ -55,20 +55,7 @@ > {{#str}} favourites, core {{/str}} - - {{#str}} recommended, core {{/str}} - - + {{#str}} all, core {{/str}} + + {{#str}} activities, core {{/str}} + + {{#str}} resources, core {{/str}} + + + {{#str}} recommended, core {{/str}} +
@@ -88,20 +114,34 @@ {{>core_course/local/activitychooser/favourites}}
- -
+
+
+ +
+
+ +
+
diff --git a/course/tests/behat/activity_chooser.feature b/course/tests/behat/activity_chooser.feature index 98ef06a6432..578e19c8aaa 100644 --- a/course/tests/behat/activity_chooser.feature +++ b/course/tests/behat/activity_chooser.feature @@ -58,7 +58,7 @@ Feature: Display and choose from the available activities in course When I log out And I log in as "admin" And I am on site homepage - And I navigate to "Courses > Recommended activities" in site administration + And I navigate to "Courses > Activity chooser > Recommended activities" in site administration And I click on ".activity-recommend-checkbox" "css_element" in the "Book" "table_row" # Setup done, lets check it works with a teacher. And I log out @@ -148,3 +148,40 @@ Feature: Display and choose from the available activities in course Then I should not see "Search query" And ".searchresultscontainer" "css_element" should not exist And ".optionscontainer" "css_element" should exist + + Scenario: Teacher gets the base case for the Activity Chooser tab mode + Given I click on "Add an activity" "button" in the "Topic 1" "section" + And I should see "Activities" in the "Add an activity" "dialogue" + When I click on "Activities" "link" in the "Add an activity" "dialogue" + Then I should not see "Book" in the "activity" "core_course > Activity chooser tab" + And I click on "Resources" "link" in the "Add an activity" "dialogue" + And I should not see "Assignment" in the "resources" "core_course > Activity chooser tab" + + Scenario: Teacher gets the simple case for the Activity Chooser tab mode + Given I log out + And I log in as "admin" + And I am on site homepage + When I navigate to "Courses > Activity chooser > Activity chooser settings" in site administration + And I select "Starred, All, Recommended" from the "Activity chooser tabs" singleselect + And I press "Save changes" + And I log out + And I log in as "teacher" + And I am on "Course" course homepage with editing mode on + And I click on "Add an activity" "button" in the "Topic 1" "section" + Then I should not see "Activities" in the "Add an activity" "dialogue" + And I should not see "Resources" in the "Add an activity" "dialogue" + + Scenario: Teacher gets the final case for the Activity Chooser tab mode + Given I log out + And I log in as "admin" + And I am on site homepage + When I navigate to "Courses > Activity chooser > Activity chooser settings" in site administration + And I select "Starred, Activities, Resources, Recommended" from the "Activity chooser tabs" singleselect + And I press "Save changes" + And I log out + And I log in as "teacher" + And I am on "Course" course homepage with editing mode on + And I click on "Add an activity" "button" in the "Topic 1" "section" + Then I should not see "All" in the "Add an activity" "dialogue" + And I should see "Activities" in the "Add an activity" "dialogue" + And I should see "Resources" in the "Add an activity" "dialogue" diff --git a/course/tests/behat/recommend_activities.feature b/course/tests/behat/recommend_activities.feature index 2b2e68d0395..818d9358d4e 100644 --- a/course/tests/behat/recommend_activities.feature +++ b/course/tests/behat/recommend_activities.feature @@ -5,24 +5,24 @@ Feature: Recommending activities Scenario: As an admin I can recommend activities from an admin setting page. Given I log in as "admin" And I am on site homepage - And I navigate to "Courses > Recommended activities" in site administration + And I navigate to "Courses > Activity chooser > Recommended activities" in site administration And I click on ".activity-recommend-checkbox" "css" in the "Assignment" "table_row" And I navigate to "Courses > Add a new course" in site administration - When I navigate to "Courses > Recommended activities" in site administration + When I navigate to "Courses > Activity chooser > Recommended activities" in site administration Then "input[aria-label=\"Recommend activity: Assignment\"][checked=checked]" "css_element" should exist And "input[aria-label=\"Recommend activity: Book\"]:not([checked=checked])" "css_element" should exist Scenario: As an admin I can remove recommend activities from an admin setting page. Given I log in as "admin" And I am on site homepage - And I navigate to "Courses > Recommended activities" in site administration + And I navigate to "Courses > Activity chooser > Recommended activities" in site administration And I click on ".activity-recommend-checkbox" "css" in the "Assignment" "table_row" And I navigate to "Courses > Add a new course" in site administration - And I navigate to "Courses > Recommended activities" in site administration + And I navigate to "Courses > Activity chooser > Recommended activities" in site administration And "input[aria-label=\"Recommend activity: Assignment\"][checked=checked]" "css_element" should exist And "input[aria-label=\"Recommend activity: Book\"]:not([checked=checked])" "css_element" should exist And I click on ".activity-recommend-checkbox" "css" in the "Assignment" "table_row" And I navigate to "Courses > Add a new course" in site administration - When I navigate to "Courses > Recommended activities" in site administration + When I navigate to "Courses > Activity chooser > Recommended activities" in site administration Then "input[aria-label=\"Recommend activity: Assignment\"]:not([checked=checked])" "css_element" should exist And "input[aria-label=\"Recommend activity: Book\"]:not([checked=checked])" "css_element" should exist diff --git a/course/tests/behat/search_recommended_activities.feature b/course/tests/behat/search_recommended_activities.feature index 3575aaf6fe7..202839dd1c1 100644 --- a/course/tests/behat/search_recommended_activities.feature +++ b/course/tests/behat/search_recommended_activities.feature @@ -5,7 +5,7 @@ Feature: Search recommended activities Scenario: Search results are returned if the search query matches any activity names Given I log in as "admin" And I am on site homepage - And I navigate to "Courses > Recommended activities" in site administration + And I navigate to "Courses > Activity chooser > Recommended activities" in site administration When I set the field "search" to "assign" And I click on "Submit search" "button" Then I should see "Search results: 1" @@ -15,7 +15,7 @@ Feature: Search recommended activities Scenario: Search results are not returned if the search query does not match with any activity names Given I log in as "admin" And I am on site homepage - And I navigate to "Courses > Recommended activities" in site administration + And I navigate to "Courses > Activity chooser > Recommended activities" in site administration When I set the field "search" to "random query" And I click on "Submit search" "button" Then I should see "Search results: 0" diff --git a/lang/en/course.php b/lang/en/course.php index c6a8d9a9e0f..0a19d434cff 100644 --- a/lang/en/course.php +++ b/lang/en/course.php @@ -22,7 +22,14 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +$string['activitychoosercategory'] = 'Activity chooser'; $string['activitychooserrecommendations'] = 'Recommended activities'; +$string['activitychoosersettings'] = 'Activity chooser settings'; +$string['activitychoosertabmode'] = 'Activity chooser tabs'; +$string['activitychoosertabmode_desc'] = "The activity chooser enables a teacher to easily select activities and resources to add to their course. This setting determines which tabs should be displayed in it. Note that the starred tab is only displayed for a user if they have starred one or more activities and the recommended tab is only displayed if a site administrator has specified some recommended activities."; +$string['activitychoosertabmodeone'] = 'Starred, All, Activities, Resources, Recommended'; +$string['activitychoosertabmodetwo'] = 'Starred, All, Recommended'; +$string['activitychoosertabmodethree'] = 'Starred, Activities, Resources, Recommended'; $string['aria:coursecategory'] = 'Course category'; $string['aria:courseimage'] = 'Course image'; $string['aria:courseshortname'] = 'Course short name';