Sindbad~EG File Manager
{"version":3,"file":"grader.min.js","sources":["../../../src/local/grades/grader.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 * This module will tie together all of the different calls the gradable module will make.\n *\n * @module mod_forum/local/grades/grader\n * @copyright 2019 Mathew May <mathew.solutions>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Templates from 'core/templates';\nimport Selectors from './local/grader/selectors';\nimport getUserPicker from './local/grader/user_picker';\nimport {createLayout as createFullScreenWindow} from 'mod_forum/local/layout/fullscreen';\nimport getGradingPanelFunctions from './local/grader/gradingpanel';\nimport {add as addToast} from 'core/toast';\nimport {addNotification} from 'core/notification';\nimport {get_string as getString} from 'core/str';\nimport {failedUpdate} from 'core_grades/grades/grader/gradingpanel/normalise';\nimport {addIconToContainerWithPromise} from 'core/loadingicon';\nimport {debounce} from 'core/utils';\nimport {fillInitialValues} from 'core_grades/grades/grader/gradingpanel/comparison';\nimport * as Modal from 'core/modal_factory';\nimport * as ModalEvents from 'core/modal_events';\nimport {subscribe} from 'core/pubsub';\nimport DrawerEvents from 'core/drawer_events';\n\nconst templateNames = {\n grader: {\n app: 'mod_forum/local/grades/grader',\n gradingPanel: {\n error: 'mod_forum/local/grades/local/grader/gradingpanel/error',\n },\n searchResults: 'mod_forum/local/grades/local/grader/user_picker/user_search',\n status: 'mod_forum/local/grades/local/grader/status',\n },\n};\n\n/**\n * Helper function that replaces the user picker placeholder with what we get back from the user picker class.\n *\n * @param {HTMLElement} root\n * @param {String} html\n */\nconst displayUserPicker = (root, html) => {\n const pickerRegion = root.querySelector(Selectors.regions.pickerRegion);\n Templates.replaceNodeContents(pickerRegion, html, '');\n};\n\n/**\n * To be removed, this is now done as a part of Templates.renderForPromise()\n *\n * @param {String} html\n * @param {String} js\n * @returns {array} An array containing the HTML, and JS.\n */\nconst fetchContentFromRender = (html, js) => {\n return [html, js];\n};\n\n/**\n * Here we build the function that is passed to the user picker that'll handle updating the user content area\n * of the grading interface.\n *\n * @param {HTMLElement} root\n * @param {Function} getContentForUser\n * @param {Function} getGradeForUser\n * @param {Function} saveGradeForUser\n * @return {Function}\n */\nconst getUpdateUserContentFunction = (root, getContentForUser, getGradeForUser, saveGradeForUser) => {\n let firstLoad = true;\n\n return async(user) => {\n const spinner = firstLoad ? null : addIconToContainerWithPromise(root);\n const [\n [html, js],\n userGrade,\n ] = await Promise.all([\n getContentForUser(user.id).then(fetchContentFromRender),\n getGradeForUser(user.id),\n ]);\n Templates.replaceNodeContents(root.querySelector(Selectors.regions.moduleReplace), html, js);\n\n const [\n gradingPanelHtml,\n gradingPanelJS\n ] = await Templates.render(userGrade.templatename, userGrade.grade).then(fetchContentFromRender);\n const panelContainer = root.querySelector(Selectors.regions.gradingPanelContainer);\n const panel = panelContainer.querySelector(Selectors.regions.gradingPanel);\n Templates.replaceNodeContents(panel, gradingPanelHtml, gradingPanelJS);\n\n const form = panel.querySelector('form');\n fillInitialValues(form);\n\n form.addEventListener('submit', event => {\n saveGradeForUser(user);\n event.preventDefault();\n });\n\n panelContainer.scrollTop = 0;\n firstLoad = false;\n\n if (spinner) {\n spinner.resolve();\n }\n return userGrade;\n };\n};\n\n/**\n * Show the search results container and hide the user picker and body content.\n *\n * @param {HTMLElement} bodyContainer The container element for the body content\n * @param {HTMLElement} userPickerContainer The container element for the user picker\n * @param {HTMLElement} searchResultsContainer The container element for the search results\n */\nconst showSearchResultContainer = (bodyContainer, userPickerContainer, searchResultsContainer) => {\n bodyContainer.classList.add('hidden');\n userPickerContainer.classList.add('hidden');\n searchResultsContainer.classList.remove('hidden');\n};\n\n/**\n * Hide the search results container and show the user picker and body content.\n *\n * @param {HTMLElement} bodyContainer The container element for the body content\n * @param {HTMLElement} userPickerContainer The container element for the user picker\n * @param {HTMLElement} searchResultsContainer The container element for the search results\n */\nconst hideSearchResultContainer = (bodyContainer, userPickerContainer, searchResultsContainer) => {\n bodyContainer.classList.remove('hidden');\n userPickerContainer.classList.remove('hidden');\n searchResultsContainer.classList.add('hidden');\n};\n\n/**\n * Toggles the visibility of the user search.\n *\n * @param {HTMLElement} toggleSearchButton The button that toggles the search\n * @param {HTMLElement} searchContainer The container element for the user search\n * @param {HTMLElement} searchInput The input element for searching\n */\nconst showUserSearchInput = (toggleSearchButton, searchContainer, searchInput) => {\n searchContainer.classList.remove('collapsed');\n toggleSearchButton.setAttribute('aria-expanded', 'true');\n toggleSearchButton.classList.add('expand');\n toggleSearchButton.classList.remove('collapse');\n\n // Hide the grading info container from screen reader.\n const gradingInfoContainer = searchContainer.parentElement.querySelector(Selectors.regions.gradingInfoContainer);\n gradingInfoContainer.setAttribute('aria-hidden', 'true');\n\n // Hide the collapse grading drawer button from screen reader.\n const collapseGradingDrawer = searchContainer.parentElement.querySelector(Selectors.buttons.collapseGradingDrawer);\n collapseGradingDrawer.setAttribute('aria-hidden', 'true');\n collapseGradingDrawer.setAttribute('tabindex', '-1');\n\n searchInput.focus();\n};\n\n/**\n * Toggles the visibility of the user search.\n *\n * @param {HTMLElement} toggleSearchButton The button that toggles the search\n * @param {HTMLElement} searchContainer The container element for the user search\n * @param {HTMLElement} searchInput The input element for searching\n */\nconst hideUserSearchInput = (toggleSearchButton, searchContainer, searchInput) => {\n searchContainer.classList.add('collapsed');\n toggleSearchButton.setAttribute('aria-expanded', 'false');\n toggleSearchButton.classList.add('collapse');\n toggleSearchButton.classList.remove('expand');\n toggleSearchButton.focus();\n\n // Show the grading info container to screen reader.\n const gradingInfoContainer = searchContainer.parentElement.querySelector(Selectors.regions.gradingInfoContainer);\n gradingInfoContainer.removeAttribute('aria-hidden');\n\n // Show the collapse grading drawer button from screen reader.\n const collapseGradingDrawer = searchContainer.parentElement.querySelector(Selectors.buttons.collapseGradingDrawer);\n collapseGradingDrawer.removeAttribute('aria-hidden');\n collapseGradingDrawer.setAttribute('tabindex', '0');\n\n searchInput.value = '';\n};\n\n/**\n * Find the list of users who's names include the given search term.\n *\n * @param {Array} userList List of users for the grader\n * @param {String} searchTerm The search term to match\n * @return {Array}\n */\nconst searchForUsers = (userList, searchTerm) => {\n if (searchTerm === '') {\n return userList;\n }\n\n searchTerm = searchTerm.toLowerCase();\n\n return userList.filter((user) => {\n return user.fullname.toLowerCase().includes(searchTerm);\n });\n};\n\n/**\n * Render the list of users in the search results area.\n *\n * @param {HTMLElement} searchResultsContainer The container element for search results\n * @param {Array} users The list of users to display\n */\nconst renderSearchResults = async(searchResultsContainer, users) => {\n const {html, js} = await Templates.renderForPromise(templateNames.grader.searchResults, {users});\n Templates.replaceNodeContents(searchResultsContainer, html, js);\n};\n\n/**\n * Add click handlers to the buttons in the header of the grading interface.\n *\n * @param {HTMLElement} graderLayout\n * @param {Object} userPicker\n * @param {Function} saveGradeFunction\n * @param {Array} userList List of users for the grader.\n */\nconst registerEventListeners = (graderLayout, userPicker, saveGradeFunction, userList) => {\n const graderContainer = graderLayout.getContainer();\n const toggleSearchButton = graderContainer.querySelector(Selectors.buttons.toggleSearch);\n const searchInputContainer = graderContainer.querySelector(Selectors.regions.userSearchContainer);\n const searchInput = searchInputContainer.querySelector(Selectors.regions.userSearchInput);\n const bodyContainer = graderContainer.querySelector(Selectors.regions.bodyContainer);\n const userPickerContainer = graderContainer.querySelector(Selectors.regions.pickerRegion);\n const searchResultsContainer = graderContainer.querySelector(Selectors.regions.searchResultsContainer);\n\n graderContainer.addEventListener('click', (e) => {\n if (e.target.closest(Selectors.buttons.toggleFullscreen)) {\n e.stopImmediatePropagation();\n e.preventDefault();\n graderLayout.toggleFullscreen();\n\n return;\n }\n\n if (e.target.closest(Selectors.buttons.closeGrader)) {\n e.stopImmediatePropagation();\n e.preventDefault();\n\n graderLayout.close();\n\n return;\n }\n\n if (e.target.closest(Selectors.buttons.saveGrade)) {\n saveGradeFunction(userPicker.currentUser);\n }\n\n if (e.target.closest(Selectors.buttons.toggleSearch)) {\n if (toggleSearchButton.getAttribute('aria-expanded') === 'true') {\n // Search is open so let's close it.\n hideUserSearchInput(toggleSearchButton, searchInputContainer, searchInput);\n hideSearchResultContainer(bodyContainer, userPickerContainer, searchResultsContainer);\n searchResultsContainer.innerHTML = '';\n } else {\n // Search is closed so let's open it.\n showUserSearchInput(toggleSearchButton, searchInputContainer, searchInput);\n showSearchResultContainer(bodyContainer, userPickerContainer, searchResultsContainer);\n renderSearchResults(searchResultsContainer, userList);\n }\n\n return;\n }\n\n const selectUserButton = e.target.closest(Selectors.buttons.selectUser);\n if (selectUserButton) {\n const userId = selectUserButton.getAttribute('data-userid');\n const user = userList.find(user => user.id == userId);\n userPicker.setUserId(userId);\n userPicker.showUser(user);\n hideUserSearchInput(toggleSearchButton, searchInputContainer, searchInput);\n hideSearchResultContainer(bodyContainer, userPickerContainer, searchResultsContainer);\n searchResultsContainer.innerHTML = '';\n }\n });\n\n // Debounce the search input so that it only executes 300 milliseconds after the user has finished typing.\n searchInput.addEventListener('input', debounce(() => {\n const users = searchForUsers(userList, searchInput.value);\n renderSearchResults(searchResultsContainer, users);\n }, 300));\n\n // Remove the right margin of the content container when the grading panel is hidden so that it expands to full-width.\n subscribe(DrawerEvents.DRAWER_HIDDEN, (drawerRoot) => {\n const gradingPanel = drawerRoot[0];\n if (gradingPanel.querySelector(Selectors.regions.gradingPanel)) {\n setContentContainerMargin(graderContainer, 0);\n }\n });\n\n // Bring back the right margin of the content container when the grading panel is shown to give space for the grading panel.\n subscribe(DrawerEvents.DRAWER_SHOWN, (drawerRoot) => {\n const gradingPanel = drawerRoot[0];\n if (gradingPanel.querySelector(Selectors.regions.gradingPanel)) {\n setContentContainerMargin(graderContainer, gradingPanel.offsetWidth);\n }\n });\n};\n\n/**\n * Adjusts the right margin of the content container.\n *\n * @param {HTMLElement} graderContainer The container for the grader app.\n * @param {Number} rightMargin The right margin value.\n */\nconst setContentContainerMargin = (graderContainer, rightMargin) => {\n const contentContainer = graderContainer.querySelector(Selectors.regions.moduleContainer);\n if (contentContainer) {\n contentContainer.style.marginRight = `${rightMargin}px`;\n }\n};\n\n/**\n * Get the function used to save a user grade.\n *\n * @param {HTMLElement} root The container for the grader\n * @param {Function} setGradeForUser The function that will be called.\n * @return {Function}\n */\nconst getSaveUserGradeFunction = (root, setGradeForUser) => {\n return async(user) => {\n try {\n root.querySelector(Selectors.regions.gradingPanelErrors).innerHTML = '';\n const result = await setGradeForUser(\n user.id,\n root.querySelector(Selectors.values.sendStudentNotifications).value,\n root.querySelector(Selectors.regions.gradingPanel)\n );\n if (result.success) {\n addToast(await getString('grades:gradesavedfor', 'mod_forum', user));\n }\n if (result.failed) {\n displayGradingError(root, user, result.error);\n }\n\n return result;\n } catch (err) {\n displayGradingError(root, user, err);\n\n return failedUpdate(err);\n }\n };\n};\n\n/**\n * Display a grading error, typically from a failed save.\n *\n * @param {HTMLElement} root The container for the grader\n * @param {Object} user The user who was errored\n * @param {Object} err The details of the error\n */\nconst displayGradingError = async(root, user, err) => {\n const [\n {html, js},\n errorString\n ] = await Promise.all([\n Templates.renderForPromise(templateNames.grader.gradingPanel.error, {error: err}),\n await getString('grades:gradesavefailed', 'mod_forum', {error: err.message, ...user}),\n ]);\n\n Templates.replaceNodeContents(root.querySelector(Selectors.regions.gradingPanelErrors), html, js);\n addToast(errorString);\n};\n\n/**\n * Launch the grader interface with the specified parameters.\n *\n * @param {Function} getListOfUsers A function to get the list of users\n * @param {Function} getContentForUser A function to get the content for a specific user\n * @param {Function} getGradeForUser A function get the grade details for a specific user\n * @param {Function} setGradeForUser A function to set the grade for a specific user\n * @param {Object} preferences Preferences for the launch function\n * @param {Number} preferences.initialUserId\n * @param {string} preferences.moduleName\n * @param {string} preferences.courseName\n * @param {string} preferences.courseUrl\n * @param {boolean} preferences.sendStudentNotifications\n * @param {null|HTMLElement} preferences.focusOnClose\n */\nexport const launch = async(getListOfUsers, getContentForUser, getGradeForUser, setGradeForUser, {\n initialUserId = null,\n moduleName,\n courseName,\n courseUrl,\n sendStudentNotifications,\n focusOnClose = null,\n} = {}) => {\n\n // We need all of these functions to be executed in series, if one step runs before another the interface\n // will not work.\n\n // We need this promise to resolve separately so that we can avoid loading the whole interface if there are no users.\n const userList = await getListOfUsers();\n if (!userList.length) {\n addNotification({\n message: await getString('nouserstograde', 'core_grades'),\n type: \"error\",\n });\n return;\n }\n\n // Now that we have confirmed there are at least some users let's boot up the grader interface.\n const [\n graderLayout,\n {html, js},\n ] = await Promise.all([\n createFullScreenWindow({\n fullscreen: false,\n showLoader: false,\n focusOnClose,\n }),\n Templates.renderForPromise(templateNames.grader.app, {\n moduleName,\n courseName,\n courseUrl,\n drawer: {show: true},\n defaultsendnotifications: sendStudentNotifications,\n }),\n ]);\n\n const graderContainer = graderLayout.getContainer();\n\n const saveGradeFunction = getSaveUserGradeFunction(graderContainer, setGradeForUser);\n\n Templates.replaceNodeContents(graderContainer, html, js);\n const updateUserContent = getUpdateUserContentFunction(graderContainer, getContentForUser, getGradeForUser, saveGradeFunction);\n\n const userIds = userList.map(user => user.id);\n const statusContainer = graderContainer.querySelector(Selectors.regions.statusContainer);\n // Fetch the userpicker for display.\n const userPicker = await getUserPicker(\n userList,\n async(user) => {\n const userGrade = await updateUserContent(user);\n const renderContext = {\n status: userGrade.hasgrade,\n index: userIds.indexOf(user.id) + 1,\n total: userList.length\n };\n Templates.render(templateNames.grader.status, renderContext).then(html => {\n statusContainer.innerHTML = html;\n return html;\n }).catch();\n },\n saveGradeFunction,\n {\n initialUserId,\n },\n );\n\n // Register all event listeners.\n registerEventListeners(graderLayout, userPicker, saveGradeFunction, userList);\n\n // Display the newly created user picker.\n displayUserPicker(graderContainer, userPicker.rootNode);\n};\n\n/**\n * Show the grade for a specific user.\n *\n * @param {Function} getGradeForUser A function get the grade details for a specific user\n * @param {Number} userid The ID of a specific user\n * @param {String} moduleName the name of the module\n * @param {object} param\n * @param {null|HTMLElement} param.focusOnClose\n */\nexport const view = async(getGradeForUser, userid, moduleName, {\n focusOnClose = null,\n} = {}) => {\n\n const [\n userGrade,\n modal,\n ] = await Promise.all([\n getGradeForUser(userid),\n Modal.create({\n title: moduleName,\n large: true,\n type: Modal.types.CANCEL\n }),\n ]);\n\n const spinner = addIconToContainerWithPromise(modal.getRoot());\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n if (focusOnClose) {\n try {\n focusOnClose.focus();\n } catch (e) {\n // eslint-disable-line\n }\n }\n });\n\n modal.show();\n const output = document.createElement('div');\n const {html, js} = await Templates.renderForPromise('mod_forum/local/grades/view_grade', userGrade);\n Templates.replaceNodeContents(output, html, js);\n\n // Note: We do not use await here because it messes with the Modal transitions.\n const [gradeHTML, gradeJS] = await renderGradeTemplate(userGrade);\n const gradeReplace = output.querySelector('[data-region=\"grade-template\"]');\n Templates.replaceNodeContents(gradeReplace, gradeHTML, gradeJS);\n modal.setBody(output.outerHTML);\n spinner.resolve();\n};\n\nconst renderGradeTemplate = async(userGrade) => {\n const {html, js} = await Templates.renderForPromise(userGrade.templatename, userGrade.grade);\n return [html, js];\n};\nexport {getGradingPanelFunctions};\n"],"names":["templateNames","app","gradingPanel","error","searchResults","status","displayUserPicker","root","html","pickerRegion","querySelector","Selectors","regions","replaceNodeContents","fetchContentFromRender","js","getUpdateUserContentFunction","getContentForUser","getGradeForUser","saveGradeForUser","firstLoad","user","spinner","Promise","all","id","then","userGrade","moduleReplace","Templates","render","templatename","grade","gradingPanelHtml","gradingPanelJS","panelContainer","gradingPanelContainer","panel","form","addEventListener","event","preventDefault","scrollTop","resolve","hideSearchResultContainer","bodyContainer","userPickerContainer","searchResultsContainer","classList","remove","add","hideUserSearchInput","toggleSearchButton","searchContainer","searchInput","setAttribute","focus","parentElement","gradingInfoContainer","removeAttribute","collapseGradingDrawer","buttons","value","renderSearchResults","users","renderForPromise","registerEventListeners","graderLayout","userPicker","saveGradeFunction","userList","graderContainer","getContainer","toggleSearch","searchInputContainer","userSearchContainer","userSearchInput","e","target","closest","toggleFullscreen","stopImmediatePropagation","closeGrader","close","saveGrade","currentUser","getAttribute","innerHTML","showUserSearchInput","showSearchResultContainer","selectUserButton","selectUser","userId","find","setUserId","showUser","searchTerm","toLowerCase","filter","fullname","includes","searchForUsers","DrawerEvents","DRAWER_HIDDEN","drawerRoot","setContentContainerMargin","DRAWER_SHOWN","offsetWidth","rightMargin","contentContainer","moduleContainer","style","marginRight","getSaveUserGradeFunction","setGradeForUser","gradingPanelErrors","values","sendStudentNotifications","result","success","addToast","failed","displayGradingError","err","message","errorString","launch","getListOfUsers","initialUserId","moduleName","courseName","courseUrl","focusOnClose","length","addNotification","type","fullscreen","showLoader","drawer","show","defaultsendnotifications","updateUserContent","userIds","map","statusContainer","renderContext","hasgrade","index","indexOf","total","catch","rootNode","view","userid","Modal","create","title","large","types","CANCEL","modal","getRoot","on","ModalEvents","hidden","destroy","output","document","createElement","renderGradeTemplate","gradeHTML","gradeJS","gradeReplace","setBody","outerHTML"],"mappings":"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"}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists