From b9f2e3d99bee65be7af2c0a5bbd30446df508956 Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Wed, 15 Jul 2020 16:37:13 +0800 Subject: [PATCH] MDL-69079 course: Handle fetch module data failures in activity chooser --- course/amd/build/activitychooser.min.js | 2 +- course/amd/build/activitychooser.min.js.map | 2 +- course/amd/src/activitychooser.js | 13 ++++++- .../local/activitychooser/error.mustache | 39 +++++++++++++++++++ 4 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 course/templates/local/activitychooser/error.mustache diff --git a/course/amd/build/activitychooser.min.js b/course/amd/build/activitychooser.min.js index b49f3dc831d..b8eeb838578 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=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}}(),j=function(){var b=null;return function(d){if(!b){b=new Promise(function(b){b(c.fetchFooterData(a,d))})}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,k,l,m,n,o,p,q;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:if(!c.target.closest(d.default.elements.sectionmodchooser)){a.next=19;break}h=c.target.closest(d.default.elements.section);k=c.target.closest(d.default.elements.sectionmodchooser);if(null!==h&&h.hasAttribute("data-sectionid")){e=h}else{e=k}m=new Promise(function(a){l=a});a.next=7;return j(e.dataset.sectionid);case 7:n=a.sent;o=t(m,n);a.next=11;return i();case 11:p=a.sent;q=r(p,e.dataset.sectionid,e.dataset.sectionreturnid);b.displayChooser(o,q,v(p,e.dataset.sectionid),n);a.t0=l;a.next=17;return f.render("core_course/activitychooser",s(q,g));case 17:a.t1=a.sent;(0,a.t0)(a.t1);case 19:case"end":return a.stop();}}},a)}));return function(){return a.apply(this,arguments)}}())})},r=function(a,b,c){var d=JSON.parse(JSON.stringify(a));d.content_items.forEach(function(a){a.link+="§ion="+b+"&sr="+(null!==c&&void 0!==c?c:0)});return d.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,b){return g.create({type:g.types.DEFAULT,title:(0,h.get_string)("addresourceoractivity"),body:a,footer:b.customfootertemplate,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)}}()}}); +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}}(),j=function(){var b=null;return function(d){if(!b){b=new Promise(function(b){b(c.fetchFooterData(a,d))})}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,k,l,m,o,p,q,u;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:if(!c.target.closest(d.default.elements.sectionmodchooser)){a.next=21;break}h=c.target.closest(d.default.elements.section);k=c.target.closest(d.default.elements.sectionmodchooser);if(null!==h&&h.hasAttribute("data-sectionid")){e=h}else{e=k}m=new Promise(function(a){l=a});a.next=7;return j(e.dataset.sectionid);case 7:o=a.sent;p=t(m,o);a.next=11;return i().catch(function(){var a=n(regeneratorRuntime.mark(function a(b){var c;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:c={errormessage:b.message};a.t0=l;a.next=4;return f.render("core_course/local/activitychooser/error",c);case 4:a.t1=a.sent;(0,a.t0)(a.t1);case 6:case"end":return a.stop();}}},a)}));return function(){return a.apply(this,arguments)}}());case 11:q=a.sent;if(q){a.next=14;break}return a.abrupt("return");case 14:u=r(q,e.dataset.sectionid,e.dataset.sectionreturnid);b.displayChooser(p,u,v(q,e.dataset.sectionid),o);a.t0=l;a.next=19;return f.render("core_course/activitychooser",s(u,g));case 19:a.t1=a.sent;(0,a.t0)(a.t1);case 21:case"end":return a.stop();}}},a)}));return function(){return a.apply(this,arguments)}}())})},r=function(a,b,c){var d=JSON.parse(JSON.stringify(a));d.content_items.forEach(function(a){a.link+="§ion="+b+"&sr="+(null!==c&&void 0!==c?c:0)});return d.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,b){return g.create({type:g.types.DEFAULT,title:(0,h.get_string)("addresourceoractivity"),body:a,footer:b.customfootertemplate,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 a409f76db86..77409060d24 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":["ACTIVITIESRESOURCES","init","courseId","chooserConfig","pendingPromise","Pending","registerListenerEvents","resolve","events","CustomEvents","activate","keyboardActivate","fetchModuleData","innerPromise","Promise","Repository","activityModules","fetchFooterData","footerInnerPromise","sectionId","define","document","forEach","event","addEventListener","e","target","closest","selectors","elements","sectionmodchooser","sectionDiv","section","button","hasAttribute","caller","bodyPromise","bodyPromiseResolver","dataset","sectionid","footerData","sectionModal","buildModal","data","builtModuleData","sectionIdMapper","sectionreturnid","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","footer","ModalFactory","create","type","types","DEFAULT","title","body","customfootertemplate","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","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 const fetchFooterData = (() => {\n let footerInnerPromise = null;\n\n return (sectionId) => {\n if (!footerInnerPromise) {\n footerInnerPromise = new Promise((resolve) => {\n resolve(Repository.fetchFooterData(courseId, sectionId));\n });\n }\n\n return footerInnerPromise;\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 let caller;\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\n // If we don't have a section ID use the fallback ID.\n // We always want the sectionDiv caller first as it keeps track of section ID's after DnD changes.\n // The button attribute is always just a fallback for us as the section div is not always available.\n // A YUI change could be done maybe to only update the button attribute but we are going for minimal change here.\n if (sectionDiv !== null && sectionDiv.hasAttribute('data-sectionid')) {\n // We check for attributes just in case of outdated contrib course formats.\n caller = sectionDiv;\n } else {\n caller = button;\n }\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 footerData = await fetchFooterData(caller.dataset.sectionid);\n const sectionModal = buildModal(bodyPromise, footerData);\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, caller.dataset.sectionreturnid);\n\n ChooserDialogue.displayChooser(\n sectionModal,\n builtModuleData,\n partiallyAppliedFavouriteManager(data, caller.dataset.sectionid),\n footerData,\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 * @param {Number|null} sectionreturnid The ID of the section return we need to append to the links\n * @return {Array} [modules] with URL's built\n */\nconst sectionIdMapper = (webServiceData, id, sectionreturnid) => {\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 + '&sr=' + (sectionreturnid ?? 0);\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 * @param {String|Boolean} footer Either a footer to add or nothing\n * @return {Object} The modal ready to display immediately and render body in later.\n */\nconst buildModal = (bodyPromise, footer) => {\n return ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: getString('addresourceoractivity'),\n body: bodyPromise,\n footer: footer.customfootertemplate,\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 +{"version":3,"sources":["../src/activitychooser.js"],"names":["ACTIVITIESRESOURCES","init","courseId","chooserConfig","pendingPromise","Pending","registerListenerEvents","resolve","events","CustomEvents","activate","keyboardActivate","fetchModuleData","innerPromise","Promise","Repository","activityModules","fetchFooterData","footerInnerPromise","sectionId","define","document","forEach","event","addEventListener","e","target","closest","selectors","elements","sectionmodchooser","sectionDiv","section","button","hasAttribute","caller","bodyPromise","bodyPromiseResolver","dataset","sectionid","footerData","sectionModal","buildModal","catch","errorTemplateData","message","Templates","render","data","builtModuleData","sectionIdMapper","sectionreturnid","ChooserDialogue","displayChooser","partiallyAppliedFavouriteManager","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","footer","ModalFactory","create","type","types","DEFAULT","title","body","customfootertemplate","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","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":"wqBAwBA,OACA,OACA,OACA,OACA,OACA,OAEA,O,25BAOMA,CAAAA,CAAmB,CAAG,C,CAafC,CAAI,CAAG,SAACC,CAAD,CAAWC,CAAX,CAA6B,CAC7C,GAAMC,CAAAA,CAAc,CAAG,GAAIC,UAA3B,CAEAC,CAAsB,CAACJ,CAAD,CAAWC,CAAX,CAAtB,CAEAC,CAAc,CAACG,OAAf,EACH,C,aASKD,CAAAA,CAAsB,CAAG,SAACJ,CAAD,CAAWC,CAAX,CAA6B,IAClDK,CAAAA,CAAM,CAAG,CACX,OADW,CAEXC,UAAaD,MAAb,CAAoBE,QAFT,CAGXD,UAAaD,MAAb,CAAoBG,gBAHT,CADyC,CAOlDC,CAAe,CAAI,UAAM,CAC3B,GAAIC,CAAAA,CAAY,CAAG,IAAnB,CAEA,MAAO,WAAM,CACT,GAAI,CAACA,CAAL,CAAmB,CACfA,CAAY,CAAG,GAAIC,CAAAA,OAAJ,CAAY,SAACP,CAAD,CAAa,CACpCA,CAAO,CAACQ,CAAU,CAACC,eAAX,CAA2Bd,CAA3B,CAAD,CACV,CAFc,CAGlB,CAED,MAAOW,CAAAA,CACV,CACJ,CAZuB,EAPgC,CAqBlDI,CAAe,CAAI,UAAM,CAC3B,GAAIC,CAAAA,CAAkB,CAAG,IAAzB,CAEA,MAAO,UAACC,CAAD,CAAe,CAClB,GAAI,CAACD,CAAL,CAAyB,CACrBA,CAAkB,CAAG,GAAIJ,CAAAA,OAAJ,CAAY,SAACP,CAAD,CAAa,CAC1CA,CAAO,CAACQ,CAAU,CAACE,eAAX,CAA2Bf,CAA3B,CAAqCiB,CAArC,CAAD,CACV,CAFoB,CAGxB,CAED,MAAOD,CAAAA,CACV,CACJ,CAZuB,EArBgC,CAmCxDT,UAAaW,MAAb,CAAoBC,QAApB,CAA8Bb,CAA9B,EAGAA,CAAM,CAACc,OAAP,CAAe,SAACC,CAAD,CAAW,CACtBF,QAAQ,CAACG,gBAAT,CAA0BD,CAA1B,4CAAiC,WAAME,CAAN,6GACzBA,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiBC,UAAUC,QAAV,CAAmBC,iBAApC,CADyB,kBAKnBC,CALmB,CAKNN,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiBC,UAAUC,QAAV,CAAmBG,OAApC,CALM,CAOnBC,CAPmB,CAOVR,CAAC,CAACC,MAAF,CAASC,OAAT,CAAiBC,UAAUC,QAAV,CAAmBC,iBAApC,CAPU,CAazB,GAAmB,IAAf,GAAAC,CAAU,EAAaA,CAAU,CAACG,YAAX,CAAwB,gBAAxB,CAA3B,CAAsE,CAElEC,CAAM,CAAGJ,CACZ,CAHD,IAGO,CACHI,CAAM,CAAGF,CACZ,CAIKG,CAtBmB,CAsBL,GAAItB,CAAAA,OAAJ,CAAY,SAAAP,CAAO,CAAI,CACvC8B,CAAmB,CAAG9B,CACzB,CAFmB,CAtBK,gBA0BAU,CAAAA,CAAe,CAACkB,CAAM,CAACG,OAAP,CAAeC,SAAhB,CA1Bf,QA0BnBC,CA1BmB,QA2BnBC,CA3BmB,CA2BJC,CAAU,CAACN,CAAD,CAAcI,CAAd,CA3BN,iBA+BN5B,CAAAA,CAAe,GAAG+B,KAAlB,4CAAwB,WAAMlB,CAAN,yFACjCmB,CADiC,CACb,CACtB,aAAgBnB,CAAC,CAACoB,OADI,CADa,MAIvCR,CAJuC,gBAIbS,CAAAA,CAAS,CAACC,MAAV,CAAiB,yCAAjB,CAA4DH,CAA5D,CAJa,2EAAxB,wDA/BM,SA+BnBI,CA/BmB,WAuCpBA,CAvCoB,oDA4CnBC,CA5CmB,CA4CDC,CAAe,CAACF,CAAD,CAAOb,CAAM,CAACG,OAAP,CAAeC,SAAtB,CAAiCJ,CAAM,CAACG,OAAP,CAAea,eAAhD,CA5Cd,CA8CzBC,CAAe,CAACC,cAAhB,CACIZ,CADJ,CAEIQ,CAFJ,CAGIK,CAAgC,CAACN,CAAD,CAAOb,CAAM,CAACG,OAAP,CAAeC,SAAtB,CAHpC,CAIIC,CAJJ,EA9CyB,KAqDzBH,CArDyB,iBAqDCS,CAAAA,CAAS,CAACC,MAAV,CACtB,6BADsB,CAEtBQ,CAAmB,CAACN,CAAD,CAAkB9C,CAAlB,CAFG,CArDD,6EAAjC,wDA2DH,CA5DD,CA6DH,C,CAYK+C,CAAe,CAAG,SAACM,CAAD,CAAiBC,CAAjB,CAAqBN,CAArB,CAAyC,CAE7D,GAAMO,CAAAA,CAAO,CAAGC,IAAI,CAACC,KAAL,CAAWD,IAAI,CAACE,SAAL,CAAeL,CAAf,CAAX,CAAhB,CACAE,CAAO,CAACI,aAAR,CAAsBxC,OAAtB,CAA8B,SAACyC,CAAD,CAAY,CACtCA,CAAM,CAACC,IAAP,EAAe,YAAcP,CAAd,CAAmB,MAAnB,SAA6BN,CAA7B,WAA6BA,CAA7B,CAA6BA,CAA7B,CAAgD,CAAhD,CAClB,CAFD,EAGA,MAAOO,CAAAA,CAAO,CAACI,aAClB,C,CAUKP,CAAmB,CAAG,SAACP,CAAD,CAAO7C,CAAP,CAAyB,IAE7C8D,CAAAA,CAAU,CAAG,EAFgC,CAG7CC,CAAS,CAAG,EAHiC,CAI7CC,CAAO,GAJsC,CAK7CC,CAAc,GAL+B,CAM7CC,CAAa,GANgC,CAS3CC,CAAO,CAAGC,QAAQ,CAACpE,CAAa,CAACqE,OAAf,CATyB,CAY3CC,CAAU,CAAGzB,CAAI,CAAC0B,MAAL,CAAY,SAAAC,CAAG,QAAI,KAAAA,CAAG,CAACC,SAAR,CAAf,CAZ8B,CAa3CC,CAAW,CAAG7B,CAAI,CAAC0B,MAAL,CAAY,SAAAC,CAAG,QAAI,KAAAA,CAAG,CAACE,WAAR,CAAf,CAb6B,CAgBjD,GAAI,CAACP,CAAO,IAAP,EAAsCA,CAAO,GAAKtE,CAAnD,GAA2EsE,CAAO,GA7K1E,CA6KZ,CAAoG,CAEhGL,CAAU,CAAGjB,CAAI,CAAC0B,MAAL,CAAY,SAAAC,CAAG,QAAIA,CAAAA,CAAG,CAACG,SAAJ,GA3KvB,CA2KmB,CAAf,CAAb,CACAZ,CAAS,CAAGlB,CAAI,CAAC0B,MAAL,CAAY,SAAAC,CAAG,QAAIA,CAAAA,CAAG,CAACG,SAAJ,GA3KtB,CA2KkB,CAAf,CAAZ,CACAV,CAAc,GAAd,CACAC,CAAa,GAAb,CAGA,GAAIC,CAAO,GAAKtE,CAAhB,CAAqC,CACjCmE,CAAO,GACV,CACJ,CA3BgD,GA+B3CY,CAAAA,CAAe,CAAG,CAAC,CAACN,CAAU,CAACO,MA/BY,CAiC3CC,CAAe,CAAG,KAAAd,CAAO,EAAc,KAAAY,CAjCI,CAmC3CG,CAAQ,CAAG,KAAAf,CAAO,EAAa,KAAAY,CAnCY,CAqCjD,MAAO,CACH,QAAW/B,CADR,CAEHmB,OAAO,CAAEA,CAFN,CAGHF,UAAU,CAAEA,CAHT,CAIHG,cAAc,CAAEA,CAJb,CAKHa,eAAe,CAAEA,CALd,CAMHf,SAAS,CAAEA,CANR,CAOHG,aAAa,CAAEA,CAPZ,CAQHI,UAAU,CAAEA,CART,CASHI,WAAW,CAAEA,CATV,CAUHE,eAAe,CAAEA,CAVd,CAWHG,QAAQ,CAAEA,CAXP,CAaV,C,CAUKxC,CAAU,CAAG,SAACN,CAAD,CAAc+C,CAAd,CAAyB,CACxC,MAAOC,CAAAA,CAAY,CAACC,MAAb,CAAoB,CACvBC,IAAI,CAAEF,CAAY,CAACG,KAAb,CAAmBC,OADF,CAEvBC,KAAK,CAAE,iBAAU,uBAAV,CAFgB,CAGvBC,IAAI,CAAEtD,CAHiB,CAIvB+C,MAAM,CAAEA,CAAM,CAACQ,oBAJQ,CAKvBC,KAAK,GALkB,CAMvBC,eAAe,CAAE,CACbC,OAAO,CAAE,YADI,CANM,CAApB,EAUNC,IAVM,CAUD,SAAAC,CAAK,CAAI,CACXA,CAAK,CAACC,IAAN,GACA,MAAOD,CAAAA,CACV,CAbM,CAcV,C,CAUKE,CAAuB,CAAG,SAACC,CAAD,CAAkBC,CAAlB,CAAgC,CAC5DD,CAAe,CAACE,QAAhB,CAA2B,CAAC,CAA5B,CACAF,CAAe,CAACG,SAAhB,CAA0BC,GAA1B,CAA8B,QAA9B,EAEA,GAAIJ,CAAe,CAACG,SAAhB,CAA0BE,QAA1B,CAAmC,QAAnC,CAAJ,CAAkD,CAC9CL,CAAe,CAACG,SAAhB,CAA0BG,MAA1B,CAAiC,QAAjC,EACAN,CAAe,CAACO,YAAhB,CAA6B,eAA7B,CAA8C,OAA9C,EACA,GAAMC,CAAAA,CAAY,CAAGP,CAAS,CAACQ,aAAV,CAAwBhF,UAAUiF,OAAV,CAAkBF,YAA1C,CAArB,CACAA,CAAY,CAACL,SAAb,CAAuBG,MAAvB,CAA8B,QAA9B,EAJ8C,GAKxCK,CAAAA,CAAa,CAAGV,CAAS,CAACQ,aAAV,CAAwBhF,UAAUiF,OAAV,CAAkBC,aAA1C,CALwB,CAMxCC,CAAgB,CAAGX,CAAS,CAACQ,aAAV,CAAwBhF,UAAUiF,OAAV,CAAkBG,cAA1C,CANqB,CAO9C,GAAI,KAAAF,CAAa,CAACR,SAAd,CAAwBE,QAAxB,CAAiC,QAAjC,CAAJ,CAA0D,CACtDM,CAAa,CAACR,SAAd,CAAwBC,GAAxB,CAA4B,QAA5B,EACAO,CAAa,CAACJ,YAAd,CAA2B,eAA3B,CAA4C,MAA5C,EACAI,CAAa,CAACT,QAAd,CAAyB,CAAzB,CACAS,CAAa,CAACG,KAAd,GACA,GAAMC,CAAAA,CAAU,CAAGd,CAAS,CAACQ,aAAV,CAAwBhF,UAAUiF,OAAV,CAAkBK,UAA1C,CAAnB,CACAA,CAAU,CAACZ,SAAX,CAAqBC,GAArB,CAAyB,QAAzB,CACH,CAPD,IAOO,CACHQ,CAAgB,CAACT,SAAjB,CAA2BC,GAA3B,CAA+B,QAA/B,EACAQ,CAAgB,CAACL,YAAjB,CAA8B,eAA9B,CAA+C,MAA/C,EACAK,CAAgB,CAACV,QAAjB,CAA4B,CAA5B,CACAU,CAAgB,CAACE,KAAjB,GACA,GAAME,CAAAA,CAAa,CAAGf,CAAS,CAACQ,aAAV,CAAwBhF,UAAUiF,OAAV,CAAkBO,WAA1C,CAAtB,CACAD,CAAa,CAACb,SAAd,CAAwBC,GAAxB,CAA4B,QAA5B,CACH,CAEJ,CACJ,C,CAWKjD,CAAgC,CAAG,SAAC+D,CAAD,CAAalG,CAAb,CAA2B,CAQhE,kDAAO,WAAMmG,CAAN,CAAgB1C,CAAhB,CAA2BwB,CAA3B,6GACGmB,CADH,CACmBnB,CAAS,CAACQ,aAAV,CAAwBhF,UAAUmB,MAAV,CAAiB0B,UAAzC,CADnB,CAIG+C,CAJH,CAIsBpB,CAAS,CAACqB,gBAAV,4BAA8CH,CAA9C,gBAA4D1F,UAAU8F,OAAV,CAAkBC,aAAlB,CAAgCC,eAA5F,EAJtB,CAKGzB,CALH,CAKqBC,CAAS,CAACQ,aAAV,CAAwBhF,UAAUiF,OAAV,CAAkBV,eAA1C,CALrB,CAMG0B,CANH,CAMYR,CAAU,CAACvD,aAAX,CAAyBgE,IAAzB,CAA8B,eAAEC,CAAAA,CAAF,GAAEA,IAAF,OAAYA,CAAAA,CAAI,GAAKT,CAArB,CAA9B,CANZ,CAOGU,CAPH,CAOc,EAPd,KAQCH,CARD,sBASKjD,CATL,kBAUKiD,CAAM,CAACjD,SAAP,IAGAoD,CAAQ,CAAClE,aAAT,CAAyBuD,CAAU,CAACvD,aAAX,CAAyBY,MAAzB,CAAgC,SAAAC,CAAG,QAAI,KAAAA,CAAG,CAACC,SAAR,CAAnC,CAAzB,CAEMqD,CAfX,CAewB/E,CAAe,CAAC8E,CAAD,CAAW7G,CAAX,CAfvC,iBAiB8B2B,CAAAA,CAAS,CAACoF,gBAAV,CAA2B,8CAA3B,CACrB,CAACzD,UAAU,CAAEwD,CAAb,CADqB,CAjB9B,kBAiBYE,CAjBZ,GAiBYA,IAjBZ,CAiBkBC,CAjBlB,GAiBkBA,EAjBlB,iBAoBWtF,CAAAA,CAAS,CAACuF,mBAAV,CAA8Bd,CAA9B,CAA6CY,CAA7C,CAAmDC,CAAnD,CApBX,SAsBKE,KAAK,CAACC,IAAN,CAAWf,CAAX,EAA6BlG,OAA7B,CAAqC,SAACkH,CAAD,CAAa,CAC9CA,CAAO,CAAClC,SAAR,CAAkBG,MAAlB,CAAyB,YAAzB,EACA+B,CAAO,CAAClC,SAAR,CAAkBC,GAAlB,CAAsB,cAAtB,EACAiC,CAAO,CAAClG,OAAR,CAAgBmG,UAAhB,CAA6B,MAA7B,CACAD,CAAO,CAAC9B,YAAR,CAAqB,cAArB,KACA8B,CAAO,CAACE,iBAAR,CAA0BpC,SAA1B,CAAoCG,MAApC,CAA2C,WAA3C,EACA+B,CAAO,CAACE,iBAAR,CAA0BpC,SAA1B,CAAoCC,GAApC,CAAwC,SAAxC,CACH,CAPD,EASAJ,CAAe,CAACG,SAAhB,CAA0BG,MAA1B,CAAiC,QAAjC,EA/BL,wBAiCKoB,CAAM,CAACjD,SAAP,IAEM+D,CAnCX,CAmC0BpB,CAAa,CAACX,aAAd,4BAA+CU,CAA/C,QAnC1B,CAqCKqB,CAAY,CAACC,UAAb,CAAwBC,WAAxB,CAAoCF,CAApC,EAEAL,KAAK,CAACC,IAAN,CAAWf,CAAX,EAA6BlG,OAA7B,CAAqC,SAACkH,CAAD,CAAa,CAC9CA,CAAO,CAAClC,SAAR,CAAkBC,GAAlB,CAAsB,YAAtB,EACAiC,CAAO,CAAClC,SAAR,CAAkBG,MAAlB,CAAyB,cAAzB,EACA+B,CAAO,CAAClG,OAAR,CAAgBmG,UAAhB,CAA6B,OAA7B,CACAD,CAAO,CAAC9B,YAAR,CAAqB,cAArB,KACA8B,CAAO,CAACE,iBAAR,CAA0BpC,SAA1B,CAAoCG,MAApC,CAA2C,SAA3C,EACA+B,CAAO,CAACE,iBAAR,CAA0BpC,SAA1B,CAAoCC,GAApC,CAAwC,WAAxC,CACH,CAPD,EAQMyB,CA/CX,CA+CsBX,CAAU,CAACvD,aAAX,CAAyBY,MAAzB,CAAgC,SAAAC,CAAG,QAAI,KAAAA,CAAG,CAACC,SAAR,CAAnC,CA/CtB,CAiDK,GAAwB,CAApB,GAAAoD,CAAQ,CAAChD,MAAb,CAA2B,CACvBkB,CAAuB,CAACC,CAAD,CAAkBC,CAAlB,CAC1B,CAnDN,yCAAP,uDAuDH,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 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 const fetchFooterData = (() => {\n let footerInnerPromise = null;\n\n return (sectionId) => {\n if (!footerInnerPromise) {\n footerInnerPromise = new Promise((resolve) => {\n resolve(Repository.fetchFooterData(courseId, sectionId));\n });\n }\n\n return footerInnerPromise;\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 let caller;\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\n // If we don't have a section ID use the fallback ID.\n // We always want the sectionDiv caller first as it keeps track of section ID's after DnD changes.\n // The button attribute is always just a fallback for us as the section div is not always available.\n // A YUI change could be done maybe to only update the button attribute but we are going for minimal change here.\n if (sectionDiv !== null && sectionDiv.hasAttribute('data-sectionid')) {\n // We check for attributes just in case of outdated contrib course formats.\n caller = sectionDiv;\n } else {\n caller = button;\n }\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 footerData = await fetchFooterData(caller.dataset.sectionid);\n const sectionModal = buildModal(bodyPromise, footerData);\n\n // Now we have a modal we should start fetching data.\n // If an error occurs while fetching the data, display the error within the modal.\n const data = await fetchModuleData().catch(async(e) => {\n const errorTemplateData = {\n 'errormessage': e.message\n };\n bodyPromiseResolver(await Templates.render('core_course/local/activitychooser/error', errorTemplateData));\n });\n\n // Early return if there is no module data.\n if (!data) {\n return;\n }\n\n // Apply the section id to all the module instance links.\n const builtModuleData = sectionIdMapper(data, caller.dataset.sectionid, caller.dataset.sectionreturnid);\n\n ChooserDialogue.displayChooser(\n sectionModal,\n builtModuleData,\n partiallyAppliedFavouriteManager(data, caller.dataset.sectionid),\n footerData,\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 * @param {Number|null} sectionreturnid The ID of the section return we need to append to the links\n * @return {Array} [modules] with URL's built\n */\nconst sectionIdMapper = (webServiceData, id, sectionreturnid) => {\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 + '&sr=' + (sectionreturnid ?? 0);\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 * @param {String|Boolean} footer Either a footer to add or nothing\n * @return {Object} The modal ready to display immediately and render body in later.\n */\nconst buildModal = (bodyPromise, footer) => {\n return ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n title: getString('addresourceoractivity'),\n body: bodyPromise,\n footer: footer.customfootertemplate,\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/src/activitychooser.js b/course/amd/src/activitychooser.js index 5de0576c21d..041a04f22c2 100644 --- a/course/amd/src/activitychooser.js +++ b/course/amd/src/activitychooser.js @@ -133,7 +133,18 @@ const registerListenerEvents = (courseId, chooserConfig) => { const sectionModal = buildModal(bodyPromise, footerData); // Now we have a modal we should start fetching data. - const data = await fetchModuleData(); + // If an error occurs while fetching the data, display the error within the modal. + const data = await fetchModuleData().catch(async(e) => { + const errorTemplateData = { + 'errormessage': e.message + }; + bodyPromiseResolver(await Templates.render('core_course/local/activitychooser/error', errorTemplateData)); + }); + + // Early return if there is no module data. + if (!data) { + return; + } // Apply the section id to all the module instance links. const builtModuleData = sectionIdMapper(data, caller.dataset.sectionid, caller.dataset.sectionreturnid); diff --git a/course/templates/local/activitychooser/error.mustache b/course/templates/local/activitychooser/error.mustache new file mode 100644 index 00000000000..98ac463c487 --- /dev/null +++ b/course/templates/local/activitychooser/error.mustache @@ -0,0 +1,39 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template core_course/local/activitychooser/error + + Chooser error template. + + Variables required for this template: + * errormessage - The error message + + Example context (json): + { + "errormessage": "Error" + } +}} +
+ +