AutorÃa | Ultima modificación | Ver Log |
{"version":3,"file":"dialogue.min.js","sources":["../../../src/local/activitychooser/dialogue.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * A type of dialogue used as for choosing options.\n *\n * @module core_course/local/activitychooser/dialogue\n * @copyright 2019 Mihail Geshoski <mihail@moodle.com>\n * @license http://www.gn
u.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as ModalEvents from 'core/modal_events';\nimport selectors from 'core_course/local/activitychooser/selectors';\nimport * as Templates from 'core/templates';\nimport {end, arrowLeft, arrowRight, home, enter, space} from 'core/key_codes';\nimport {addIconToContainer} from 'core/loadingicon';\nimport * as Repository from 'core_course/local/activitychooser/repository';\nimport Notification from 'core/notification';\nimport {debounce} from 'core/utils';\nconst getPlugin = pluginName => import(pluginName);\n\n/**\n * Given an event from the main module 'page' navigate to it's help section via a carousel.\n *\n * @method showModuleHelp\n * @param {jQuery} carousel Our initialized carousel to manipulate\n * @param {Object} moduleData Data of the module to carousel to\n * @param {jQuery} modal We need to figure out if the current modal has a footer.\n */\nconst showModuleHelp = (carousel, moduleData, modal = null) => {\n // If we
have a real footer then we need to change temporarily.\n if (modal !== null && moduleData.showFooter === true) {\n modal.setFooter(Templates.render('core_course/local/activitychooser/footer_partial', moduleData));\n }\n const help = carousel.find(selectors.regions.help)[0];\n help.innerHTML = '';\n help.classList.add('m-auto');\n\n // Add a spinner.\n const spinnerPromise = addIconToContainer(help);\n\n // Used later...\n let transitionPromiseResolver = null;\n const transitionPromise = new Promise(resolve => {\n transitionPromiseResolver = resolve;\n });\n\n // Build up the html & js ready to place into the help section.\n const contentPromise = Templates.renderForPromise('core_course/local/activitychooser/help', moduleData);\n\n // Wait for the content to be ready, and for the transition to be complet.\n Promise.all([contentPromise, spinnerPromise, transitionPromise])\n .then(([{html, js}]) => Templates.replaceNodeContents(help, html, js))\
n .then(() => {\n help.querySelector(selectors.regions.chooserSummary.header).focus();\n return help;\n })\n .catch(Notification.exception);\n\n // Move to the next slide, and resolve the transition promise when it's done.\n carousel.one('slid.bs.carousel', () => {\n transitionPromiseResolver();\n });\n // Trigger the transition between 'pages'.\n carousel.carousel('next');\n};\n\n/**\n * Given a user wants to change the favourite state of a module we either add or remove the status.\n * We also propergate this change across our map of modals.\n *\n * @method manageFavouriteState\n * @param {HTMLElement} modalBody The DOM node of the modal to manipulate\n * @param {HTMLElement} caller\n * @param {Function} partialFavourite Partially applied function we need to manage favourite status\n */\nconst manageFavouriteState = async(modalBody, caller, partialFavourite) => {\n const isFavourite = caller.dataset.favourited;\n const id = caller.datas
et.id;\n const name = caller.dataset.name;\n const internal = caller.dataset.internal;\n // Switch on fave or not.\n if (isFavourite === 'true') {\n await Repository.unfavouriteModule(name, id);\n\n partialFavourite(internal, false, modalBody);\n } else {\n await Repository.favouriteModule(name, id);\n\n partialFavourite(internal, true, modalBody);\n }\n\n};\n\n/**\n * Register chooser related event listeners.\n *\n * @method registerListenerEvents\n * @param {Promise} modal Our modal that we are working with\n * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}\n * @param {Function} partialFavourite Partially applied function we need to manage favourite status\n * @param {Object} footerData Our base footer object.\n */\nconst registerListenerEvents = (modal, mappedModules, partialFavourite, footerData) => {\n const bodyClickListener = async(e) => {\n if (e.target.closest(selectors.actions.optionActi
ons.showSummary)) {\n const carousel = $(modal.getBody()[0].querySelector(selectors.regions.carousel));\n\n const module = e.target.closest(selectors.regions.chooserOption.container);\n const moduleName = module.dataset.modname;\n const moduleData = mappedModules.get(moduleName);\n // We need to know if the overall modal has a footer so we know when to show a real / vs fake footer.\n moduleData.showFooter = modal.hasFooterContent();\n showModuleHelp(carousel, moduleData, modal);\n }\n\n if (e.target.closest(selectors.actions.optionActions.manageFavourite)) {\n const caller = e.target.closest(selectors.actions.optionActions.manageFavourite);\n await manageFavouriteState(modal.getBody()[0], caller, partialFavourite);\n const activeSectionId = modal.getBody()[0].querySelector(selectors.elements.activetab).getAttribute(\"href\");\n const sectionChooserOptions = modal.getBody()[0]
\n .querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = sectionChooserOptions\n .querySelector(selectors.regions.chooserOption.container);\n toggleFocusableChooserOption(firstChooserOption, true);\n initChooserOptionsKeyboardNavigation(modal.getBody()[0], mappedModules, sectionChooserOptions, modal);\n }\n\n // From the help screen go back to the module overview.\n if (e.target.matches(selectors.actions.closeOption)) {\n const carousel = $(modal.getBody()[0].querySelector(selectors.regions.carousel));\n\n // Trigger the transition between 'pages'.\n carousel.carousel('prev');\n carousel.on('slid.bs.carousel', () => {\n const allModules = modal.getBody()[0].querySelector(selectors.regions.modules);\n const caller = allModules.querySelector(selectors.regions.getModuleSelector(e.target.dataset.modname));
\n caller.focus();\n });\n }\n\n // The \"clear search\" button is triggered.\n if (e.target.closest(selectors.actions.clearSearch)) {\n // Clear the entered search query in the search bar and hide the search results container.\n const searchInput = modal.getBody()[0].querySelector(selectors.actions.search);\n searchInput.value = \"\";\n searchInput.focus();\n toggleSearchResultsView(modal, mappedModules, searchInput.value);\n }\n };\n\n // We essentially have two types of footer.\n // A fake one that is handled within the template for chooser_help and then all of the stuff for\n // modal.footer. We need to ensure we know exactly what type of footer we are using so we know what we\n // need to manage. The below code handles a real footer going to a mnet carousel item.\n const footerClickListener = async(e) => {\n if (footerData.footer === true) {\n const footerjs =
await getPlugin(footerData.customfooterjs);\n await footerjs.footerClickListener(e, footerData, modal);\n }\n };\n\n modal.getBodyPromise()\n\n // The return value of getBodyPromise is a jquery object containing the body NodeElement.\n .then(body => body[0])\n\n // Set up the carousel.\n .then(body => {\n $(body.querySelector(selectors.regions.carousel))\n .carousel({\n interval: false,\n pause: true,\n keyboard: false\n });\n\n return body;\n })\n\n // Add the listener for clicks on the body.\n .then(body => {\n body.addEventListener('click', bodyClickListener);\n return body;\n })\n\n // Add a listener for an input change in the activity chooser's search bar.\n .then(body => {\n const searchInput = body.querySelector(selectors.actions.search);\n // The search input is triggered.\n searchInput.addEventListener('input', debounce(() => {\n
// Display the search results.\n toggleSearchResultsView(modal, mappedModules, searchInput.value);\n }, 300));\n return body;\n })\n\n // Register event listeners related to the keyboard navigation controls.\n .then(body => {\n // Get the active chooser options section.\n const activeSectionId = body.querySelector(selectors.elements.activetab).getAttribute(\"href\");\n const sectionChooserOptions = body.querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = sectionChooserOptions.querySelector(selectors.regions.chooserOption.container);\n\n toggleFocusableChooserOption(firstChooserOption, true);\n initChooserOptionsKeyboardNavigation(body, mappedModules, sectionChooserOptions, modal);\n\n return body;\n })\n .catch();\n\n modal.getFooterPromise()\n\n // The return value of getBodyPromise is a jquery object containing the body NodeElement.\n .then(footer =
> footer[0])\n // Add the listener for clicks on the footer.\n .then(footer => {\n footer.addEventListener('click', footerClickListener);\n return footer;\n })\n .catch();\n};\n\n/**\n * Initialise the keyboard navigation controls for the chooser options.\n *\n * @method initChooserOptionsKeyboardNavigation\n * @param {HTMLElement} body Our modal that we are working with\n * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}\n * @param {HTMLElement} chooserOptionsContainer The section that contains the chooser items\n * @param {Object} modal Our created modal for the section\n */\nconst initChooserOptionsKeyboardNavigation = (body, mappedModules, chooserOptionsContainer, modal = null) => {\n const chooserOptions = chooserOptionsContainer.querySelectorAll(selectors.regions.chooserOption.container);\n\n Array.from(chooserOptions).forEach((element) => {\n return element.addEventListener('keydown', (e) => {\n\n
// Check for enter/ space triggers for showing the help.\n if (e.keyCode === enter || e.keyCode === space) {\n if (e.target.matches(selectors.actions.optionActions.showSummary)) {\n e.preventDefault();\n const module = e.target.closest(selectors.regions.chooserOption.container);\n const moduleName = module.dataset.modname;\n const moduleData = mappedModules.get(moduleName);\n const carousel = $(body.querySelector(selectors.regions.carousel));\n carousel.carousel({\n interval: false,\n pause: true,\n keyboard: false\n });\n\n // We need to know if the overall modal has a footer so we know when to show a real / vs fake footer.\n moduleData.showFooter = modal.hasFooterContent();\n showModuleHelp(carousel, moduleData, modal);\n
}\n }\n\n // Next.\n if (e.keyCode === arrowRight) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const nextOption = currentOption.nextElementSibling;\n const firstOption = chooserOptionsContainer.firstElementChild;\n const toFocusOption = clickErrorHandler(nextOption, firstOption);\n focusChooserOption(toFocusOption, currentOption);\n }\n\n // Previous.\n if (e.keyCode === arrowLeft) {\n e.preventDefault();\n const currentOption = e.target.closest(selectors.regions.chooserOption.container);\n const previousOption = currentOption.previousElementSibling;\n const lastOption = chooserOptionsContainer.lastElementChild;\n const toFocusOption = clickErrorHandler(previousOption, lastOption);\n focus
ChooserOption(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 {HTMLEleme
nt|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.querySelec
tor(selectors.actions.optionActions.manageFavourite);\n\n if (isFocusable) {\n // Set tabindex to 0 to add current chooser option element to the focus order.\n chooserOption.tabIndex = 0;\n chooserOptionLink.tabIndex = 0;\n chooserOptionHelp.tabIndex = 0;\n chooserOptionFavourite.tabIndex = 0;\n } else {\n // Set tabindex to -1 to remove the previous chooser option element from the focus order.\n chooserOption.tabIndex = -1;\n chooserOptionLink.tabIndex = -1;\n chooserOptionHelp.tabIndex = -1;\n chooserOptionFavourite.tabIndex = -1;\n }\n};\n\n/**\n * Small error handling function to make sure the navigated to object exists\n *\n * @method clickErrorHandler\n * @param {HTMLElement} item What we want to check exists\n * @param {HTMLElement} fallback If we dont match anything fallback the focus\n * @return {HTMLElement}\n */\nconst clickErrorHandler = (item, fallback) => {\n if (item !== null) {\n return item;\n } else
{\n return fallback;\n }\n};\n\n/**\n * Render the search results in a defined container\n *\n * @method renderSearchResults\n * @param {HTMLElement} searchResultsContainer The container where the data should be rendered\n * @param {Object} searchResultsData Data containing the module items that satisfy the search criteria\n */\nconst renderSearchResults = async(searchResultsContainer, searchResultsData) => {\n const templateData = {\n 'searchresultsnumber': searchResultsData.length,\n 'searchresults': searchResultsData\n };\n // Build up the html & js ready to place into the help section.\n const {html, js} = await Templates.renderForPromise('core_course/local/activitychooser/search_results', templateData);\n await Templates.replaceNodeContents(searchResultsContainer, html, js);\n};\n\n/**\n * Toggle (display/hide) the search results depending on the value of the search query\n *\n * @method toggleSearchResultsView\n * @param {Object} modal Our created modal for the se
ction\n * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}\n * @param {String} searchQuery The search query\n */\nconst toggleSearchResultsView = async(modal, mappedModules, searchQuery) => {\n const modalBody = modal.getBody()[0];\n const searchResultsContainer = modalBody.querySelector(selectors.regions.searchResults);\n const chooserContainer = modalBody.querySelector(selectors.regions.chooser);\n const clearSearchButton = modalBody.querySelector(selectors.actions.clearSearch);\n\n if (searchQuery.length > 0) { // Search query is present.\n const searchResultsData = searchModules(mappedModules, searchQuery);\n await renderSearchResults(searchResultsContainer, searchResultsData);\n const searchResultItemsContainer = searchResultsContainer.querySelector(selectors.regions.searchResultItems);\n const firstSearchResultItem = searchResultItemsContainer.querySelector(selectors.regions.chooserOption.container);\n
if (firstSearchResultItem) {\n // Set the first result item to be focusable.\n toggleFocusableChooserOption(firstSearchResultItem, true);\n // Register keyboard events on the created search result items.\n initChooserOptionsKeyboardNavigation(modalBody, mappedModules, searchResultItemsContainer, modal);\n }\n // Display the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.remove('d-none');\n // Hide the default chooser options container.\n chooserContainer.setAttribute('hidden', 'hidden');\n // Display the search results container.\n searchResultsContainer.removeAttribute('hidden');\n } else { // Search query is not present.\n // Hide the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.add('d-none');\n // Hide the search results container.\n searchResultsContainer.setAttribute('hidden', 'hidden');\n
// Display the default chooser options container.\n chooserContainer.removeAttribute('hidden');\n }\n};\n\n/**\n * Return the list of modules which have a name or description that matches the given search term.\n *\n * @method searchModules\n * @param {Array} modules List of available modules\n * @param {String} searchTerm The search term to match\n * @return {Array}\n */\nconst searchModules = (modules, searchTerm) => {\n if (searchTerm === '') {\n return modules;\n }\n searchTerm = searchTerm.toLowerCase();\n const searchResults = [];\n modules.forEach((activity) => {\n const activityName = activity.title.toLowerCase();\n const activityDesc = activity.help.toLowerCase();\n if (activityName.includes(searchTerm) || activityDesc.includes(searchTerm)) {\n searchResults.push(activity);\n }\n });\n\n return searchResults;\n};\n\n/**\n * Set up our tabindex information across the chooser.\n *\n * @method setupKeyboardAccessibility\
n * @param {Promise} modal Our created modal for the section\n * @param {Map} mappedModules A map of all of the built module information\n */\nconst setupKeyboardAccessibility = (modal, mappedModules) => {\n modal.getModal()[0].tabIndex = -1;\n\n modal.getBodyPromise().then(body => {\n $(selectors.elements.tab).on('shown.bs.tab', (e) => {\n const activeSectionId = e.target.getAttribute(\"href\");\n const activeSectionChooserOptions = body[0]\n .querySelector(selectors.regions.getSectionChooserOptions(activeSectionId));\n const firstChooserOption = activeSectionChooserOptions\n .querySelector(selectors.regions.chooserOption.container);\n const prevActiveSectionId = e.relatedTarget.getAttribute(\"href\");\n const prevActiveSectionChooserOptions = body[0]\n .querySelector(selectors.regions.getSectionChooserOptions(prevActiveSectionId));\n\n // Disable the focus of every chooser option in t
he previous active section.\n disableFocusAllChooserOptions(prevActiveSectionChooserOptions);\n // Enable the focus of the first chooser option in the current active section.\n toggleFocusableChooserOption(firstChooserOption, true);\n initChooserOptionsKeyboardNavigation(body[0], mappedModules, activeSectionChooserOptions, modal);\n });\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Disable the focus of all chooser options in a specific container (section).\n *\n * @method disableFocusAllChooserOptions\n * @param {HTMLElement} sectionChooserOptions The section that contains the chooser items\n */\nconst disableFocusAllChooserOptions = (sectionChooserOptions) => {\n const allChooserOptions = sectionChooserOptions.querySelectorAll(selectors.regions.chooserOption.container);\n allChooserOptions.forEach((chooserOption) => {\n toggleFocusableChooserOption(chooserOption, false);\n });\n};\n\n/**\n * Display the module choose
r.\n *\n * @method displayChooser\n * @param {Promise} modalPromise Our created modal for the section\n * @param {Array} sectionModules An array of all of the built module information\n * @param {Function} partialFavourite Partially applied function we need to manage favourite status\n * @param {Object} footerData Our base footer object.\n */\nexport const displayChooser = (modalPromise, sectionModules, partialFavourite, footerData) => {\n // Make a map so we can quickly fetch a specific module's object for either rendering or searching.\n const mappedModules = new Map();\n sectionModules.forEach((module) => {\n mappedModules.set(module.componentname + '_' + module.link, module);\n });\n\n // Register event listeners.\n modalPromise.then(modal => {\n registerListenerEvents(modal, mappedModules, partialFavourite, footerData);\n\n // We want to focus on the first chooser option element as soon as the modal is opened.\n setupKeyboardAccessibility(modal, mappedModule
s);\n\n // We want to focus on the action select when the dialog is closed.\n modal.getRoot().on(ModalEvents.hidden, () => {\n modal.destroy();\n });\n\n return modal;\n }).catch();\n};\n"],"names":["showModuleHelp","carousel","moduleData","modal","showFooter","setFooter","Templates","render","help","find","selectors","regions","innerHTML","classList","add","spinnerPromise","transitionPromiseResolver","transitionPromise","Promise","resolve","contentPromise","renderForPromise","all","then","_ref","html","js","replaceNodeContents","querySelector","chooserSummary","header","focus","catch","Notification","exception","one","registerListenerEvents","mappedModules","partialFavourite","footerData","bodyClickListener","async","e","target","closest","actions","optionActions","showSummary","getBody","moduleName","chooserOption","container","dataset","modname","get","hasFooterContent","manageFavourite","caller","modalBody","isFavourite","favourited","id","name","internal","Repos
itory","unfavouriteModule","favouriteModule","manageFavouriteState","activeSectionId","elements","activetab","getAttribute","sectionChooserOptions","getSectionChooserOptions","firstChooserOption","toggleFocusableChooserOption","initChooserOptionsKeyboardNavigation","matches","closeOption","on","modules","getModuleSelector","clearSearch","searchInput","search","value","toggleSearchResultsView","footerClickListener","footer","footerjs","pluginName","customfooterjs","getBodyPromise","body","interval","pause","keyboard","addEventListener","getFooterPromise","chooserOptionsContainer","chooserOptions","querySelectorAll","Array","from","forEach","element","keyCode","enter","space","preventDefault","arrowRight","currentOption","nextOption","nextElementSibling","firstOption","firstElementChild","toFocusOption","clickErrorHandler","focusChooserOption","arrowLeft","previousOption","previousElementSibling","lastOption","lastElementChild","home","end","currentChooserOption","previousChooserOption","isFocusable","chooserO
ptionLink","addChooser","chooserOptionHelp","chooserOptionFavourite","tabIndex","item","fallback","searchQuery","searchResultsContainer","searchResults","chooserContainer","chooser","clearSearchButton","length","searchResultsData","searchModules","templateData","renderSearchResults","searchResultItemsContainer","searchResultItems","firstSearchResultItem","remove","setAttribute","removeAttribute","searchTerm","toLowerCase","activity","activityName","title","activityDesc","includes","push","disableFocusAllChooserOptions","modalPromise","sectionModules","Map","module","set","componentname","link","getModal","tab","activeSectionChooserOptions","prevActiveSectionId","relatedTarget","prevActiveSectionChooserOptions","setupKeyboardAccessibility","getRoot","ModalEvents","hidden","destroy"],"mappings":"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"}